status_t Converter::initEncoder() { AString outputMIME; CHECK(mOutputFormat->findString("mime", &outputMIME)); bool isAudio = !strncasecmp(outputMIME.c_str(), "audio/", 6); if (!mIsPCMAudio) { mEncoder = MediaCodec::CreateByType( mCodecLooper, outputMIME.c_str(), true /* encoder */); if (mEncoder == NULL) { return ERROR_UNSUPPORTED; } } if (mIsPCMAudio) { return OK; } int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000); int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000); mPrevVideoBitrate = videoBitrate; ALOGI("using audio bitrate of %d bps, video bitrate of %d bps", audioBitrate, videoBitrate); if (isAudio) { mOutputFormat->setInt32("bitrate", audioBitrate); } else { mOutputFormat->setInt32("bitrate", videoBitrate); mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant); mOutputFormat->setInt32("frame-rate", 30); mOutputFormat->setInt32("i-frame-interval", 15); // Iframes every 15 secs // Configure encoder to use intra macroblock refresh mode mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic); int width, height, mbs; if (!mOutputFormat->findInt32("width", &width) || !mOutputFormat->findInt32("height", &height)) { return ERROR_UNSUPPORTED; } // Update macroblocks in a cyclic fashion with 10% of all MBs within // frame gets updated at one time. It takes about 10 frames to // completely update a whole video frame. If the frame rate is 30, // it takes about 333 ms in the best case (if next frame is not an IDR) // to recover from a lost/corrupted packet. mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100; mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs); } ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); mNeedToManuallyPrependSPSPPS = false; status_t err = NO_INIT; if (!isAudio) { sp<AMessage> tmp = mOutputFormat->dup(); tmp->setInt32("prepend-sps-pps-to-idr-frames", 1); err = mEncoder->configure( tmp, NULL /* nativeWindow */, NULL /* crypto */, MediaCodec::CONFIGURE_FLAG_ENCODE); if (err == OK) { // Encoder supported prepending SPS/PPS, we don't need to emulate // it. mOutputFormat = tmp; } else { mNeedToManuallyPrependSPSPPS = true; ALOGI("We going to manually prepend SPS and PPS to IDR frames."); } } if (err != OK) { // We'll get here for audio or if we failed to configure the encoder // to automatically prepend SPS/PPS in the case of video. err = mEncoder->configure( mOutputFormat, NULL /* nativeWindow */, NULL /* crypto */, MediaCodec::CONFIGURE_FLAG_ENCODE); } if (err != OK) { return err; } if (mFlags & FLAG_USE_SURFACE_INPUT) { CHECK(mIsVideo); err = mEncoder->createInputSurface(&mGraphicBufferProducer); if (err != OK) { return err; } } err = mEncoder->start(); if (err != OK) { return err; } err = mEncoder->getInputBuffers(&mEncoderInputBuffers); if (err != OK) { return err; } err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers); if (err != OK) { return err; } if (mFlags & FLAG_USE_SURFACE_INPUT) { scheduleDoMoreWork(); } return OK; }
void Converter::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatMediaPullerNotify: { int32_t what; CHECK(msg->findInt32("what", &what)); if (!mIsPCMAudio && mEncoder == NULL) { ALOGV("got msg '%s' after encoder shutdown.", msg->debugString().c_str()); if (what == MediaPuller::kWhatAccessUnit) { sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); accessUnit->setMediaBufferBase(NULL); } break; } if (what == MediaPuller::kWhatEOS) { mInputBufferQueue.push_back(NULL); feedEncoderInputBuffers(); scheduleDoMoreWork(); } else { CHECK_EQ(what, MediaPuller::kWhatAccessUnit); sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); if (mNumFramesToDrop > 0 || mEncodingSuspended) { if (mNumFramesToDrop > 0) { --mNumFramesToDrop; ALOGI("dropping frame."); } accessUnit->setMediaBufferBase(NULL); break; } #if 0 MediaBuffer *mbuf = (MediaBuffer *)(accessUnit->getMediaBufferBase()); if (mbuf != NULL) { ALOGI("queueing mbuf %p", mbuf); mbuf->release(); } #endif #if ENABLE_SILENCE_DETECTION if (!mIsVideo) { if (IsSilence(accessUnit)) { if (mInSilentMode) { break; } int64_t nowUs = ALooper::GetNowUs(); if (mFirstSilentFrameUs < 0ll) { mFirstSilentFrameUs = nowUs; } else if (nowUs >= mFirstSilentFrameUs + 10000000ll) { mInSilentMode = true; ALOGI("audio in silent mode now."); break; } } else { if (mInSilentMode) { ALOGI("audio no longer in silent mode."); } mInSilentMode = false; mFirstSilentFrameUs = -1ll; } } #endif mInputBufferQueue.push_back(accessUnit); feedEncoderInputBuffers(); scheduleDoMoreWork(); } break; } case kWhatEncoderActivity: { #if 0 int64_t whenUs; if (msg->findInt64("whenUs", &whenUs)) { int64_t nowUs = ALooper::GetNowUs(); ALOGI("[%s] kWhatEncoderActivity after %lld us", mIsVideo ? "video" : "audio", nowUs - whenUs); } #endif mDoMoreWorkPending = false; if (mEncoder == NULL) { break; } status_t err = doMoreWork(); if (err != OK) { notifyError(err); } else { scheduleDoMoreWork(); } break; } case kWhatRequestIDRFrame: { if (mEncoder == NULL) { break; } if (mIsVideo) { ALOGV("requesting IDR frame"); mEncoder->requestIDRFrame(); } break; } case kWhatShutdown: { ALOGI("shutting down %s encoder", mIsVideo ? "video" : "audio"); releaseEncoder(); AString mime; CHECK(mOutputFormat->findString("mime", &mime)); ALOGI("encoder (%s) shut down.", mime.c_str()); sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatShutdownCompleted); notify->post(); break; } case kWhatDropAFrame: { ++mNumFramesToDrop; break; } case kWhatReleaseOutputBuffer: { if (mEncoder != NULL) { size_t bufferIndex; CHECK(msg->findInt32("bufferIndex", (int32_t*)&bufferIndex)); CHECK(bufferIndex < mEncoderOutputBuffers.size()); mEncoder->releaseOutputBuffer(bufferIndex); } break; } case kWhatSuspendEncoding: { int32_t suspend; CHECK(msg->findInt32("suspend", &suspend)); mEncodingSuspended = suspend; if (mFlags & FLAG_USE_SURFACE_INPUT) { sp<AMessage> params = new AMessage; params->setInt32("drop-input-frames",suspend); mEncoder->setParameters(params); } break; } default: TRESPASS(); } }
void Converter::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatMediaPullerNotify: { int32_t what; CHECK(msg->findInt32("what", &what)); if (!mIsPCMAudio && mEncoder == NULL) { ALOGV("got msg '%s' after encoder shutdown.", msg->debugString().c_str()); if (what == MediaPuller::kWhatAccessUnit) { sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); void *mbuf; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) && mbuf != NULL) { ALOGV("releasing mbuf %p", mbuf); accessUnit->meta()->setPointer("mediaBuffer", NULL); static_cast<MediaBuffer *>(mbuf)->release(); mbuf = NULL; } } break; } if (what == MediaPuller::kWhatEOS) { mInputBufferQueue.push_back(NULL); feedEncoderInputBuffers(); scheduleDoMoreWork(); } else { CHECK_EQ(what, MediaPuller::kWhatAccessUnit); sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); #if 0 void *mbuf; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) && mbuf != NULL) { ALOGI("queueing mbuf %p", mbuf); } #endif #if ENABLE_SILENCE_DETECTION if (!mIsVideo) { if (IsSilence(accessUnit)) { if (mInSilentMode) { break; } int64_t nowUs = ALooper::GetNowUs(); if (mFirstSilentFrameUs < 0ll) { mFirstSilentFrameUs = nowUs; } else if (nowUs >= mFirstSilentFrameUs + 10000000ll) { mInSilentMode = true; ALOGI("audio in silent mode now."); break; } } else { if (mInSilentMode) { ALOGI("audio no longer in silent mode."); } mInSilentMode = false; mFirstSilentFrameUs = -1ll; } } #endif mInputBufferQueue.push_back(accessUnit); feedEncoderInputBuffers(); scheduleDoMoreWork(); } break; } case kWhatEncoderActivity: { #if 0 int64_t whenUs; if (msg->findInt64("whenUs", &whenUs)) { int64_t nowUs = ALooper::GetNowUs(); ALOGI("[%s] kWhatEncoderActivity after %lld us", mIsVideo ? "video" : "audio", nowUs - whenUs); } #endif mDoMoreWorkPending = false; if (mEncoder == NULL) { break; } status_t err = doMoreWork(); if (err != OK) { notifyError(err); } else { scheduleDoMoreWork(); } break; } case kWhatRequestIDRFrame: { if (mEncoder == NULL) { break; } if (mIsVideo) { ALOGI("requesting IDR frame"); mEncoder->requestIDRFrame(); } break; } case kWhatShutdown: { ALOGI("shutting down encoder"); /*bugfix: release queue buffer,it may fall into blackhold. * when 4kplayer is floating in the dynamic desktop, * and someone disable wifi in the quicksetting, this will lead to UI deadlock. * It mainly let the source emit onDisplayDisconnect msg to framework. */ while (!mInputBufferQueue.empty()) { sp<ABuffer> accessUnit = *mInputBufferQueue.begin(); mInputBufferQueue.erase(mInputBufferQueue.begin()); void *mbuf = NULL; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) && mbuf != NULL) { ALOGI(">>releasing mbuf %p", mbuf); accessUnit->meta()->setPointer("mediaBuffer", NULL); static_cast<MediaBuffer *>(mbuf)->release(); mbuf = NULL; } } if (mEncoder != NULL) { mEncoder->release(); mEncoder.clear(); } AString mime; CHECK(mInputFormat->findString("mime", &mime)); ALOGI("encoder (%s) shut down.", mime.c_str()); break; } default: TRESPASS(); } }