blob: 9e7bafde310662e07e32403db9ad7711adffa40e [file] [log] [blame]
/*
* Copyright (C) 2011 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.
*/
/**
*************************************************************************
* @file VideoEditorVideoDecoder.cpp
* @brief StageFright shell video decoder
*************************************************************************
*/
#define LOG_NDEBUG 1
#define LOG_TAG "VIDEOEDITOR_VIDEODECODER"
/*******************
* HEADERS *
*******************/
#include "VideoEditorVideoDecoder_internal.h"
#include "VideoEditorUtils.h"
#include "M4VD_Tools.h"
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDefs.h>
/********************
* DEFINITIONS *
********************/
#define MAX_DEC_BUFFERS 10
/********************
* SOURCE CLASS *
********************/
using namespace android;
static M4OSA_ERR copyBufferToQueue(
VideoEditorVideoDecoder_Context* pDecShellContext,
MediaBuffer* pDecodedBuffer);
class VideoEditorVideoDecoderSource : public MediaSource {
public:
VideoEditorVideoDecoderSource(
const sp<MetaData> &format,
VIDEOEDITOR_CodecType codecType,
void *decoderShellContext);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
protected :
virtual ~VideoEditorVideoDecoderSource();
private:
sp<MetaData> mFormat;
MediaBuffer* mBuffer;
MediaBufferGroup* mGroup;
Mutex mLock;
VideoEditorVideoDecoder_Context* mpDecShellContext;
int32_t mMaxAUSize;
bool mStarted;
VIDEOEDITOR_CodecType mCodecType;
// Don't call me
VideoEditorVideoDecoderSource(const VideoEditorVideoDecoderSource &);
VideoEditorVideoDecoderSource &operator=(
const VideoEditorVideoDecoderSource &);
};
VideoEditorVideoDecoderSource::VideoEditorVideoDecoderSource(
const sp<MetaData> &format, VIDEOEDITOR_CodecType codecType,
void *decoderShellContext) :
mFormat(format),
mBuffer(NULL),
mGroup(NULL),
mStarted(false),
mCodecType(codecType) {
mpDecShellContext = (VideoEditorVideoDecoder_Context*) decoderShellContext;
}
VideoEditorVideoDecoderSource::~VideoEditorVideoDecoderSource() {
if (mStarted == true) {
stop();
}
}
status_t VideoEditorVideoDecoderSource::start(
MetaData *params) {
if (!mStarted) {
if (mFormat->findInt32(kKeyMaxInputSize, &mMaxAUSize) == false) {
ALOGE("Could not find kKeyMaxInputSize");
return ERROR_MALFORMED;
}
mGroup = new MediaBufferGroup;
if (mGroup == NULL) {
ALOGE("FATAL: memory limitation ! ");
return NO_MEMORY;
}
mGroup->add_buffer(new MediaBuffer(mMaxAUSize));
mStarted = true;
}
return OK;
}
status_t VideoEditorVideoDecoderSource::stop() {
if (mStarted) {
if (mBuffer != NULL) {
// FIXME:
// Why do we need to check on the ref count?
int ref_count = mBuffer->refcount();
ALOGV("MediaBuffer refcount is %d",ref_count);
for (int i = 0; i < ref_count; ++i) {
mBuffer->release();
}
mBuffer = NULL;
}
delete mGroup;
mGroup = NULL;
mStarted = false;
}
return OK;
}
sp<MetaData> VideoEditorVideoDecoderSource::getFormat() {
Mutex::Autolock autolock(mLock);
return mFormat;
}
status_t VideoEditorVideoDecoderSource::read(MediaBuffer** buffer_out,
const ReadOptions *options) {
Mutex::Autolock autolock(mLock);
if (options != NULL) {
int64_t time_us;
MediaSource::ReadOptions::SeekMode mode;
options->getSeekTo(&time_us, &mode);
if (mode != MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC) {
ALOGE("Unexpected read options");
return BAD_VALUE;
}
M4OSA_ERR err;
M4OSA_Int32 rapTime = time_us / 1000;
/*--- Retrieve the previous RAP time ---*/
err = mpDecShellContext->m_pReaderGlobal->m_pFctGetPrevRapTime(
mpDecShellContext->m_pReader->m_readerContext,
(M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler,
&rapTime);
if (err == M4WAR_READER_INFORMATION_NOT_PRESENT) {
/* No RAP table, jump backward and predecode */
rapTime -= 40000;
if(rapTime < 0) rapTime = 0;
} else if (err != OK) {
ALOGE("get rap time error = 0x%x\n", (uint32_t)err);
return UNKNOWN_ERROR;
}
err = mpDecShellContext->m_pReaderGlobal->m_pFctJump(
mpDecShellContext->m_pReader->m_readerContext,
(M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler,
&rapTime);
if (err != OK) {
ALOGE("jump err = 0x%x\n", (uint32_t)err);
return BAD_VALUE;
}
}
*buffer_out = NULL;
M4OSA_ERR lerr = mGroup->acquire_buffer(&mBuffer);
if (lerr != OK) {
return lerr;
}
mBuffer->meta_data()->clear(); // clear all the meta data
if (mStarted) {
//getNext AU from reader.
M4_AccessUnit* pAccessUnit = mpDecShellContext->m_pNextAccessUnitToDecode;
lerr = mpDecShellContext->m_pReader->m_pFctGetNextAu(
mpDecShellContext->m_pReader->m_readerContext,
(M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler,
pAccessUnit);
if (lerr == M4WAR_NO_DATA_YET || lerr == M4WAR_NO_MORE_AU) {
*buffer_out = NULL;
return ERROR_END_OF_STREAM;
}
//copy the reader AU buffer to mBuffer
M4OSA_UInt32 lSize = (pAccessUnit->m_size > (M4OSA_UInt32)mMaxAUSize)\
? (M4OSA_UInt32)mMaxAUSize : pAccessUnit->m_size;
memcpy((void *)mBuffer->data(),(void *)pAccessUnit->m_dataAddress,
lSize);
mBuffer->set_range(0, lSize);
int64_t frameTimeUs = (int64_t) (pAccessUnit->m_CTS * 1000);
mBuffer->meta_data()->setInt64(kKeyTime, frameTimeUs);
// Replace the AU start code for H264
if (VIDEOEDITOR_kH264VideoDec == mCodecType) {
uint8_t *data =(uint8_t *)mBuffer->data() + mBuffer->range_offset();
data[0]=0;
data[1]=0;
data[2]=0;
data[3]=1;
}
mBuffer->meta_data()->setInt32(kKeyIsSyncFrame,
(pAccessUnit->m_attribute == 0x04)? 1 : 0);
*buffer_out = mBuffer;
}
return OK;
}
static M4OSA_UInt32 VideoEditorVideoDecoder_GetBitsFromMemory(
VIDEOEDITOR_VIDEO_Bitstream_ctxt* parsingCtxt, M4OSA_UInt32 nb_bits) {
return (M4VD_Tools_GetBitsFromMemory((M4VS_Bitstream_ctxt*) parsingCtxt,
nb_bits));
}
M4OSA_ERR VideoEditorVideoDecoder_internalParseVideoDSI(M4OSA_UInt8* pVol,
M4OSA_Int32 aVolSize, M4DECODER_MPEG4_DecoderConfigInfo* pDci,
M4DECODER_VideoSize* pVideoSize) {
VIDEOEDITOR_VIDEO_Bitstream_ctxt parsingCtxt;
M4OSA_UInt32 code, j;
M4OSA_MemAddr8 start;
M4OSA_UInt8 i;
M4OSA_UInt32 time_incr_length;
M4OSA_UInt8 vol_verid=0, b_hierarchy_type;
/* Parsing variables */
M4OSA_UInt8 video_object_layer_shape = 0;
M4OSA_UInt8 sprite_enable = 0;
M4OSA_UInt8 reduced_resolution_vop_enable = 0;
M4OSA_UInt8 scalability = 0;
M4OSA_UInt8 enhancement_type = 0;
M4OSA_UInt8 complexity_estimation_disable = 0;
M4OSA_UInt8 interlaced = 0;
M4OSA_UInt8 sprite_warping_points = 0;
M4OSA_UInt8 sprite_brightness_change = 0;
M4OSA_UInt8 quant_precision = 0;
/* Fill the structure with default parameters */
pVideoSize->m_uiWidth = 0;
pVideoSize->m_uiHeight = 0;
pDci->uiTimeScale = 0;
pDci->uiProfile = 0;
pDci->uiUseOfResynchMarker = 0;
pDci->bDataPartition = M4OSA_FALSE;
pDci->bUseOfRVLC = M4OSA_FALSE;
/* Reset the bitstream context */
parsingCtxt.stream_byte = 0;
parsingCtxt.stream_index = 8;
parsingCtxt.in = (M4OSA_MemAddr8) pVol;
start = (M4OSA_MemAddr8) pVol;
/* Start parsing */
while (parsingCtxt.in - start < aVolSize) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt, 8);
if (code == 0) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt, 8);
if (code == 0) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt,8);
if (code == 1) {
/* start code found */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);
/* ----- 0x20..0x2F : video_object_layer_start_code ----- */
if ((code > 0x1F) && (code < 0x30)) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
if (code == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4);
vol_verid = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 3);
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4);
if (code == 15) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 16);
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
if (code == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 3);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
if (code == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 32);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 31);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 16);
}
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 2);
/* Need to save it for vop parsing */
video_object_layer_shape = (M4OSA_UInt8)code;
if (code != 0) {
return 0; /* only rectangular case supported */
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 16);
pDci->uiTimeScale = code;
/* Computes time increment length */
j = code - 1;
for (i = 0; (i < 32) && (j != 0); j >>=1) {
i++;
}
time_incr_length = (i == 0) ? 1 : i;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
if (code == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, time_incr_length);
}
if(video_object_layer_shape != 1) { /* 1 = Binary */
if(video_object_layer_shape == 0) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* Width */
pVideoSize->m_uiWidth = code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* Height */
pVideoSize->m_uiHeight = code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
}
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* interlaced */
interlaced = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* OBMC disable */
if(vol_verid == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* sprite enable */
sprite_enable = (M4OSA_UInt8)code;
} else {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 2);/* sprite enable */
sprite_enable = (M4OSA_UInt8)code;
}
if ((sprite_enable == 1) || (sprite_enable == 2)) {
if (sprite_enable != 2) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* sprite width */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* sprite height */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* sprite l coordinate */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 13);/* sprite top coordinate */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* Marker bit */
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 6);/* sprite warping points */
sprite_warping_points = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 2);/* sprite warping accuracy */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* sprite brightness change */
sprite_brightness_change = (M4OSA_UInt8)code;
if (sprite_enable != 2) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
}
}
if ((vol_verid != 1) && (video_object_layer_shape != 0)){
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* sadct disable */
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1); /* not 8 bits */
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4);/* quant precision */
quant_precision = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4);/* bits per pixel */
}
/* greyscale not supported */
if(video_object_layer_shape == 3) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 3);
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* quant type */
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* load intra quant mat */
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);/* */
i = 1;
while (i < 64) {
code =
VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);
if (code == 0) {
break;
}
i++;
}
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* load non intra quant mat */
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);/* */
i = 1;
while (i < 64) {
code =
VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);
if (code == 0) {
break;
}
i++;
}
}
}
if (vol_verid != 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* quarter sample */
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* complexity estimation disable */
complexity_estimation_disable = (M4OSA_UInt8)code;
if (!code) {
//return M4ERR_NOT_IMPLEMENTED;
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* resync marker disable */
pDci->uiUseOfResynchMarker = (code) ? 0 : 1;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* data partitionned */
pDci->bDataPartition = (code) ? M4OSA_TRUE : M4OSA_FALSE;
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* reversible VLC */
pDci->bUseOfRVLC = (code) ? M4OSA_TRUE : M4OSA_FALSE;
}
if (vol_verid != 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* newpred */
if (code) {
//return M4ERR_PARAMETER;
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);
reduced_resolution_vop_enable = (M4OSA_UInt8)code;
}
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* scalability */
scalability = (M4OSA_UInt8)code;
if (code) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* hierarchy type */
b_hierarchy_type = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4);/* ref layer id */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* ref sampling direct */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);/* hor sampling factor N */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);/* hor sampling factor M */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);/* vert sampling factor N */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);/* vert sampling factor M */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* enhancement type */
enhancement_type = (M4OSA_UInt8)code;
if ((!b_hierarchy_type) &&
(video_object_layer_shape == 1)) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* use ref shape */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* use ref texture */
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 5);
}
}
break;
}
/* ----- 0xB0 : visual_object_sequence_start_code ----- */
else if(code == 0xB0) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 8);/* profile_and_level_indication */
pDci->uiProfile = (M4OSA_UInt8)code;
}
/* ----- 0xB5 : visual_object_start_code ----- */
else if(code == 0xB5) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 1);/* is object layer identifier */
if (code == 1) {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 4); /* visual object verid */
vol_verid = (M4OSA_UInt8)code;
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 3);
} else {
code = VideoEditorVideoDecoder_GetBitsFromMemory(
&parsingCtxt, 7); /* Realign on byte */
vol_verid = 1;
}
}
/* ----- end ----- */
} else {
if ((code >> 2) == 0x20) {
/* H263 ...-> wrong*/
break;
}
}
}
}
}
return M4NO_ERROR;
}
M4VIFI_UInt8 M4VIFI_SemiplanarYVU420toYUV420(void *user_data,
M4VIFI_UInt8 *inyuv, M4VIFI_ImagePlane *PlaneOut ) {
M4VIFI_UInt8 return_code = M4VIFI_OK;
M4VIFI_UInt8 *outyuv =
((M4VIFI_UInt8*)&(PlaneOut[0].pac_data[PlaneOut[0].u_topleft]));
int32_t width = PlaneOut[0].u_width;
int32_t height = PlaneOut[0].u_height;
int32_t outYsize = width * height;
uint32_t *outy = (uint32_t *) outyuv;
uint16_t *outcb =
(uint16_t *) &(PlaneOut[1].pac_data[PlaneOut[1].u_topleft]);
uint16_t *outcr =
(uint16_t *) &(PlaneOut[2].pac_data[PlaneOut[2].u_topleft]);
/* Y copying */
memcpy((void *)outy, (void *)inyuv, outYsize);
/* U & V copying */
uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
for (int32_t i = height >> 1; i > 0; --i) {
for (int32_t j = width >> 2; j > 0; --j) {
uint32_t temp = *inyuv_4++;
uint32_t tempU = temp & 0xFF;
tempU = tempU | ((temp >> 8) & 0xFF00);
uint32_t tempV = (temp >> 8) & 0xFF;
tempV = tempV | ((temp >> 16) & 0xFF00);
// Flip U and V
*outcb++ = tempV;
*outcr++ = tempU;
}
}
return return_code;
}
void logSupportDecodersAndCapabilities(M4DECODER_VideoDecoders* decoders) {
VideoDecoder *pDecoder;
VideoComponentCapabilities *pOmxComponents = NULL;
VideoProfileLevel *pProfileLevel = NULL;
pDecoder = decoders->decoder;
for (size_t i = 0; i< decoders->decoderNumber; i++) {
ALOGV("Supported Codec[%d] :%d", i, pDecoder->codec);
pOmxComponents = pDecoder->component;
for(size_t j = 0; j < pDecoder->componentNumber; j++) {
pProfileLevel = pOmxComponents->profileLevel;
ALOGV("-->component %d", j);
for(size_t k = 0; k < pOmxComponents->profileNumber; k++) {
ALOGV("-->profile:%ld maxLevel:%ld", pProfileLevel->mProfile,
pProfileLevel->mLevel);
pProfileLevel++;
}
pOmxComponents++;
}
pDecoder++;
}
}
M4OSA_ERR queryVideoDecoderCapabilities
(M4DECODER_VideoDecoders** decoders) {
M4OSA_ERR err = M4NO_ERROR;
const char *kMimeTypes[] = {
MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
MEDIA_MIMETYPE_VIDEO_H263
};
int32_t supportFormats = sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
M4DECODER_VideoDecoders *pDecoders;
VideoDecoder *pDecoder;
VideoComponentCapabilities *pOmxComponents = NULL;
VideoProfileLevel *pProfileLevel = NULL;
OMXClient client;
status_t status = OK;
SAFE_MALLOC(pDecoders, M4DECODER_VideoDecoders, 1, "VideoDecoders");
SAFE_MALLOC(pDecoder, VideoDecoder, supportFormats,
"VideoDecoder");
pDecoders->decoder = pDecoder;
pDecoders->decoderNumber= supportFormats;
status = client.connect();
CHECK(status == OK);
for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
++k) {
Vector<CodecCapabilities> results;
CHECK_EQ(QueryCodecs(client.interface(), kMimeTypes[k],
true, // queryDecoders
&results), (status_t)OK);
if (results.size()) {
SAFE_MALLOC(pOmxComponents, VideoComponentCapabilities,
results.size(), "VideoComponentCapabilities");
ALOGV("K=%d",k);
pDecoder->component = pOmxComponents;
pDecoder->componentNumber = results.size();
}
for (size_t i = 0; i < results.size(); ++i) {
ALOGV(" decoder '%s' supports ",
results[i].mComponentName.string());
if (results[i].mProfileLevels.size() == 0) {
ALOGV("NOTHING.\n");
continue;
}
#if 0
// FIXME:
// We should ignore the software codecs and make IsSoftwareCodec()
// part of pubic API from OMXCodec.cpp
if (IsSoftwareCodec(results[i].mComponentName.string())) {
ALOGV("Ignore software codec %s", results[i].mComponentName.string());
continue;
}
#endif
// Count the supported profiles
int32_t profileNumber = 0;
int32_t profile = -1;
for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
const CodecProfileLevel &profileLevel =
results[i].mProfileLevels[j];
// FIXME: assume that the profiles are ordered
if (profileLevel.mProfile != profile) {
profile = profileLevel.mProfile;
profileNumber++;
}
}
SAFE_MALLOC(pProfileLevel, VideoProfileLevel,
profileNumber, "VideoProfileLevel");
pOmxComponents->profileLevel = pProfileLevel;
pOmxComponents->profileNumber = profileNumber;
// Get the max Level for each profile.
int32_t maxLevel = -1;
profile = -1;
profileNumber = 0;
for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
const CodecProfileLevel &profileLevel =
results[i].mProfileLevels[j];
if (profile == -1 && maxLevel == -1) {
profile = profileLevel.mProfile;
maxLevel = profileLevel.mLevel;
pProfileLevel->mProfile = profile;
pProfileLevel->mLevel = maxLevel;
ALOGV("%d profile: %ld, max level: %ld",
__LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel);
}
if (profileLevel.mProfile != profile) {
profile = profileLevel.mProfile;
maxLevel = profileLevel.mLevel;
profileNumber++;
pProfileLevel++;
pProfileLevel->mProfile = profile;
pProfileLevel->mLevel = maxLevel;
ALOGV("%d profile: %ld, max level: %ld",
__LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel);
} else if (profileLevel.mLevel > maxLevel) {
maxLevel = profileLevel.mLevel;
pProfileLevel->mLevel = maxLevel;
ALOGV("%d profile: %ld, max level: %ld",
__LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel);
}
}
pOmxComponents++;
}
if (!strcmp(MEDIA_MIMETYPE_VIDEO_AVC, kMimeTypes[k]))
pDecoder->codec = M4DA_StreamTypeVideoMpeg4Avc;
if (!strcmp(MEDIA_MIMETYPE_VIDEO_MPEG4, kMimeTypes[k]))
pDecoder->codec = M4DA_StreamTypeVideoMpeg4;
if (!strcmp(MEDIA_MIMETYPE_VIDEO_H263, kMimeTypes[k]))
pDecoder->codec = M4DA_StreamTypeVideoH263;
pDecoder++;
}
logSupportDecodersAndCapabilities(pDecoders);
*decoders = pDecoders;
cleanUp:
return err;
}
/********************
* ENGINE INTERFACE *
********************/
M4OSA_ERR VideoEditorVideoDecoder_configureFromMetadata(M4OSA_Context pContext,
MetaData* meta) {
M4OSA_ERR err = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext = M4OSA_NULL;
bool success = OK;
int32_t width = 0;
int32_t height = 0;
int32_t frameSize = 0;
int32_t vWidth, vHeight;
int32_t cropLeft, cropTop, cropRight, cropBottom;
VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != meta, M4ERR_PARAMETER);
ALOGV("VideoEditorVideoDecoder_configureFromMetadata begin");
pDecShellContext = (VideoEditorVideoDecoder_Context*)pContext;
success = meta->findInt32(kKeyWidth, &vWidth);
VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER);
success = meta->findInt32(kKeyHeight, &vHeight);
VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER);
ALOGV("vWidth = %d, vHeight = %d", vWidth, vHeight);
pDecShellContext->mGivenWidth = vWidth;
pDecShellContext->mGivenHeight = vHeight;
if (!meta->findRect(
kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
cropLeft = cropTop = 0;
cropRight = vWidth - 1;
cropBottom = vHeight - 1;
ALOGV("got dimensions only %d x %d", width, height);
} else {
ALOGV("got crop rect %d, %d, %d, %d",
cropLeft, cropTop, cropRight, cropBottom);
}
pDecShellContext->mCropRect.left = cropLeft;
pDecShellContext->mCropRect.right = cropRight;
pDecShellContext->mCropRect.top = cropTop;
pDecShellContext->mCropRect.bottom = cropBottom;
width = cropRight - cropLeft + 1;
height = cropBottom - cropTop + 1;
ALOGV("VideoDecoder_configureFromMetadata : W=%d H=%d", width, height);
VIDEOEDITOR_CHECK((0 != width) && (0 != height), M4ERR_PARAMETER);
if( (M4OSA_NULL != pDecShellContext->m_pDecBufferPool) &&
(pDecShellContext->m_pVideoStreamhandler->m_videoWidth == \
(uint32_t)width) &&
(pDecShellContext->m_pVideoStreamhandler->m_videoHeight == \
(uint32_t)height) ) {
// No need to reconfigure
goto cleanUp;
}
ALOGV("VideoDecoder_configureFromMetadata reset: W=%d H=%d", width, height);
// Update the stream handler parameters
pDecShellContext->m_pVideoStreamhandler->m_videoWidth = width;
pDecShellContext->m_pVideoStreamhandler->m_videoHeight = height;
frameSize = (width * height * 3) / 2;
// Configure the buffer pool
if( M4OSA_NULL != pDecShellContext->m_pDecBufferPool ) {
ALOGV("VideoDecoder_configureFromMetadata : reset the buffer pool");
VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool);
pDecShellContext->m_pDecBufferPool = M4OSA_NULL;
}
err = VIDEOEDITOR_BUFFER_allocatePool(&pDecShellContext->m_pDecBufferPool,
MAX_DEC_BUFFERS, (M4OSA_Char*)"VIDEOEDITOR_DecodedBufferPool");
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
err = VIDEOEDITOR_BUFFER_initPoolBuffers(pDecShellContext->m_pDecBufferPool,
frameSize + pDecShellContext->mGivenWidth * 2);
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
cleanUp:
if( M4NO_ERROR == err ) {
ALOGV("VideoEditorVideoDecoder_configureFromMetadata no error");
} else {
if( (M4OSA_NULL != pDecShellContext) && \
(M4OSA_NULL != pDecShellContext->m_pDecBufferPool) ) {
VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool);
pDecShellContext->m_pDecBufferPool = M4OSA_NULL;
}
ALOGV("VideoEditorVideoDecoder_configureFromMetadata ERROR 0x%X", err);
}
ALOGV("VideoEditorVideoDecoder_configureFromMetadata end");
return err;
}
M4OSA_ERR VideoEditorVideoDecoder_destroy(M4OSA_Context pContext) {
M4OSA_ERR err = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext =
(VideoEditorVideoDecoder_Context*)pContext;
// Input parameters check
ALOGV("VideoEditorVideoDecoder_destroy begin");
VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER);
// Release the color converter
delete pDecShellContext->mI420ColorConverter;
// Destroy the graph
if( pDecShellContext->mVideoDecoder != NULL ) {
ALOGV("### VideoEditorVideoDecoder_destroy : releasing decoder");
pDecShellContext->mVideoDecoder->stop();
pDecShellContext->mVideoDecoder.clear();
}
pDecShellContext->mClient.disconnect();
pDecShellContext->mReaderSource.clear();
// Release memory
if( pDecShellContext->m_pDecBufferPool != M4OSA_NULL ) {
VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool);
pDecShellContext->m_pDecBufferPool = M4OSA_NULL;
}
SAFE_FREE(pDecShellContext);
pContext = NULL;
cleanUp:
if( M4NO_ERROR == err ) {
ALOGV("VideoEditorVideoDecoder_destroy no error");
} else {
ALOGV("VideoEditorVideoDecoder_destroy ERROR 0x%X", err);
}
ALOGV("VideoEditorVideoDecoder_destroy end");
return err;
}
M4OSA_ERR VideoEditorVideoDecoder_create(M4OSA_Context *pContext,
M4_StreamHandler *pStreamHandler,
M4READER_GlobalInterface *pReaderGlobalInterface,
M4READER_DataInterface *pReaderDataInterface,
M4_AccessUnit *pAccessUnit, M4OSA_Void *pUserData) {
M4OSA_ERR err = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext = M4OSA_NULL;
status_t status = OK;
bool success = TRUE;
int32_t colorFormat = 0;
M4OSA_UInt32 size = 0;
sp<MetaData> decoderMetadata = NULL;
int decoderOutput = OMX_COLOR_FormatYUV420Planar;
ALOGV("VideoEditorVideoDecoder_create begin");
// Input parameters check
VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pStreamHandler, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pReaderDataInterface, M4ERR_PARAMETER);
// Context allocation & initialization
SAFE_MALLOC(pDecShellContext, VideoEditorVideoDecoder_Context, 1,
"VideoEditorVideoDecoder");
pDecShellContext->m_pVideoStreamhandler =
(M4_VideoStreamHandler*)pStreamHandler;
pDecShellContext->m_pNextAccessUnitToDecode = pAccessUnit;
pDecShellContext->m_pReaderGlobal = pReaderGlobalInterface;
pDecShellContext->m_pReader = pReaderDataInterface;
pDecShellContext->m_lastDecodedCTS = -1;
pDecShellContext->m_lastRenderCts = -1;
switch( pStreamHandler->m_streamType ) {
case M4DA_StreamTypeVideoH263:
pDecShellContext->mDecoderType = VIDEOEDITOR_kH263VideoDec;
break;
case M4DA_StreamTypeVideoMpeg4:
pDecShellContext->mDecoderType = VIDEOEDITOR_kMpeg4VideoDec;
// Parse the VOL header
err = VideoEditorVideoDecoder_internalParseVideoDSI(
(M4OSA_UInt8*)pDecShellContext->m_pVideoStreamhandler->\
m_basicProperties.m_pDecoderSpecificInfo,
pDecShellContext->m_pVideoStreamhandler->\
m_basicProperties.m_decoderSpecificInfoSize,
&pDecShellContext->m_Dci, &pDecShellContext->m_VideoSize);
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
break;
case M4DA_StreamTypeVideoMpeg4Avc:
pDecShellContext->mDecoderType = VIDEOEDITOR_kH264VideoDec;
break;
default:
VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type",
M4ERR_PARAMETER);
break;
}
pDecShellContext->mNbInputFrames = 0;
pDecShellContext->mFirstInputCts = -1.0;
pDecShellContext->mLastInputCts = -1.0;
pDecShellContext->mNbRenderedFrames = 0;
pDecShellContext->mFirstRenderedCts = -1.0;
pDecShellContext->mLastRenderedCts = -1.0;
pDecShellContext->mNbOutputFrames = 0;
pDecShellContext->mFirstOutputCts = -1;
pDecShellContext->mLastOutputCts = -1;
pDecShellContext->m_pDecBufferPool = M4OSA_NULL;
// Calculate the interval between two video frames.
CHECK(pDecShellContext->m_pVideoStreamhandler->m_averageFrameRate > 0);
pDecShellContext->mFrameIntervalMs =
1000.0 / pDecShellContext->m_pVideoStreamhandler->m_averageFrameRate;
/**
* StageFright graph building
*/
decoderMetadata = new MetaData;
switch( pDecShellContext->mDecoderType ) {
case VIDEOEDITOR_kH263VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
break;
case VIDEOEDITOR_kMpeg4VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
decoderMetadata->setData(kKeyESDS, kTypeESDS,
pStreamHandler->m_pESDSInfo,
pStreamHandler->m_ESDSInfoSize);
break;
case VIDEOEDITOR_kH264VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
decoderMetadata->setData(kKeyAVCC, kTypeAVCC,
pStreamHandler->m_pH264DecoderSpecificInfo,
pStreamHandler->m_H264decoderSpecificInfoSize);
break;
default:
VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type",
M4ERR_PARAMETER);
break;
}
decoderMetadata->setInt32(kKeyMaxInputSize, pStreamHandler->m_maxAUSize);
decoderMetadata->setInt32(kKeyWidth,
pDecShellContext->m_pVideoStreamhandler->m_videoWidth);
decoderMetadata->setInt32(kKeyHeight,
pDecShellContext->m_pVideoStreamhandler->m_videoHeight);
// Create the decoder source
pDecShellContext->mReaderSource = new VideoEditorVideoDecoderSource(
decoderMetadata, pDecShellContext->mDecoderType,
(void *)pDecShellContext);
VIDEOEDITOR_CHECK(NULL != pDecShellContext->mReaderSource.get(),
M4ERR_SF_DECODER_RSRC_FAIL);
// Connect to the OMX client
status = pDecShellContext->mClient.connect();
VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL);
// Create the decoder
pDecShellContext->mVideoDecoder = OMXCodec::Create(
pDecShellContext->mClient.interface(),
decoderMetadata, false, pDecShellContext->mReaderSource);
VIDEOEDITOR_CHECK(NULL != pDecShellContext->mVideoDecoder.get(),
M4ERR_SF_DECODER_RSRC_FAIL);
// Get the output color format
success = pDecShellContext->mVideoDecoder->getFormat()->findInt32(
kKeyColorFormat, &colorFormat);
VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER);
pDecShellContext->decOuputColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat;
pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyWidth,
pDecShellContext->m_pVideoStreamhandler->m_videoWidth);
pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyHeight,
pDecShellContext->m_pVideoStreamhandler->m_videoHeight);
// Get the color converter
pDecShellContext->mI420ColorConverter = new I420ColorConverter;
if (pDecShellContext->mI420ColorConverter->isLoaded()) {
decoderOutput = pDecShellContext->mI420ColorConverter->getDecoderOutputFormat();
}
if (decoderOutput == OMX_COLOR_FormatYUV420Planar) {
delete pDecShellContext->mI420ColorConverter;
pDecShellContext->mI420ColorConverter = NULL;
}
ALOGI("decoder output format = 0x%X\n", decoderOutput);
// Configure the buffer pool from the metadata
err = VideoEditorVideoDecoder_configureFromMetadata(pDecShellContext,
pDecShellContext->mVideoDecoder->getFormat().get());
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
// Start the graph
status = pDecShellContext->mVideoDecoder->start();
VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL);
*pContext = (M4OSA_Context)pDecShellContext;
cleanUp:
if( M4NO_ERROR == err ) {
ALOGV("VideoEditorVideoDecoder_create no error");
} else {
VideoEditorVideoDecoder_destroy(pDecShellContext);
*pContext = M4OSA_NULL;
ALOGV("VideoEditorVideoDecoder_create ERROR 0x%X", err);
}
ALOGV("VideoEditorVideoDecoder_create : DONE");
return err;
}
M4OSA_ERR VideoEditorVideoSoftwareDecoder_create(M4OSA_Context *pContext,
M4_StreamHandler *pStreamHandler,
M4READER_GlobalInterface *pReaderGlobalInterface,
M4READER_DataInterface *pReaderDataInterface,
M4_AccessUnit *pAccessUnit, M4OSA_Void *pUserData) {
M4OSA_ERR err = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext = M4OSA_NULL;
status_t status = OK;
bool success = TRUE;
int32_t colorFormat = 0;
M4OSA_UInt32 size = 0;
sp<MetaData> decoderMetadata = NULL;
ALOGV("VideoEditorVideoDecoder_create begin");
// Input parameters check
VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pStreamHandler, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pReaderDataInterface, M4ERR_PARAMETER);
// Context allocation & initialization
SAFE_MALLOC(pDecShellContext, VideoEditorVideoDecoder_Context, 1,
"VideoEditorVideoDecoder");
pDecShellContext->m_pVideoStreamhandler =
(M4_VideoStreamHandler*)pStreamHandler;
pDecShellContext->m_pNextAccessUnitToDecode = pAccessUnit;
pDecShellContext->m_pReaderGlobal = pReaderGlobalInterface;
pDecShellContext->m_pReader = pReaderDataInterface;
pDecShellContext->m_lastDecodedCTS = -1;
pDecShellContext->m_lastRenderCts = -1;
switch( pStreamHandler->m_streamType ) {
case M4DA_StreamTypeVideoH263:
pDecShellContext->mDecoderType = VIDEOEDITOR_kH263VideoDec;
break;
case M4DA_StreamTypeVideoMpeg4:
pDecShellContext->mDecoderType = VIDEOEDITOR_kMpeg4VideoDec;
// Parse the VOL header
err = VideoEditorVideoDecoder_internalParseVideoDSI(
(M4OSA_UInt8*)pDecShellContext->m_pVideoStreamhandler->\
m_basicProperties.m_pDecoderSpecificInfo,
pDecShellContext->m_pVideoStreamhandler->\
m_basicProperties.m_decoderSpecificInfoSize,
&pDecShellContext->m_Dci, &pDecShellContext->m_VideoSize);
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
break;
case M4DA_StreamTypeVideoMpeg4Avc:
pDecShellContext->mDecoderType = VIDEOEDITOR_kH264VideoDec;
break;
default:
VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type",
M4ERR_PARAMETER);
break;
}
pDecShellContext->mNbInputFrames = 0;
pDecShellContext->mFirstInputCts = -1.0;
pDecShellContext->mLastInputCts = -1.0;
pDecShellContext->mNbRenderedFrames = 0;
pDecShellContext->mFirstRenderedCts = -1.0;
pDecShellContext->mLastRenderedCts = -1.0;
pDecShellContext->mNbOutputFrames = 0;
pDecShellContext->mFirstOutputCts = -1;
pDecShellContext->mLastOutputCts = -1;
pDecShellContext->m_pDecBufferPool = M4OSA_NULL;
/**
* StageFright graph building
*/
decoderMetadata = new MetaData;
switch( pDecShellContext->mDecoderType ) {
case VIDEOEDITOR_kH263VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
break;
case VIDEOEDITOR_kMpeg4VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
decoderMetadata->setData(kKeyESDS, kTypeESDS,
pStreamHandler->m_pESDSInfo,
pStreamHandler->m_ESDSInfoSize);
break;
case VIDEOEDITOR_kH264VideoDec:
decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
decoderMetadata->setData(kKeyAVCC, kTypeAVCC,
pStreamHandler->m_pH264DecoderSpecificInfo,
pStreamHandler->m_H264decoderSpecificInfoSize);
break;
default:
VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type",
M4ERR_PARAMETER);
break;
}
decoderMetadata->setInt32(kKeyMaxInputSize, pStreamHandler->m_maxAUSize);
decoderMetadata->setInt32(kKeyWidth,
pDecShellContext->m_pVideoStreamhandler->m_videoWidth);
decoderMetadata->setInt32(kKeyHeight,
pDecShellContext->m_pVideoStreamhandler->m_videoHeight);
// Create the decoder source
pDecShellContext->mReaderSource = new VideoEditorVideoDecoderSource(
decoderMetadata, pDecShellContext->mDecoderType,
(void *)pDecShellContext);
VIDEOEDITOR_CHECK(NULL != pDecShellContext->mReaderSource.get(),
M4ERR_SF_DECODER_RSRC_FAIL);
// Connect to the OMX client
status = pDecShellContext->mClient.connect();
VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL);
ALOGI("Using software codecs only");
// Create the decoder
pDecShellContext->mVideoDecoder = OMXCodec::Create(
pDecShellContext->mClient.interface(),
decoderMetadata, false, pDecShellContext->mReaderSource,NULL,OMXCodec::kSoftwareCodecsOnly);
VIDEOEDITOR_CHECK(NULL != pDecShellContext->mVideoDecoder.get(),
M4ERR_SF_DECODER_RSRC_FAIL);
// Get the output color format
success = pDecShellContext->mVideoDecoder->getFormat()->findInt32(
kKeyColorFormat, &colorFormat);
VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER);
pDecShellContext->decOuputColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat;
pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyWidth,
pDecShellContext->m_pVideoStreamhandler->m_videoWidth);
pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyHeight,
pDecShellContext->m_pVideoStreamhandler->m_videoHeight);
// Configure the buffer pool from the metadata
err = VideoEditorVideoDecoder_configureFromMetadata(pDecShellContext,
pDecShellContext->mVideoDecoder->getFormat().get());
VIDEOEDITOR_CHECK(M4NO_ERROR == err, err);
// Start the graph
status = pDecShellContext->mVideoDecoder->start();
VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL);
*pContext = (M4OSA_Context)pDecShellContext;
cleanUp:
if( M4NO_ERROR == err ) {
ALOGV("VideoEditorVideoDecoder_create no error");
} else {
VideoEditorVideoDecoder_destroy(pDecShellContext);
*pContext = M4OSA_NULL;
ALOGV("VideoEditorVideoDecoder_create ERROR 0x%X", err);
}
ALOGV("VideoEditorVideoDecoder_create : DONE");
return err;
}
M4OSA_ERR VideoEditorVideoDecoder_getOption(M4OSA_Context context,
M4OSA_OptionID optionId, M4OSA_DataOption pValue) {
M4OSA_ERR lerr = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext =
(VideoEditorVideoDecoder_Context*) context;
M4_VersionInfo* pVersionInfo;
M4DECODER_VideoSize* pVideoSize;
M4OSA_UInt32* pNextFrameCts;
M4OSA_UInt32 *plastDecodedFrameCts;
M4DECODER_AVCProfileLevel* profile;
M4DECODER_MPEG4_DecoderConfigInfo* pDecConfInfo;
ALOGV("VideoEditorVideoDecoder_getOption begin");
switch (optionId) {
case M4DECODER_kOptionID_AVCLastDecodedFrameCTS:
plastDecodedFrameCts = (M4OSA_UInt32 *) pValue;
*plastDecodedFrameCts = pDecShellContext->m_lastDecodedCTS;
break;
case M4DECODER_kOptionID_Version:
pVersionInfo = (M4_VersionInfo*)pValue;
pVersionInfo->m_major = VIDEOEDITOR_VIDEC_SHELL_VER_MAJOR;
pVersionInfo->m_minor= VIDEOEDITOR_VIDEC_SHELL_VER_MINOR;
pVersionInfo->m_revision = VIDEOEDITOR_VIDEC_SHELL_VER_REVISION;
pVersionInfo->m_structSize=sizeof(M4_VersionInfo);
break;
case M4DECODER_kOptionID_VideoSize:
/** Only VPS uses this Option ID. */
pVideoSize = (M4DECODER_VideoSize*)pValue;
pDecShellContext->mVideoDecoder->getFormat()->findInt32(kKeyWidth,
(int32_t*)(&pVideoSize->m_uiWidth));
pDecShellContext->mVideoDecoder->getFormat()->findInt32(kKeyHeight,
(int32_t*)(&pVideoSize->m_uiHeight));
ALOGV("VideoEditorVideoDecoder_getOption : W=%d H=%d",
pVideoSize->m_uiWidth, pVideoSize->m_uiHeight);
break;
case M4DECODER_kOptionID_NextRenderedFrameCTS:
/** How to get this information. SF decoder does not provide this. *
** Let us provide last decoded frame CTS as of now. *
** Only VPS uses this Option ID. */
pNextFrameCts = (M4OSA_UInt32 *)pValue;
*pNextFrameCts = pDecShellContext->m_lastDecodedCTS;
break;
case M4DECODER_MPEG4_kOptionID_DecoderConfigInfo:
if(pDecShellContext->mDecoderType == VIDEOEDITOR_kMpeg4VideoDec) {
(*(M4DECODER_MPEG4_DecoderConfigInfo*)pValue) =
pDecShellContext->m_Dci;
}
break;
default:
lerr = M4ERR_BAD_OPTION_ID;
break;
}
ALOGV("VideoEditorVideoDecoder_getOption: end with err = 0x%x", lerr);
return lerr;
}
M4OSA_ERR VideoEditorVideoDecoder_setOption(M4OSA_Context context,
M4OSA_OptionID optionId, M4OSA_DataOption pValue) {
M4OSA_ERR lerr = M4NO_ERROR;
VideoEditorVideoDecoder_Context *pDecShellContext =
(VideoEditorVideoDecoder_Context*) context;
ALOGV("VideoEditorVideoDecoder_setOption begin");
switch (optionId) {
case M4DECODER_kOptionID_OutputFilter: {
M4DECODER_OutputFilter* pOutputFilter =
(M4DECODER_OutputFilter*) pValue;
pDecShellContext->m_pFilter =
(M4VIFI_PlanConverterFunctionType*)pOutputFilter->\
m_pFilterFunction;
pDecShellContext->m_pFilterUserData =
pOutputFilter->m_pFilterUserData;
}
break;
case M4DECODER_kOptionID_DeblockingFilter:
break;
default:
lerr = M4ERR_BAD_CONTEXT;
break;
}
ALOGV("VideoEditorVideoDecoder_setOption: end with err = 0x%x", lerr);
return lerr;
}
M4OSA_ERR VideoEditorVideoDecoder_decode(M4OSA_Context context,
M4_MediaTime* pTime, M4OSA_Bool bJump, M4OSA_UInt32 tolerance) {
M4OSA_ERR lerr = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext =
(VideoEditorVideoDecoder_Context*) context;
int64_t lFrameTime;
MediaBuffer* pDecoderBuffer = NULL;
MediaBuffer* pNextBuffer = NULL;
status_t errStatus;
bool needSeek = bJump;
ALOGV("VideoEditorVideoDecoder_decode begin");
if( M4OSA_TRUE == pDecShellContext->mReachedEOS ) {
// Do not call read(), it could lead to a freeze
ALOGV("VideoEditorVideoDecoder_decode : EOS already reached");
lerr = M4WAR_NO_MORE_AU;
goto VIDEOEDITOR_VideoDecode_cleanUP;
}
if(pDecShellContext->m_lastDecodedCTS >= *pTime) {
ALOGV("VideoDecoder_decode: Already decoded up to this time CTS = %lf.",
pDecShellContext->m_lastDecodedCTS);
goto VIDEOEDITOR_VideoDecode_cleanUP;
}
if(M4OSA_TRUE == bJump) {
ALOGV("VideoEditorVideoDecoder_decode: Jump called");
pDecShellContext->m_lastDecodedCTS = -1;
pDecShellContext->m_lastRenderCts = -1;
}
pDecShellContext->mNbInputFrames++;
if (0 > pDecShellContext->mFirstInputCts){
pDecShellContext->mFirstInputCts = *pTime;
}
pDecShellContext->mLastInputCts = *pTime;
while (pDecoderBuffer == NULL || pDecShellContext->m_lastDecodedCTS + tolerance < *pTime) {
ALOGV("VideoEditorVideoDecoder_decode, frameCTS = %lf, DecodeUpTo = %lf",
pDecShellContext->m_lastDecodedCTS, *pTime);
// Read the buffer from the stagefright decoder
if (needSeek) {
MediaSource::ReadOptions options;
int64_t time_us = *pTime * 1000;
options.setSeekTo(time_us, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
errStatus = pDecShellContext->mVideoDecoder->read(&pNextBuffer, &options);
needSeek = false;
} else {
errStatus = pDecShellContext->mVideoDecoder->read(&pNextBuffer);
}
// Handle EOS and format change
if (errStatus == ERROR_END_OF_STREAM) {
ALOGV("End of stream reached, returning M4WAR_NO_MORE_AU ");
pDecShellContext->mReachedEOS = M4OSA_TRUE;
lerr = M4WAR_NO_MORE_AU;
// If we decoded a buffer before EOS, we still need to put it
// into the queue.
if (pDecoderBuffer && bJump) {
copyBufferToQueue(pDecShellContext, pDecoderBuffer);
}
goto VIDEOEDITOR_VideoDecode_cleanUP;
} else if (INFO_FORMAT_CHANGED == errStatus) {
ALOGV("VideoDecoder_decode : source returns INFO_FORMAT_CHANGED");
lerr = VideoEditorVideoDecoder_configureFromMetadata(
pDecShellContext,
pDecShellContext->mVideoDecoder->getFormat().get());
if( M4NO_ERROR != lerr ) {
ALOGV("!!! VideoEditorVideoDecoder_decode ERROR : "
"VideoDecoder_configureFromMetadata returns 0x%X", lerr);
break;
}
continue;
} else if (errStatus != OK) {
ALOGE("VideoEditorVideoDecoder_decode ERROR:0x%x(%d)",
errStatus,errStatus);
lerr = errStatus;
goto VIDEOEDITOR_VideoDecode_cleanUP;
}
// The OMXCodec client should expect to receive 0-length buffers
// and drop the 0-length buffers.
if (pNextBuffer->range_length() == 0) {
pNextBuffer->release();
continue;
}
// Now we have a good next buffer, release the previous one.
if (pDecoderBuffer != NULL) {
pDecoderBuffer->release();
pDecoderBuffer = NULL;
}
pDecoderBuffer = pNextBuffer;
// Record the timestamp of last decoded buffer
pDecoderBuffer->meta_data()->findInt64(kKeyTime, &lFrameTime);
pDecShellContext->m_lastDecodedCTS = (M4_MediaTime)(lFrameTime/1000);
ALOGV("VideoEditorVideoDecoder_decode,decoded frametime = %lf,size = %d",
(M4_MediaTime)lFrameTime, pDecoderBuffer->size() );
/*
* We need to save a buffer if bJump == false to a queue. These
* buffers have a timestamp >= the target time, *pTime (for instance,
* the transition between two videos, or a trimming postion inside
* one video), since they are part of the transition clip or the
* trimmed video.
*
* If *pTime does not have the same value as any of the existing
* video frames, we would like to get the buffer right before *pTime
* and in the transcoding phrase, this video frame will be encoded
* as a key frame and becomes the first video frame for the transition or the
* trimmed video to be generated. This buffer must also be queued.
*
*/
int64_t targetTimeMs =
pDecShellContext->m_lastDecodedCTS +
pDecShellContext->mFrameIntervalMs +
tolerance;
if (!bJump || targetTimeMs > *pTime) {
lerr = copyBufferToQueue(pDecShellContext, pDecoderBuffer);
if (lerr != M4NO_ERROR) {
goto VIDEOEDITOR_VideoDecode_cleanUP;
}
}
}
pDecShellContext->mNbOutputFrames++;
if ( 0 > pDecShellContext->mFirstOutputCts ) {
pDecShellContext->mFirstOutputCts = *pTime;
}
pDecShellContext->mLastOutputCts = *pTime;
VIDEOEDITOR_VideoDecode_cleanUP:
*pTime = pDecShellContext->m_lastDecodedCTS;
if (pDecoderBuffer != NULL) {
pDecoderBuffer->release();
pDecoderBuffer = NULL;
}
ALOGV("VideoEditorVideoDecoder_decode: end with 0x%x", lerr);
return lerr;
}
static M4OSA_ERR copyBufferToQueue(
VideoEditorVideoDecoder_Context* pDecShellContext,
MediaBuffer* pDecoderBuffer) {
M4OSA_ERR lerr = M4NO_ERROR;
VIDEOEDITOR_BUFFER_Buffer* tmpDecBuffer;
// Get a buffer from the queue
lerr = VIDEOEDITOR_BUFFER_getBuffer(pDecShellContext->m_pDecBufferPool,
VIDEOEDITOR_BUFFER_kEmpty, &tmpDecBuffer);
if (lerr == (M4OSA_UInt32)M4ERR_NO_BUFFER_AVAILABLE) {
lerr = VIDEOEDITOR_BUFFER_getOldestBuffer(
pDecShellContext->m_pDecBufferPool,
VIDEOEDITOR_BUFFER_kFilled, &tmpDecBuffer);
tmpDecBuffer->state = VIDEOEDITOR_BUFFER_kEmpty;
lerr = M4NO_ERROR;
}
if (lerr != M4NO_ERROR) return lerr;
// Color convert or copy from the given MediaBuffer to our buffer
if (pDecShellContext->mI420ColorConverter) {
if (pDecShellContext->mI420ColorConverter->convertDecoderOutputToI420(
(uint8_t *)pDecoderBuffer->data(),// ?? + pDecoderBuffer->range_offset(), // decoderBits
pDecShellContext->mGivenWidth, // decoderWidth
pDecShellContext->mGivenHeight, // decoderHeight
pDecShellContext->mCropRect, // decoderRect
tmpDecBuffer->pData /* dstBits */) < 0) {
ALOGE("convertDecoderOutputToI420 failed");
lerr = M4ERR_NOT_IMPLEMENTED;
}
} else if (pDecShellContext->decOuputColorFormat == OMX_COLOR_FormatYUV420Planar) {
int32_t width = pDecShellContext->m_pVideoStreamhandler->m_videoWidth;
int32_t height = pDecShellContext->m_pVideoStreamhandler->m_videoHeight;
int32_t yPlaneSize = width * height;
int32_t uvPlaneSize = width * height / 4;
int32_t offsetSrc = 0;
if (( width == pDecShellContext->mGivenWidth ) &&
( height == pDecShellContext->mGivenHeight ))
{
M4OSA_MemAddr8 pTmpBuff = (M4OSA_MemAddr8)pDecoderBuffer->data() + pDecoderBuffer->range_offset();
memcpy((void *)tmpDecBuffer->pData, (void *)pTmpBuff, yPlaneSize);
offsetSrc += pDecShellContext->mGivenWidth * pDecShellContext->mGivenHeight;
memcpy((void *)((M4OSA_MemAddr8)tmpDecBuffer->pData + yPlaneSize),
(void *)(pTmpBuff + offsetSrc), uvPlaneSize);
offsetSrc += (pDecShellContext->mGivenWidth >> 1) * (pDecShellContext->mGivenHeight >> 1);
memcpy((void *)((M4OSA_MemAddr8)tmpDecBuffer->pData + yPlaneSize + uvPlaneSize),
(void *)(pTmpBuff + offsetSrc), uvPlaneSize);
}
else
{
M4OSA_MemAddr8 pTmpBuff = (M4OSA_MemAddr8)pDecoderBuffer->data() + pDecoderBuffer->range_offset();
M4OSA_MemAddr8 pTmpBuffDst = (M4OSA_MemAddr8)tmpDecBuffer->pData;
int32_t index;
for ( index = 0; index < height; index++)
{
memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width);
pTmpBuffDst += width;
pTmpBuff += pDecShellContext->mGivenWidth;
}
pTmpBuff += (pDecShellContext->mGivenWidth * ( pDecShellContext->mGivenHeight - height));
for ( index = 0; index < height >> 1; index++)
{
memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width >> 1);
pTmpBuffDst += width >> 1;
pTmpBuff += pDecShellContext->mGivenWidth >> 1;
}
pTmpBuff += ((pDecShellContext->mGivenWidth * (pDecShellContext->mGivenHeight - height)) / 4);
for ( index = 0; index < height >> 1; index++)
{
memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width >> 1);
pTmpBuffDst += width >> 1;
pTmpBuff += pDecShellContext->mGivenWidth >> 1;
}
}
} else {
ALOGE("VideoDecoder_decode: unexpected color format 0x%X",
pDecShellContext->decOuputColorFormat);
lerr = M4ERR_PARAMETER;
}
tmpDecBuffer->buffCTS = pDecShellContext->m_lastDecodedCTS;
tmpDecBuffer->state = VIDEOEDITOR_BUFFER_kFilled;
tmpDecBuffer->size = pDecoderBuffer->size();
return lerr;
}
M4OSA_ERR VideoEditorVideoDecoder_render(M4OSA_Context context,
M4_MediaTime* pTime, M4VIFI_ImagePlane* pOutputPlane,
M4OSA_Bool bForceRender) {
M4OSA_ERR err = M4NO_ERROR;
VideoEditorVideoDecoder_Context* pDecShellContext =
(VideoEditorVideoDecoder_Context*) context;
M4OSA_UInt32 lindex, i;
M4OSA_UInt8* p_buf_src, *p_buf_dest;
M4VIFI_ImagePlane tmpPlaneIn, tmpPlaneOut;
VIDEOEDITOR_BUFFER_Buffer* pTmpVIDEOEDITORBuffer, *pRenderVIDEOEDITORBuffer
= M4OSA_NULL;
M4_MediaTime candidateTimeStamp = -1;
M4OSA_Bool bFound = M4OSA_FALSE;
ALOGV("VideoEditorVideoDecoder_render begin");
// Input parameters check
VIDEOEDITOR_CHECK(M4OSA_NULL != context, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pTime, M4ERR_PARAMETER);
VIDEOEDITOR_CHECK(M4OSA_NULL != pOutputPlane, M4ERR_PARAMETER);
// The output buffer is already allocated, just copy the data
if ( (*pTime <= pDecShellContext->m_lastRenderCts) &&
(M4OSA_FALSE == bForceRender) ) {
ALOGV("VIDEOEDITOR_VIDEO_render Frame in the past");
err = M4WAR_VIDEORENDERER_NO_NEW_FRAME;
goto cleanUp;
}
ALOGV("VideoDecoder_render: lastRendered time = %lf,requested render time = "
"%lf", pDecShellContext->m_lastRenderCts, *pTime);
/**
* Find the buffer appropriate for rendering. */
for (i=0; i < pDecShellContext->m_pDecBufferPool->NB; i++) {
pTmpVIDEOEDITORBuffer = &pDecShellContext->m_pDecBufferPool\
->pNXPBuffer[i];
if (pTmpVIDEOEDITORBuffer->state == VIDEOEDITOR_BUFFER_kFilled) {
/** Free all those buffers older than last rendered frame. */
if (pTmpVIDEOEDITORBuffer->buffCTS < pDecShellContext->\
m_lastRenderCts) {
pTmpVIDEOEDITORBuffer->state = VIDEOEDITOR_BUFFER_kEmpty;
}
/** Get the buffer with appropriate timestamp */
if ( (pTmpVIDEOEDITORBuffer->buffCTS >= pDecShellContext->\
m_lastRenderCts) &&
(pTmpVIDEOEDITORBuffer->buffCTS <= *pTime) &&
(pTmpVIDEOEDITORBuffer->buffCTS > candidateTimeStamp)) {
bFound = M4OSA_TRUE;
pRenderVIDEOEDITORBuffer = pTmpVIDEOEDITORBuffer;
candidateTimeStamp = pTmpVIDEOEDITORBuffer->buffCTS;
ALOGV("VideoDecoder_render: found a buffer with timestamp = %lf",
candidateTimeStamp);
}
}
}
if (M4OSA_FALSE == bFound) {
err = M4WAR_VIDEORENDERER_NO_NEW_FRAME;
goto cleanUp;
}
ALOGV("VideoEditorVideoDecoder_render 3 output %d %d %d %d",
pOutputPlane[0].u_width, pOutputPlane[0].u_height,
pOutputPlane[0].u_topleft, pOutputPlane[0].u_stride);
pDecShellContext->m_lastRenderCts = candidateTimeStamp;
if( M4OSA_NULL != pDecShellContext->m_pFilter ) {
// Filtering was requested
M4VIFI_ImagePlane tmpPlane[3];
// Prepare the output image for conversion
tmpPlane[0].u_width =
pDecShellContext->m_pVideoStreamhandler->m_videoWidth;
tmpPlane[0].u_height =
pDecShellContext->m_pVideoStreamhandler->m_videoHeight;
tmpPlane[0].u_topleft = 0;
tmpPlane[0].u_stride = tmpPlane[0].u_width;
tmpPlane[0].pac_data = (M4VIFI_UInt8*)pRenderVIDEOEDITORBuffer->pData;
tmpPlane[1].u_width = tmpPlane[0].u_width;
tmpPlane[1].u_height = tmpPlane[0].u_height/2;
tmpPlane[1].u_topleft = 0;
tmpPlane[1].u_stride = tmpPlane[0].u_stride;
tmpPlane[1].pac_data = tmpPlane[0].pac_data +
(tmpPlane[0].u_stride * tmpPlane[0].u_height);
ALOGV("VideoEditorVideoDecoder_render W = %d H = %d",
tmpPlane[0].u_width,tmpPlane[0].u_height);
pDecShellContext->m_pFilter(M4OSA_NULL, &tmpPlane[0], pOutputPlane);
} else {
// Just copy the YUV420P buffer
M4OSA_MemAddr8 tempBuffPtr =
(M4OSA_MemAddr8)pRenderVIDEOEDITORBuffer->pData;
M4OSA_UInt32 tempWidth =
pDecShellContext->m_pVideoStreamhandler->m_videoWidth;
M4OSA_UInt32 tempHeight =
pDecShellContext->m_pVideoStreamhandler->m_videoHeight;
memcpy((void *) pOutputPlane[0].pac_data, (void *)tempBuffPtr,
tempWidth * tempHeight);
tempBuffPtr += (tempWidth * tempHeight);
memcpy((void *) pOutputPlane[1].pac_data, (void *)tempBuffPtr,
tempWidth * tempHeight/2);
}
pDecShellContext->mNbRenderedFrames++;
if ( 0 > pDecShellContext->mFirstRenderedCts ) {
pDecShellContext->mFirstRenderedCts = *pTime;
}
pDecShellContext->mLastRenderedCts = *pTime;
cleanUp:
if( M4NO_ERROR == err ) {
*pTime = pDecShellContext->m_lastRenderCts;
ALOGV("VideoEditorVideoDecoder_render no error");
} else {
ALOGV("VideoEditorVideoDecoder_render ERROR 0x%X", err);
}
ALOGV("VideoEditorVideoDecoder_render end");
return err;
}
M4OSA_ERR VideoEditorVideoDecoder_getInterface(M4DECODER_VideoType decoderType,
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
M4DECODER_VideoInterface* pDecoderInterface = M4OSA_NULL;
pDecoderInterface = (M4DECODER_VideoInterface*)M4OSA_32bitAlignedMalloc(
sizeof(M4DECODER_VideoInterface), M4DECODER_EXTERNAL,
(M4OSA_Char*)"VideoEditorVideoDecoder_getInterface" );
if (M4OSA_NULL == pDecoderInterface) {
return M4ERR_ALLOC;
}
*pDecoderType = decoderType;
pDecoderInterface->m_pFctCreate = VideoEditorVideoDecoder_create;
pDecoderInterface->m_pFctDestroy = VideoEditorVideoDecoder_destroy;
pDecoderInterface->m_pFctGetOption = VideoEditorVideoDecoder_getOption;
pDecoderInterface->m_pFctSetOption = VideoEditorVideoDecoder_setOption;
pDecoderInterface->m_pFctDecode = VideoEditorVideoDecoder_decode;
pDecoderInterface->m_pFctRender = VideoEditorVideoDecoder_render;
*pDecInterface = (M4OSA_Context)pDecoderInterface;
return M4NO_ERROR;
}
M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_VideoType decoderType,
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
M4DECODER_VideoInterface* pDecoderInterface = M4OSA_NULL;
pDecoderInterface = (M4DECODER_VideoInterface*)M4OSA_32bitAlignedMalloc(
sizeof(M4DECODER_VideoInterface), M4DECODER_EXTERNAL,
(M4OSA_Char*)"VideoEditorVideoDecoder_getInterface" );
if (M4OSA_NULL == pDecoderInterface) {
return M4ERR_ALLOC;
}
*pDecoderType = decoderType;
pDecoderInterface->m_pFctCreate = VideoEditorVideoSoftwareDecoder_create;
pDecoderInterface->m_pFctDestroy = VideoEditorVideoDecoder_destroy;
pDecoderInterface->m_pFctGetOption = VideoEditorVideoDecoder_getOption;
pDecoderInterface->m_pFctSetOption = VideoEditorVideoDecoder_setOption;
pDecoderInterface->m_pFctDecode = VideoEditorVideoDecoder_decode;
pDecoderInterface->m_pFctRender = VideoEditorVideoDecoder_render;
*pDecInterface = (M4OSA_Context)pDecoderInterface;
return M4NO_ERROR;
}
extern "C" {
M4OSA_ERR VideoEditorVideoDecoder_getInterface_MPEG4(
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
return VideoEditorVideoDecoder_getInterface(M4DECODER_kVideoTypeMPEG4,
pDecoderType, pDecInterface);
}
M4OSA_ERR VideoEditorVideoDecoder_getInterface_H264(
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
return VideoEditorVideoDecoder_getInterface(M4DECODER_kVideoTypeAVC,
pDecoderType, pDecInterface);
}
M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface_MPEG4(
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
return VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_kVideoTypeMPEG4,
pDecoderType, pDecInterface);
}
M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface_H264(
M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) {
return VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_kVideoTypeAVC,
pDecoderType, pDecInterface);
}
M4OSA_ERR VideoEditorVideoDecoder_getVideoDecodersAndCapabilities(
M4DECODER_VideoDecoders** decoders) {
return queryVideoDecoderCapabilities(decoders);
}
} // extern "C"