bool ASessionDescription::getDimensions( size_t index, unsigned long PT, int32_t *width, int32_t *height) const { *width = 0; *height = 0; char key[33]; snprintf(key, sizeof(key), "a=framesize:%lu", PT); if (PT > 9999999) { android_errorWriteLog(0x534e4554, "25747670"); } AString value; if (!findAttribute(index, key, &value)) { return false; } const char *s = value.c_str(); char *end; *width = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK_EQ(*end, '-'); s = end + 1; *height = strtoul(s, &end, 10); CHECK_GT(end, s); CHECK_EQ(*end, '\0'); return true; }
bool SoftAVC::setDecodeArgs( ivd_video_decode_ip_t *ps_dec_ip, ivd_video_decode_op_t *ps_dec_op, OMX_BUFFERHEADERTYPE *inHeader, OMX_BUFFERHEADERTYPE *outHeader, size_t timeStampIx) { size_t sizeY = outputBufferWidth() * outputBufferHeight(); size_t sizeUV; ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t); ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t); ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE; /* When in flush and after EOS with zero byte input, * inHeader is set to zero. Hence check for non-null */ if (inHeader) { ps_dec_ip->u4_ts = timeStampIx; ps_dec_ip->pv_stream_buffer = inHeader->pBuffer + inHeader->nOffset + mInputOffset; ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen - mInputOffset; } else { ps_dec_ip->u4_ts = 0; ps_dec_ip->pv_stream_buffer = NULL; ps_dec_ip->u4_num_Bytes = 0; } sizeUV = sizeY / 4; ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY; ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV; ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV; uint8_t *pBuf; if (outHeader) { if (outHeader->nAllocLen < sizeY + (sizeUV * 2)) { android_errorWriteLog(0x534e4554, "27833616"); return false; } pBuf = outHeader->pBuffer; } else { // mFlushOutBuffer always has the right size. pBuf = mFlushOutBuffer; } ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf; ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY; ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV; ps_dec_ip->s_out_buffer.u4_num_bufs = 3; return true; }
void SoftAACEncoder2::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); if (!mSentCodecSpecificData) { // The very first thing we want to output is the codec specific // data. It does not require any input data but we will need an // output buffer to store it in. if (outQueue.empty()) { return; } if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) { ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } OMX_U32 actualBitRate = aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE); if (mBitRate != actualBitRate) { ALOGW("Requested bitrate %u unsupported, using %u", mBitRate, actualBitRate); } AACENC_InfoStruct encInfo; if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) { ALOGE("Failed to get AAC encoder info"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (outHeader->nOffset + encInfo.confSize > outHeader->nAllocLen) { ALOGE("b/34617444"); android_errorWriteLog(0x534e4554,"34617444"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } outHeader->nFilledLen = encInfo.confSize; outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; uint8_t *out = outHeader->pBuffer + outHeader->nOffset; memcpy(out, encInfo.confBuf, encInfo.confSize); outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); mSentCodecSpecificData = true; } size_t numBytesPerInputFrame = mNumChannels * kNumSamplesPerFrame * sizeof(int16_t); // Limit input size so we only get one ELD frame if (mAACProfile == OMX_AUDIO_AACObjectELD && numBytesPerInputFrame > 512) { numBytesPerInputFrame = 512; } for (;;) { // We do the following until we run out of buffers. while (mInputSize < numBytesPerInputFrame) { // As long as there's still input data to be read we // will drain "kNumSamplesPerFrame * mNumChannels" samples // into the "mInputFrame" buffer and then encode those // as a unit into an output buffer. if (mSawInputEOS || inQueue.empty()) { return; } BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; const void *inData = inHeader->pBuffer + inHeader->nOffset; size_t copy = numBytesPerInputFrame - mInputSize; if (copy > inHeader->nFilledLen) { copy = inHeader->nFilledLen; } if (mInputFrame == NULL) { mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; mAllocatedFrameSize = numBytesPerInputFrame; } else if (mAllocatedFrameSize != numBytesPerInputFrame) { ALOGE("b/34621073: changed size from %d to %d", (int)mAllocatedFrameSize, (int)numBytesPerInputFrame); android_errorWriteLog(0x534e4554,"34621073"); delete mInputFrame; mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; mAllocatedFrameSize = numBytesPerInputFrame; } if (mInputSize == 0) { mInputTimeUs = inHeader->nTimeStamp; } memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); mInputSize += copy; inHeader->nOffset += copy; inHeader->nFilledLen -= copy; // "Time" on the input buffer has in effect advanced by the // number of audio frames we just advanced nOffset by. inHeader->nTimeStamp += (copy * 1000000ll / mSampleRate) / (mNumChannels * sizeof(int16_t)); if (inHeader->nFilledLen == 0) { if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { mSawInputEOS = true; // Pad any remaining data with zeroes. memset((uint8_t *)mInputFrame + mInputSize, 0, numBytesPerInputFrame - mInputSize); mInputSize = numBytesPerInputFrame; } inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); inData = NULL; inHeader = NULL; inInfo = NULL; } } // At this point we have all the input data necessary to encode // a single frame, all we need is an output buffer to store the result // in. if (outQueue.empty()) { return; } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset; size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; AACENC_InArgs inargs; AACENC_OutArgs outargs; memset(&inargs, 0, sizeof(inargs)); memset(&outargs, 0, sizeof(outargs)); inargs.numInSamples = numBytesPerInputFrame / sizeof(int16_t); void* inBuffer[] = { (unsigned char *)mInputFrame }; INT inBufferIds[] = { IN_AUDIO_DATA }; INT inBufferSize[] = { (INT)numBytesPerInputFrame }; INT inBufferElSize[] = { sizeof(int16_t) }; AACENC_BufDesc inBufDesc; inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*); inBufDesc.bufs = (void**)&inBuffer; inBufDesc.bufferIdentifiers = inBufferIds; inBufDesc.bufSizes = inBufferSize; inBufDesc.bufElSizes = inBufferElSize; void* outBuffer[] = { outPtr }; INT outBufferIds[] = { OUT_BITSTREAM_DATA }; INT outBufferSize[] = { 0 }; INT outBufferElSize[] = { sizeof(UCHAR) }; AACENC_BufDesc outBufDesc; outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*); outBufDesc.bufs = (void**)&outBuffer; outBufDesc.bufferIdentifiers = outBufferIds; outBufDesc.bufSizes = outBufferSize; outBufDesc.bufElSizes = outBufferElSize; // Encode the mInputFrame, which is treated as a modulo buffer AACENC_ERROR encoderErr = AACENC_OK; size_t nOutputBytes = 0; do { memset(&outargs, 0, sizeof(outargs)); outBuffer[0] = outPtr; outBufferSize[0] = outAvailable - nOutputBytes; encoderErr = aacEncEncode(mAACEncoder, &inBufDesc, &outBufDesc, &inargs, &outargs); if (encoderErr == AACENC_OK) { outPtr += outargs.numOutBytes; nOutputBytes += outargs.numOutBytes; if (outargs.numInSamples > 0) { int numRemainingSamples = inargs.numInSamples - outargs.numInSamples; if (numRemainingSamples > 0) { memmove(mInputFrame, &mInputFrame[outargs.numInSamples], sizeof(int16_t) * numRemainingSamples); } inargs.numInSamples -= outargs.numInSamples; } } } while (encoderErr == AACENC_OK && inargs.numInSamples > 0); outHeader->nFilledLen = nOutputBytes; outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; if (mSawInputEOS) { // We also tag this output buffer with EOS if it corresponds // to the final input buffer. outHeader->nFlags = OMX_BUFFERFLAG_EOS; } outHeader->nTimeStamp = mInputTimeUs; #if 0 ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", nOutputBytes, mInputTimeUs, outHeader->nFlags); hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); #endif outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); outHeader = NULL; outInfo = NULL; mInputSize = 0; } }
void SoftGSM::onQueueFilled(OMX_U32 portIndex) { if (mSignalledError) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); outHeader->nFilledLen = 0; outHeader->nFlags = OMX_BUFFERFLAG_EOS; outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); return; } if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) { ALOGE("input buffer too large (%ld).", inHeader->nFilledLen); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; } if(((inHeader->nFilledLen / 65) * 65) != inHeader->nFilledLen) { ALOGE("input buffer not multiple of 65 (%ld).", inHeader->nFilledLen); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; } if (outHeader->nAllocLen < (inHeader->nFilledLen / kMSGSMFrameSize) * 320) { ALOGE("output buffer is not large enough (%d).", outHeader->nAllocLen); android_errorWriteLog(0x534e4554, "27793367"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; int n = mSignalledError ? 0 : DecodeGSM(mGsm, reinterpret_cast<int16_t *>(outHeader->pBuffer), inputptr, inHeader->nFilledLen); outHeader->nTimeStamp = inHeader->nTimeStamp; outHeader->nOffset = 0; outHeader->nFilledLen = n * sizeof(int16_t); outHeader->nFlags = 0; inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } }
void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) { List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); if (mOutputPortSettingsChange != NONE) { return; } while (!mHaveEOS && !inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; if (mInputBufferCount < 3) { const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; size_t size = inHeader->nFilledLen; if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && size == 0) { handleEOS(); return; } if (mInputBufferCount == 0) { CHECK(mHeader == NULL); mHeader = new OpusHeader(); memset(mHeader, 0, sizeof(*mHeader)); if (!ParseOpusHeader(data, size, mHeader)) { ALOGV("Parsing Opus Header failed."); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } uint8_t channel_mapping[kMaxChannels] = {0}; if (mHeader->channels <= kMaxChannelsWithDefaultLayout) { memcpy(&channel_mapping, kDefaultOpusChannelLayout, kMaxChannelsWithDefaultLayout); } else { memcpy(&channel_mapping, mHeader->stream_map, mHeader->channels); } int status = OPUS_INVALID_STATE; mDecoder = opus_multistream_decoder_create(kRate, mHeader->channels, mHeader->num_streams, mHeader->num_coupled, channel_mapping, &status); if (!mDecoder || status != OPUS_OK) { ALOGV("opus_multistream_decoder_create failed status=%s", opus_strerror(status)); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } status = opus_multistream_decoder_ctl(mDecoder, OPUS_SET_GAIN(mHeader->gain_db)); if (status != OPUS_OK) { ALOGV("Failed to set OPUS header gain; status=%s", opus_strerror(status)); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } } else if (mInputBufferCount == 1) { mCodecDelay = ns_to_samples( *(reinterpret_cast<int64_t*>(inHeader->pBuffer + inHeader->nOffset)), kRate); mSamplesToDiscard = mCodecDelay; } else { mSeekPreRoll = ns_to_samples( *(reinterpret_cast<int64_t*>(inHeader->pBuffer + inHeader->nOffset)), kRate); notify(OMX_EventPortSettingsChanged, 1, 0, NULL); mOutputPortSettingsChange = AWAITING_DISABLED; } if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { handleEOS(); return; } inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); ++mInputBufferCount; continue; } // Ignore CSD re-submissions. if (mInputBufferCount >= 3 && (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { handleEOS(); return; } inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); continue; } BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { handleEOS(); return; } if (inHeader->nOffset == 0) { mAnchorTimeUs = inHeader->nTimeStamp; mNumFramesOutput = 0; } // When seeking to zero, |mCodecDelay| samples has to be discarded // instead of |mSeekPreRoll| samples (as we would when seeking to any // other timestamp). if (inHeader->nTimeStamp == 0) { mSamplesToDiscard = mCodecDelay; } const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; const uint32_t size = inHeader->nFilledLen; size_t frameSize = kMaxOpusOutputPacketSizeSamples; if (frameSize > outHeader->nAllocLen / sizeof(int16_t) / mHeader->channels) { frameSize = outHeader->nAllocLen / sizeof(int16_t) / mHeader->channels; android_errorWriteLog(0x534e4554, "27833616"); } int numFrames = opus_multistream_decode(mDecoder, data, size, (int16_t *)outHeader->pBuffer, frameSize, 0); if (numFrames < 0) { ALOGE("opus_multistream_decode returned %d", numFrames); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } outHeader->nOffset = 0; if (mSamplesToDiscard > 0) { if (mSamplesToDiscard > numFrames) { mSamplesToDiscard -= numFrames; numFrames = 0; } else { numFrames -= mSamplesToDiscard; outHeader->nOffset = mSamplesToDiscard * sizeof(int16_t) * mHeader->channels; mSamplesToDiscard = 0; } } outHeader->nFilledLen = numFrames * sizeof(int16_t) * mHeader->channels; outHeader->nTimeStamp = mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / kRate; mNumFramesOutput += numFrames; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { outHeader->nFlags = OMX_BUFFERFLAG_EOS; mHaveEOS = true; } else { outHeader->nFlags = 0; } inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); notifyEmptyBufferDone(inHeader); ++mInputBufferCount; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); notifyFillBufferDone(outHeader); } }
void SoftMP3::onQueueFilled(OMX_U32 portIndex) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); if (!mIsFirst) { // pad the end of the stream with 529 samples, since that many samples // were trimmed off the beginning when decoding started outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); memset(outHeader->pBuffer, 0, outHeader->nFilledLen); } else { // Since we never discarded frames from the start, we won't have // to add any padding at the end either. outHeader->nFilledLen = 0; } outHeader->nFlags = OMX_BUFFERFLAG_EOS; outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); return; } if (inHeader->nOffset == 0) { mAnchorTimeUs = inHeader->nTimeStamp; mNumFramesOutput = 0; } mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset; mConfig->inputBufferCurrentLength = inHeader->nFilledLen; mConfig->inputBufferMaxLength = 0; mConfig->inputBufferUsedLength = 0; mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); if ((int32)outHeader->nAllocLen < mConfig->outputFrameSize) { ALOGE("input buffer too small: got %lu, expected %u", outHeader->nAllocLen, mConfig->outputFrameSize); android_errorWriteLog(0x534e4554, "27793371"); notify(OMX_EventError, OMX_ErrorUndefined, OUTPUT_BUFFER_TOO_SMALL, NULL); mSignalledError = true; return; } mConfig->pOutputBuffer = reinterpret_cast<int16_t *>(outHeader->pBuffer); ERROR_CODE decoderErr; if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) != NO_DECODING_ERROR) { ALOGV("mp3 decoder returned error %d", decoderErr); if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR && decoderErr != SIDE_INFO_ERROR) { ALOGE("mp3 decoder returned error %d", decoderErr); if(decoderErr == SYNCH_LOST_ERROR) { mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); } else { notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); mSignalledError = true; return; } } if (mConfig->outputFrameSize == 0) { mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); } // This is recoverable, just ignore the current frame and // play silence instead. memset(outHeader->pBuffer, 0, mConfig->outputFrameSize * sizeof(int16_t)); mConfig->inputBufferUsedLength = inHeader->nFilledLen; } else if (mConfig->samplingRate != mSamplingRate || mConfig->num_channels != mNumChannels) { mSamplingRate = mConfig->samplingRate; mNumChannels = mConfig->num_channels; notify(OMX_EventPortSettingsChanged, 1, 0, NULL); mOutputPortSettingsChange = AWAITING_DISABLED; return; } if (mIsFirst) { mIsFirst = false; // The decoder delay is 529 samples, so trim that many samples off // the start of the first output buffer. This essentially makes this // decoder have zero delay, which the rest of the pipeline assumes. outHeader->nOffset = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; } else { outHeader->nOffset = 0; outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); } outHeader->nTimeStamp = mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate; outHeader->nFlags = 0; CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength); inHeader->nOffset += mConfig->inputBufferUsedLength; inHeader->nFilledLen -= mConfig->inputBufferUsedLength; mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; if (inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } }
void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; if (inHeader == NULL) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; continue; } PortInfo *port = editPortInfo(1); OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); ++mInputBufferCount; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { outHeader->nFilledLen = 0; outHeader->nFlags = OMX_BUFFERFLAG_EOS; List<BufferInfo *>::iterator it = outQueue.begin(); while ((*it)->mHeader != outHeader) { ++it; } BufferInfo *outInfo = *it; outInfo->mOwnedByUs = false; outQueue.erase(it); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } return; } uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset; uint32_t *start_code = (uint32_t *)bitstream; bool volHeader = *start_code == 0xB0010000; if (volHeader) { PVCleanUpVideoDecoder(mHandle); mInitialized = false; } if (!mInitialized) { uint8_t *vol_data[1]; int32_t vol_size = 0; vol_data[0] = NULL; if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) || volHeader) { vol_data[0] = bitstream; vol_size = inHeader->nFilledLen; } MP4DecodingMode mode = (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE; Bool success = PVInitVideoDecoder( mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(), mode); if (!success) { ALOGW("PVInitVideoDecoder failed. Unsupported content?"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); if (mode != actualMode) { notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } PVSetPostProcType((VideoDecControls *) mHandle, 0); bool hasFrameData = false; if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } else if (volHeader) { hasFrameData = true; } mInitialized = true; if (mode == MPEG4_MODE && handlePortSettingsChange()) { return; } if (!hasFrameData) { continue; } } if (!mFramesConfigured) { PortInfo *port = editPortInfo(1); OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader; OMX_U32 yFrameSize = sizeof(uint8) * mHandle->size; if ((outHeader->nAllocLen < yFrameSize) || (outHeader->nAllocLen - yFrameSize < yFrameSize / 2)) { ALOGE("Too small output buffer for reference frame: %lu bytes", (unsigned long)outHeader->nAllocLen); android_errorWriteLog(0x534e4554, "30033990"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } PVSetReferenceYUV(mHandle, outHeader->pBuffer); mFramesConfigured = true; } uint32_t useExtTimestamp = (inHeader->nOffset == 0); // decoder deals in ms (int32_t), OMX in us (int64_t) // so use fake timestamp instead uint32_t timestamp = 0xFFFFFFFF; if (useExtTimestamp) { mPvToOmxTimeMap.add(mPvTime, inHeader->nTimeStamp); timestamp = mPvTime; mPvTime++; } int32_t bufferSize = inHeader->nFilledLen; int32_t tmp = bufferSize; OMX_U32 frameSize; OMX_U64 yFrameSize = (OMX_U64)mWidth * (OMX_U64)mHeight; if (yFrameSize > ((OMX_U64)UINT32_MAX / 3) * 2) { ALOGE("Frame size too large"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } frameSize = (OMX_U32)(yFrameSize + (yFrameSize / 2)); if (outHeader->nAllocLen < frameSize) { android_errorWriteLog(0x534e4554, "27833616"); ALOGE("Insufficient output buffer size"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } // The PV decoder is lying to us, sometimes it'll claim to only have // consumed a subset of the buffer when it clearly consumed all of it. // ignore whatever it says... if (PVDecodeVideoFrame( mHandle, &bitstream, ×tamp, &tmp, &useExtTimestamp, outHeader->pBuffer) != PV_TRUE) { ALOGE("failed to decode video frame."); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } // H263 doesn't have VOL header, the frame size information is in short header, i.e. the // decoder may detect size change after PVDecodeVideoFrame. if (handlePortSettingsChange()) { return; } // decoder deals in ms, OMX in us. outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp); mPvToOmxTimeMap.removeItem(timestamp); inHeader->nOffset += bufferSize; inHeader->nFilledLen = 0; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { outHeader->nFlags = OMX_BUFFERFLAG_EOS; } else { outHeader->nFlags = 0; } if (inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } ++mInputBufferCount; outHeader->nOffset = 0; outHeader->nFilledLen = frameSize; List<BufferInfo *>::iterator it = outQueue.begin(); while ((*it)->mHeader != outHeader) { ++it; } BufferInfo *outInfo = *it; outInfo->mOwnedByUs = false; outQueue.erase(it); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; ++mNumSamplesOutput; } }