/* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger * wants to send them. * * The muxer must *not* have been started before calling. */ static status_t runEncoder(const sp<MediaCodec>& encoder, const sp<MediaMuxer>& muxer) { static int kTimeout = 250000; // be responsive on signal status_t err; ssize_t trackIdx = -1; uint32_t debugNumFrames = 0; int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); Vector<sp<ABuffer> > buffers; err = encoder->getOutputBuffers(&buffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get output buffers (err=%d)\n", err); return err; } // This is set by the signal handler. gStopRequested = false; // Run until we're signaled. while (!gStopRequested) { size_t bufIndex, offset, size; int64_t ptsUsec; uint32_t flags; if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) { if (gVerbose) { printf("Time limit reached\n"); } break; } ALOGV("Calling dequeueOutputBuffer"); err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec, &flags, kTimeout); ALOGV("dequeueOutputBuffer returned %d", err); switch (err) { case NO_ERROR: // got a buffer if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { // ignore this -- we passed the CSD into MediaMuxer when // we got the format change notification ALOGV("Got codec config buffer (%u bytes); ignoring", size); size = 0; } if (size != 0) { ALOGV("Got data in buffer %d, size=%d, pts=%lld", bufIndex, size, ptsUsec); CHECK(trackIdx != -1); // If the virtual display isn't providing us with timestamps, // use the current time. if (ptsUsec == 0) { ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000; } // The MediaMuxer docs are unclear, but it appears that we // need to pass either the full set of BufferInfo flags, or // (flags & BUFFER_FLAG_SYNCFRAME). err = muxer->writeSampleData(buffers[bufIndex], trackIdx, ptsUsec, flags); if (err != NO_ERROR) { fprintf(stderr, "Failed writing data to muxer (err=%d)\n", err); return err; } debugNumFrames++; } err = encoder->releaseOutputBuffer(bufIndex); if (err != NO_ERROR) { fprintf(stderr, "Unable to release output buffer (err=%d)\n", err); return err; } if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) { // Not expecting EOS from SurfaceFlinger. Go with it. ALOGD("Received end-of-stream"); gStopRequested = false; } break; case -EAGAIN: // INFO_TRY_AGAIN_LATER ALOGV("Got -EAGAIN, looping"); break; case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED { // format includes CSD, which we must provide to muxer ALOGV("Encoder format changed"); sp<AMessage> newFormat; encoder->getOutputFormat(&newFormat); trackIdx = muxer->addTrack(newFormat); ALOGV("Starting muxer"); err = muxer->start(); if (err != NO_ERROR) { fprintf(stderr, "Unable to start muxer (err=%d)\n", err); return err; } } break; case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED // not expected for an encoder; handle it anyway ALOGV("Encoder buffers changed"); err = encoder->getOutputBuffers(&buffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get new output buffers (err=%d)\n", err); return err; } break; case INVALID_OPERATION: fprintf(stderr, "Request for encoder buffer failed\n"); return err; default: fprintf(stderr, "Got weird result %d from dequeueOutputBuffer\n", err); return err; } } ALOGV("Encoder stopping (req=%d)", gStopRequested); if (gVerbose) { printf("Encoder stopping; recorded %u frames in %lld seconds\n", debugNumFrames, nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec)); } return NO_ERROR; }
/* * Runs the MediaCodec encoder, sending the output to the MediaMuxer. The * input frames are coming from the virtual display as fast as SurfaceFlinger * wants to send them. * * The muxer must *not* have been started before calling. */ static status_t runEncoder(const sp<MediaCodec>& audioEncoder, const sp<MediaCodec>& videoEncoder, const sp<MediaMuxer>& muxer) { static int kTimeout = 5000; // be responsive on signal status_t err; ssize_t videoTrackIdx = -1; ssize_t audioTrackIdx = -1; uint32_t debugNumFrames = 0; int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC); int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec); uint32_t tracksAdded = 0; int64_t lastAudioPtsUs = 0; Vector<sp<ABuffer> > buffers; err = videoEncoder->getOutputBuffers(&buffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get output buffers (err=%d)\n", err); return err; } Vector<sp<ABuffer> > audioOutputBuffers; Vector<sp<ABuffer> > audioInputBuffers; sp<AudioRecord> audioRecorder; if (gRecordAudio) { err = audioEncoder->getOutputBuffers(&audioOutputBuffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get output audio buffers (err=%d)\n", err); return err; } err = audioEncoder->getInputBuffers(&audioInputBuffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get input audio buffers (err=%d)\n", err); return err; } // setup AudioRecord so we can source audio data to the audio codec audioRecorder = new AudioRecord(); size_t minBuffSize = getMinBuffSize(kAudioSampleRate, 1, AUDIO_FORMAT_PCM_16_BIT); size_t buffSize = kSamplesPerFrame * 10; if (buffSize < minBuffSize) { buffSize = ((minBuffSize / kSamplesPerFrame) + 1) * kSamplesPerFrame * 2; } audioRecorder->set( (audio_source_t) 1, kAudioSampleRate, AUDIO_FORMAT_PCM_16_BIT, // byte length, PCM (audio_channel_mask_t) 16, buffSize / 2, NULL,// callback_t NULL,// void* user 0, // notificationFrames, false, // threadCanCallJava 0); err = audioRecorder->initCheck(); if (err != NO_ERROR) { fprintf(stderr, "Error creating AudioRecord instance: initialization check failed (err=%d)\n", err); return err; } audioRecorder->start(); } // This is set by the signal handler. gStopRequested = false; jni_onRecordingStarted(); // Run until we're signaled. while (!gStopRequested) { size_t bufIndex, offset, size; int64_t ptsUsec; uint32_t flags; if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) { if (gVerbose) { printf("Time limit reached\n"); } break; } // first lets send some audio off to the audio encoder if enabled if (gRecordAudio) { err = audioEncoder->dequeueInputBuffer(&bufIndex, kTimeout); if (err == NO_ERROR) { ssize_t audioSize = audioRecorder->read(audioInputBuffers[bufIndex]->data(), kSamplesPerFrame); err = audioEncoder->queueInputBuffer( bufIndex, 0, audioSize, systemTime(CLOCK_MONOTONIC) / 1000, 0); ALOGV("Queued %d bytes of audio", audioSize); } } ALOGV("Calling dequeueOutputBuffer"); err = videoEncoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec, &flags, kTimeout); ALOGV("dequeueOutputBuffer returned %d", err); switch (err) { case NO_ERROR: // got a buffer if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { // ignore this -- we passed the CSD into MediaMuxer when // we got the format change notification ALOGV("Got codec config buffer (%u bytes); ignoring", size); size = 0; } if (size != 0) { ALOGV("Got data in video buffer %d, size=%d, pts=%lld", bufIndex, size, ptsUsec); CHECK(videoTrackIdx != -1); // If the virtual display isn't providing us with timestamps, // use the current time. if (ptsUsec == 0) { ptsUsec = systemTime(SYSTEM_TIME_MONOTONIC) / 1000; } // The MediaMuxer docs are unclear, but it appears that we // need to pass either the full set of BufferInfo flags, or // (flags & BUFFER_FLAG_SYNCFRAME). err = muxer->writeSampleData(buffers[bufIndex], videoTrackIdx, ptsUsec, flags); if (err != NO_ERROR) { fprintf(stderr, "Failed writing data to muxer (err=%d)\n", err); return err; } debugNumFrames++; } err = videoEncoder->releaseOutputBuffer(bufIndex); if (err != NO_ERROR) { fprintf(stderr, "Unable to release output buffer (err=%d)\n", err); return err; } if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) { // Not expecting EOS from SurfaceFlinger. Go with it. ALOGV("Received end-of-stream"); gStopRequested = false; } break; case -EAGAIN: // INFO_TRY_AGAIN_LATER ALOGV("Got -EAGAIN, looping"); break; case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED { // format includes CSD, which we must provide to muxer ALOGV("Encoder format changed"); sp<AMessage> newFormat; videoEncoder->getOutputFormat(&newFormat); videoTrackIdx = muxer->addTrack(newFormat); if (++tracksAdded >= (gRecordAudio ? 2 : 1)) { ALOGV("Starting muxer"); err = muxer->start(); if (err != NO_ERROR) { fprintf(stderr, "Unable to start muxer (err=%d)\n", err); return err; } } } break; case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED // not expected for an encoder; handle it anyway ALOGV("Encoder buffers changed"); err = videoEncoder->getOutputBuffers(&buffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get new output buffers (err=%d)\n", err); return err; } break; case INVALID_OPERATION: fprintf(stderr, "Request for encoder buffer failed\n"); return err; default: fprintf(stderr, "Got weird result %d from dequeueOutputBuffer\n", err); return err; } if (gRecordAudio) { ALOGV("Calling dequeueOutputBuffer for audioEncoder"); err = audioEncoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec, &flags, kTimeout); ALOGV("dequeueOutputBuffer returned %d", err); switch (err) { case NO_ERROR: // got a buffer if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) { // ignore this -- we passed the CSD into MediaMuxer when // we got the format change notification ALOGV("Got codec config buffer (%u bytes); ignoring", size); size = 0; } if (size != 0) { ALOGV("Got data in audio buffer %d, offset=%d, size=%d, pts=%lld", bufIndex, offset, size, ptsUsec); CHECK(audioTrackIdx != -1); if (ptsUsec < 0) ptsUsec = 0; if (ptsUsec < lastAudioPtsUs) ptsUsec = lastAudioPtsUs + 23219; // magical AAC encoded frame time lastAudioPtsUs = ptsUsec; // The MediaMuxer docs are unclear, but it appears that we // need to pass either the full set of BufferInfo flags, or // (flags & BUFFER_FLAG_SYNCFRAME). err = muxer->writeSampleData(audioOutputBuffers[bufIndex], audioTrackIdx, ptsUsec, flags); if (err != NO_ERROR) { fprintf(stderr, "Failed writing data to muxer (err=%d)\n", err); return err; } } err = audioEncoder->releaseOutputBuffer(bufIndex); if (err != NO_ERROR) { fprintf(stderr, "Unable to release output buffer (err=%d)\n", err); return err; } if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) { // Not expecting EOS. Go with it. ALOGV("Received end-of-stream"); gStopRequested = false; } break; case -EAGAIN: // INFO_TRY_AGAIN_LATER ALOGV("Got -EAGAIN, looping"); break; case INFO_FORMAT_CHANGED: // INFO_OUTPUT_FORMAT_CHANGED { // format includes CSD, which we must provide to muxer ALOGV("Audio encoder format changed"); sp<AMessage> newFormat; audioEncoder->getOutputFormat(&newFormat); audioTrackIdx = muxer->addTrack(newFormat); if (++tracksAdded >= 2) { ALOGV("Starting muxer"); err = muxer->start(); if (err != NO_ERROR) { fprintf(stderr, "Unable to start muxer (err=%d)\n", err); return err; } } } break; case INFO_OUTPUT_BUFFERS_CHANGED: // INFO_OUTPUT_BUFFERS_CHANGED // not expected for an encoder; handle it anyway ALOGV("Audio encoder buffers changed"); err = audioEncoder->getOutputBuffers(&audioOutputBuffers); if (err != NO_ERROR) { fprintf(stderr, "Unable to get new output buffers (err=%d)\n", err); return err; } break; case INVALID_OPERATION: fprintf(stderr, "Request for encoder buffer failed\n"); return err; default: fprintf(stderr, "Got weird result %d from dequeueOutputBuffer\n", err); return err; } } } if (gRecordAudio) audioRecorder->stop(); ALOGV("Encoder stopping (req=%d)", gStopRequested); if (gVerbose) { printf("Encoder stopping; recorded %u frames in %lld seconds\n", debugNumFrames, nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec)); } return NO_ERROR; }