diff options
author | Venkatarama Avadhani <venkatarama.avadhani@ittiam.com> | 2015-03-31 18:18:04 +0530 |
---|---|---|
committer | Lajos Molnar <lajos@google.com> | 2015-04-15 15:29:40 -0700 |
commit | 8f9d9d06ed6c8447c7fd02745b2a22dd4bba8e7e (patch) | |
tree | ce0c4692b3195cb538ad1b87016fe44cc6bebd74 | |
parent | 0958e926fef85d7e1e0eb08f330ff46218b72822 (diff) | |
download | frameworks_av-8f9d9d06ed6c8447c7fd02745b2a22dd4bba8e7e.zip frameworks_av-8f9d9d06ed6c8447c7fd02745b2a22dd4bba8e7e.tar.gz frameworks_av-8f9d9d06ed6c8447c7fd02745b2a22dd4bba8e7e.tar.bz2 |
stagefright: add SoftMPEG2 decoder using libmpeg2
Change-Id: I74f5f4a1e6d7be8df45bcbc83a965111454b97ea
-rw-r--r-- | media/libstagefright/codecs/mpeg2dec/Android.mk | 27 | ||||
-rw-r--r-- | media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp | 756 | ||||
-rw-r--r-- | media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h | 178 | ||||
-rwxr-xr-x[-rw-r--r--] | media/libstagefright/data/media_codecs_google_video.xml | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | media/libstagefright/omx/SoftOMXPlugin.cpp | 1 |
5 files changed, 971 insertions, 0 deletions
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.mk b/media/libstagefright/codecs/mpeg2dec/Android.mk new file mode 100644 index 0000000..23b126d --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/Android.mk @@ -0,0 +1,27 @@ +ifeq ($(if $(wildcard external/libmpeg2),1,0),1) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libstagefright_soft_mpeg2dec +LOCAL_MODULE_TAGS := optional + +LOCAL_STATIC_LIBRARIES := libmpeg2dec +LOCAL_SRC_FILES := SoftMPEG2.cpp + +LOCAL_C_INCLUDES := $(TOP)/external/libmpeg2/decoder +LOCAL_C_INCLUDES += $(TOP)/external/libmpeg2/common +LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include +LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax + +LOCAL_SHARED_LIBRARIES := libstagefright +LOCAL_SHARED_LIBRARIES += libstagefright_omx +LOCAL_SHARED_LIBRARIES += libstagefright_foundation +LOCAL_SHARED_LIBRARIES += libutils +LOCAL_SHARED_LIBRARIES += liblog + +LOCAL_LDFLAGS := -Wl,-Bsymbolic + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp new file mode 100644 index 0000000..fb7394b --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp @@ -0,0 +1,756 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoftMPEG2" +#include <utils/Log.h> + +#include "iv_datatypedef.h" +#include "iv.h" +#include "ivd.h" +#include "ithread.h" +#include "impeg2d.h" +#include "SoftMPEG2.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaDefs.h> +#include <OMX_VideoExt.h> + +namespace android { + +#define componentName "video_decoder.mpeg2" +#define codingType OMX_VIDEO_CodingMPEG2 +#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_MPEG2 + +/** Function and structure definitions to keep code similar for each codec */ +#define ivdec_api_function impeg2d_api_function +#define ivdext_init_ip_t impeg2d_init_ip_t +#define ivdext_init_op_t impeg2d_init_op_t +#define ivdext_fill_mem_rec_ip_t impeg2d_fill_mem_rec_ip_t +#define ivdext_fill_mem_rec_op_t impeg2d_fill_mem_rec_op_t +#define ivdext_ctl_set_num_cores_ip_t impeg2d_ctl_set_num_cores_ip_t +#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t + +#define IVDEXT_CMD_CTL_SET_NUM_CORES \ + (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelLL }, + { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelML }, + { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelH14 }, + { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL }, + + { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelLL }, + { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelML }, + { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelH14 }, + { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelHL }, +}; + +SoftMPEG2::SoftMPEG2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent( + name, componentName, codingType, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + 320 /* width */, 240 /* height */, callbacks, + appData, component), + mMemRecords(NULL), + mFlushOutBuffer(NULL), + mOmxColorFormat(OMX_COLOR_FormatYUV420Planar), + mIvColorFormat(IV_YUV_420P), + mNewWidth(mWidth), + mNewHeight(mHeight), + mChangingResolution(false) { + initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE); + + // If input dump is enabled, then open create an empty file + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); + + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftMPEG2::~SoftMPEG2() { + CHECK_EQ(deInitDecoder(), (status_t)OK); +} + + +static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) { + OMX_S64 minTimeStamp = LLONG_MAX; + int idx = -1; + for (size_t i = 0; i < MAX_TIME_STAMPS; i++) { + if (pIsTimeStampValid[i]) { + if (pNTimeStamp[i] < minTimeStamp) { + minTimeStamp = pNTimeStamp[i]; + idx = i; + } + } + } + return idx; +} + +static size_t GetCPUCoreCount() { + long cpuCoreCount = 1; +#if defined(_SC_NPROCESSORS_ONLN) + cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN); +#else + // _SC_NPROC_ONLN must be defined... + cpuCoreCount = sysconf(_SC_NPROC_ONLN); +#endif + CHECK(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +void SoftMPEG2::logVersion() { + ivd_ctl_getversioninfo_ip_t s_ctl_ip; + ivd_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t); + s_ctl_ip.pv_version_buffer = au1_buf; + s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version number: 0x%x", + s_ctl_op.u4_error_code); + } else { + ALOGV("Ittiam decoder version number: %s", + (char *)s_ctl_ip.pv_version_buffer); + } + return; +} + +status_t SoftMPEG2::setParams(size_t stride) { + ivd_ctl_set_config_ip_t s_ctl_ip; + ivd_ctl_set_config_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + s_ctl_ip.u4_disp_wd = (UWORD32)stride; + s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; + + s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; + s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME; + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; + s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); + + ALOGV("Set the run-time (dynamic) parameters stride = %u", stride); + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the run-time parameters: 0x%x", + s_ctl_op.u4_error_code); + + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftMPEG2::resetPlugin() { + mIsInFlush = false; + mReceivedEOS = false; + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + /* Initialize both start and end times */ + gettimeofday(&mTimeStart, NULL); + gettimeofday(&mTimeEnd, NULL); + + return OK; +} + +status_t SoftMPEG2::resetDecoder() { + ivd_ctl_reset_ip_t s_ctl_ip; + ivd_ctl_reset_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; + s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + if (IV_SUCCESS != status) { + ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); + return UNKNOWN_ERROR; + } + + /* Set the run-time (dynamic) parameters */ + setParams(outputBufferWidth()); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + return OK; +} + +status_t SoftMPEG2::setNumCores() { + ivdext_ctl_set_num_cores_ip_t s_set_cores_ip; + ivdext_ctl_set_num_cores_op_t s_set_cores_op; + IV_API_CALL_STATUS_T status; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES); + s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op); + if (IV_SUCCESS != status) { + ALOGE("Error in setting number of cores: 0x%x", + s_set_cores_op.u4_error_code); + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftMPEG2::setFlushMode() { + IV_API_CALL_STATUS_T status; + ivd_ctl_flush_ip_t s_video_flush_ip; + ivd_ctl_flush_op_t s_video_flush_op; + + s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH; + s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t); + s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t); + + /* Set the decoder in Flush mode, subsequent decode() calls will flush */ + status = ivdec_api_function( + mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status, + s_video_flush_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mIsInFlush = true; + return OK; +} + +status_t SoftMPEG2::initDecoder() { + IV_API_CALL_STATUS_T status; + + UWORD32 u4_num_reorder_frames; + UWORD32 u4_num_ref_frames; + UWORD32 u4_share_disp_buf; + + mNumCores = GetCPUCoreCount(); + + /* Initialize number of ref and reorder modes (for MPEG2) */ + u4_num_reorder_frames = 16; + u4_num_ref_frames = 16; + u4_share_disp_buf = 0; + + uint32_t displayStride = outputBufferWidth(); + uint32_t displayHeight = outputBufferHeight(); + uint32_t displaySizeY = displayStride * displayHeight; + + { + iv_num_mem_rec_ip_t s_num_mem_rec_ip; + iv_num_mem_rec_op_t s_num_mem_rec_op; + + s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip); + s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op); + s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC; + + status = ivdec_api_function( + mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op); + if (IV_SUCCESS != status) { + ALOGE("Error in getting mem records: 0x%x", + s_num_mem_rec_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec; + } + + mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc( + 128, mNumMemRecords * sizeof(iv_mem_rec_t)); + if (mMemRecords == NULL) { + ALOGE("Allocation failure"); + return NO_MEMORY; + } + + memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t)); + + { + size_t i; + ivdext_fill_mem_rec_ip_t s_fill_mem_ip; + ivdext_fill_mem_rec_op_t s_fill_mem_op; + iv_mem_rec_t *ps_mem_rec; + + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = + sizeof(ivdext_fill_mem_rec_ip_t); + + s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf; + s_fill_mem_ip.e_output_format = mIvColorFormat; + + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight; + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = + sizeof(ivdext_fill_mem_rec_op_t); + + ps_mem_rec = mMemRecords; + for (i = 0; i < mNumMemRecords; i++) { + ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t); + } + + status = ivdec_api_function( + mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op); + + if (IV_SUCCESS != status) { + ALOGE("Error in filling mem records: 0x%x", + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + mNumMemRecords = + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled; + + ps_mem_rec = mMemRecords; + + for (i = 0; i < mNumMemRecords; i++) { + ps_mem_rec->pv_base = ivd_aligned_malloc( + ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size); + if (ps_mem_rec->pv_base == NULL) { + ALOGE("Allocation failure for memory record #%zu of size %u", + i, ps_mem_rec->u4_mem_size); + status = IV_FAIL; + return NO_MEMORY; + } + + ps_mem_rec++; + } + } + + /* Initialize the decoder */ + { + ivdext_init_ip_t s_init_ip; + ivdext_init_op_t s_init_op; + + void *dec_fxns = (void *)ivdec_api_function; + + s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t); + s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT; + s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight; + + s_init_ip.u4_share_disp_buf = u4_share_disp_buf; + + s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op); + + s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords; + s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat; + + mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base; + mCodecCtx->pv_fxns = dec_fxns; + mCodecCtx->u4_size = sizeof(iv_obj_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op); + if (status != IV_SUCCESS) { + ALOGE("Error in init: 0x%x", + s_init_op.s_ivd_init_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + } + + /* Reset the plugin state */ + resetPlugin(); + + /* Set the run time (dynamic) parameters */ + setParams(displayStride); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + /* Get codec version */ + logVersion(); + + /* Allocate internal picture buffer */ + uint32_t bufferSize = displaySizeY * 3 / 2; + mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %zu", bufferSize); + return NO_MEMORY; + } + + mInitNeeded = false; + mFlushNeeded = false; + return OK; +} + +status_t SoftMPEG2::deInitDecoder() { + size_t i; + + if (mMemRecords) { + iv_mem_rec_t *ps_mem_rec; + + ps_mem_rec = mMemRecords; + for (i = 0; i < mNumMemRecords; i++) { + if (ps_mem_rec->pv_base) { + ivd_aligned_free(ps_mem_rec->pv_base); + } + ps_mem_rec++; + } + ivd_aligned_free(mMemRecords); + mMemRecords = NULL; + } + + if (mFlushOutBuffer) { + ivd_aligned_free(mFlushOutBuffer); + mFlushOutBuffer = NULL; + } + + mInitNeeded = true; + mChangingResolution = false; + + return OK; +} + +status_t SoftMPEG2::reInitDecoder() { + status_t ret; + + deInitDecoder(); + + ret = initDecoder(); + if (OK != ret) { + ALOGE("Create failure"); + deInitDecoder(); + return NO_MEMORY; + } + return OK; +} + +void SoftMPEG2::onReset() { + SoftVideoDecoderOMXComponent::onReset(); + + resetDecoder(); + resetPlugin(); +} + +OMX_ERRORTYPE SoftMPEG2::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) { + const uint32_t oldWidth = mWidth; + const uint32_t oldHeight = mHeight; + OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params); + if (mWidth != oldWidth || mHeight != oldHeight) { + reInitDecoder(); + } + return ret; +} + +void SoftMPEG2::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; + uint8_t *pBuf; + + 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; + ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen; + } else { + ps_dec_ip->u4_ts = 0; + ps_dec_ip->pv_stream_buffer = NULL; + ps_dec_ip->u4_num_Bytes = 0; + } + + if (outHeader) { + pBuf = outHeader->pBuffer; + } else { + pBuf = mFlushOutBuffer; + } + + 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; + + 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; +} +void SoftMPEG2::onPortFlushCompleted(OMX_U32 portIndex) { + /* Once the output buffers are flushed, ignore any buffers that are held in decoder */ + if (kOutputPortIndex == portIndex) { + setFlushMode(); + + while (true) { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + IV_API_CALL_STATUS_T status; + size_t sizeY, sizeUV; + + setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0); + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + if (0 == s_dec_op.u4_output_present) { + resetPlugin(); + break; + } + } + } +} + +void SoftMPEG2::onQueueFilled(OMX_U32 portIndex) { + UNUSED(portIndex); + + if (mOutputPortSettingsChange != NONE) { + return; + } + + List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); + List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); + + /* If input EOS is seen and decoder is not in flush mode, + * set the decoder in flush mode. + * There can be a case where EOS is sent along with last picture data + * In that case, only after decoding that input data, decoder has to be + * put in flush. This case is handled here */ + + if (mReceivedEOS && !mIsInFlush) { + setFlushMode(); + } + + while (!outQueue.empty()) { + BufferInfo *inInfo; + OMX_BUFFERHEADERTYPE *inHeader; + + BufferInfo *outInfo; + OMX_BUFFERHEADERTYPE *outHeader; + size_t timeStampIx; + + inInfo = NULL; + inHeader = NULL; + + if (!mIsInFlush) { + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } else { + break; + } + } + + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + outHeader->nTimeStamp = 0; + outHeader->nOffset = 0; + + if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + mReceivedEOS = true; + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + setFlushMode(); + } + } + + // When there is an init required and the decoder is not in flush mode, + // update output port's definition and reinitialize decoder. + if (mInitNeeded && !mIsInFlush) { + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); + + CHECK_EQ(reInitDecoder(), (status_t)OK); + return; + } + + /* Get a free slot in timestamp array to hold input timestamp */ + { + size_t i; + timeStampIx = 0; + for (i = 0; i < MAX_TIME_STAMPS; i++) { + if (!mTimeStampsValid[i]) { + timeStampIx = i; + break; + } + } + if (inHeader != NULL) { + mTimeStampsValid[timeStampIx] = true; + mTimeStamps[timeStampIx] = inHeader->nTimeStamp; + } + } + + { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + WORD32 timeDelay, timeTaken; + size_t sizeY, sizeUV; + + setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); + // If input dump is enabled, then write to file + DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes); + + if (s_dec_ip.u4_num_Bytes > 0) { + char *ptr = (char *)s_dec_ip.pv_stream_buffer; + } + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + IV_API_CALL_STATUS_T status; + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + + bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code); + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); + + GETTIME(&mTimeEnd, NULL); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + s_dec_op.u4_num_bytes_consumed); + if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { + mFlushNeeded = true; + } + + if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { + /* If the input did not contain picture data, then ignore + * the associated timestamp */ + mTimeStampsValid[timeStampIx] = false; + } + + // This is needed to handle CTS DecoderTest testCodecResetsMPEG2WithoutSurface, + // which is not sending SPS/PPS after port reconfiguration and flush to the codec. + if (unsupportedDimensions && !mFlushNeeded) { + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht); + + CHECK_EQ(reInitDecoder(), (status_t)OK); + + setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); + + ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + return; + } + + // If the decoder is in the changing resolution mode and there is no output present, + // that means the switching is done and it's ready to reset the decoder and the plugin. + if (mChangingResolution && !s_dec_op.u4_output_present) { + mChangingResolution = false; + resetDecoder(); + resetPlugin(); + continue; + } + + if (unsupportedDimensions || resChanged) { + mChangingResolution = true; + if (mFlushNeeded) { + setFlushMode(); + } + + if (unsupportedDimensions) { + mNewWidth = s_dec_op.u4_pic_wd; + mNewHeight = s_dec_op.u4_pic_ht; + mInitNeeded = true; + } + continue; + } + + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { + uint32_t width = s_dec_op.u4_pic_wd; + uint32_t height = s_dec_op.u4_pic_ht; + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, width, height); + + if (portWillReset) { + resetDecoder(); + return; + } + } + + if (s_dec_op.u4_output_present) { + size_t timeStampIdx; + outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; + + timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid); + outHeader->nTimeStamp = mTimeStamps[timeStampIdx]; + mTimeStampsValid[timeStampIdx] = false; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } else { + /* If in flush mode and no output is returned by the codec, + * then come out of flush mode */ + mIsInFlush = false; + + /* If EOS was recieved on input port and there is no output + * from the codec, then signal EOS on output port */ + if (mReceivedEOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags |= OMX_BUFFERFLAG_EOS; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + resetPlugin(); + } + } + } + + // TODO: Handle more than one picture data + if (inHeader != NULL) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } +} + +} // namespace android + +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + return new android::SoftMPEG2(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h new file mode 100644 index 0000000..f7b1961 --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h @@ -0,0 +1,178 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOFT_MPEG2_H_ + +#define SOFT_MPEG2_H_ + +#include "SoftVideoDecoderOMXComponent.h" +#include <sys/time.h> + +namespace android { + +#define ivd_aligned_malloc(alignment, size) memalign(alignment, size) +#define ivd_aligned_free(buf) free(buf) + +/** Number of entries in the time-stamp array */ +#define MAX_TIME_STAMPS 64 + +/** Maximum number of cores supported by the codec */ +#define CODEC_MAX_NUM_CORES 4 + +#define CODEC_MAX_WIDTH 1920 + +#define CODEC_MAX_HEIGHT 1088 + +/** Input buffer size */ +#define INPUT_BUF_SIZE (1024 * 1024) + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = ((end.tv_sec - start.tv_sec) * 1000000) + \ + (end.tv_usec - start.tv_usec); + +struct SoftMPEG2 : public SoftVideoDecoderOMXComponent { + SoftMPEG2( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMPEG2(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params); +private: + // Number of input and output buffers + enum { + kNumBuffers = 8 + }; + + iv_obj_t *mCodecCtx; // Codec context + iv_mem_rec_t *mMemRecords; // Memory records requested by the codec + size_t mNumMemRecords; // Number of memory records requested by the codec + + size_t mNumCores; // Number of cores to be uesd by the codec + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + // Internal buffer to be used to flush out the buffers from decoder + uint8_t *mFlushOutBuffer; + + // Status of entries in the timestamp array + bool mTimeStampsValid[MAX_TIME_STAMPS]; + + // Timestamp array - Since codec does not take 64 bit timestamps, + // they are maintained in the plugin + OMX_S64 mTimeStamps[MAX_TIME_STAMPS]; + +#ifdef FILE_DUMP_ENABLE + char mInFile[200]; +#endif /* FILE_DUMP_ENABLE */ + + OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format + IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format + + bool mIsInFlush; // codec is flush mode + bool mReceivedEOS; // EOS is receieved on input port + bool mInitNeeded; + uint32_t mNewWidth; + uint32_t mNewHeight; + // The input stream has changed to a different resolution, which is still supported by the + // codec. So the codec is switching to decode the new resolution. + bool mChangingResolution; + bool mFlushNeeded; + + status_t initDecoder(); + status_t deInitDecoder(); + status_t setFlushMode(); + status_t setParams(size_t stride); + void logVersion(); + status_t setNumCores(); + status_t resetDecoder(); + status_t resetPlugin(); + status_t reInitDecoder(); + + void 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); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG2); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/sdcard/media/mpeg2d_input" +#define INPUT_DUMP_EXT "m2v" + +#define GENERATE_FILE_NAMES() { \ + GETTIME(&mTimeStart, NULL); \ + strcpy(mInFile, ""); \ + sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ + mTimeStart.tv_sec, mTimeStart.tv_usec, \ + INPUT_DUMP_EXT); \ +} + +#define CREATE_DUMP_FILE(m_filename) { \ + FILE *fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ +} +#define DUMP_TO_FILE(m_filename, m_buf, m_size) \ +{ \ + FILE *fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL) { \ + int i; \ + i = fwrite(m_buf, 1, m_size, fp); \ + ALOGD("fwrite ret %d to write %d", i, m_size); \ + if (i != (int)m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + fclose(fp); \ + } else { \ + ALOGD("Could not write to file %s", m_filename);\ + } \ +} +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // SOFT_MPEG2_H_ diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml index 7e9fa18..e7dc173 100644..100755 --- a/media/libstagefright/data/media_codecs_google_video.xml +++ b/media/libstagefright/data/media_codecs_google_video.xml @@ -16,6 +16,15 @@ <Included> <Decoders> + <MediaCodec name="OMX.google.mpeg2.decoder" type="video/mpeg2"> + <!-- profiles and levels: ProfileMain : LevelHL --> + <Limit name="size" min="16x16" max="1920x1088" /> + <Limit name="alignment" value="2x2" /> + <Limit name="block-size" value="16x16" /> + <Limit name="blocks-per-second" range="1-244800" /> + <Limit name="bitrate" range="1-20000000" /> + <Feature name="adaptive-playback" /> + </MediaCodec> <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es"> <!-- profiles and levels: ProfileSimple : Level3 --> <Limit name="size" min="2x2" max="352x288" /> diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp index 3ab241a..41135ff 100644..100755 --- a/media/libstagefright/omx/SoftOMXPlugin.cpp +++ b/media/libstagefright/omx/SoftOMXPlugin.cpp @@ -45,6 +45,7 @@ static const struct { { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" }, { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" }, { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" }, + { "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" }, { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" }, { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" }, { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" }, |