blob: 299d82ae45a3ba84121c9e7d472b025817517d6d [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 VideoEditorUtils.cpp
* @brief StageFright shell Utilities
*************************************************************************
*/
#define LOG_NDEBUG 1
#define LOG_TAG "SF_utils"
#include "utils/Log.h"
#include "IntelVideoEditorUtils.h"
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
/* Android includes*/
#include <utils/Log.h>
#include <memory.h>
/*---------------------*/
/* DEBUG LEVEL SETUP */
/*---------------------*/
#define LOG1 ALOGE /*ERRORS Logging*/
#define LOG2 ALOGI /*WARNING Logging*/
#define LOG3 //ALOGV /*COMMENTS Logging*/
namespace android {
void displayMetaData(const sp<MetaData> meta) {
const char* charData;
int32_t int32Data;
int64_t int64Data;
uint32_t type;
const void* data;
void* ptr;
size_t size;
if (meta->findCString(kKeyMIMEType, &charData)) {
LOG1("displayMetaData kKeyMIMEType %s", charData);
}
if (meta->findInt32(kKeyWidth, &int32Data)) {
LOG1("displayMetaData kKeyWidth %d", int32Data);
}
if (meta->findInt32(kKeyHeight, &int32Data)) {
LOG1("displayMetaData kKeyHeight %d", int32Data);
}
if (meta->findInt32(kKeyIFramesInterval, &int32Data)) {
LOG1("displayMetaData kKeyIFramesInterval %d", int32Data);
}
if (meta->findInt32(kKeyStride, &int32Data)) {
LOG1("displayMetaData kKeyStride %d", int32Data);
}
if (meta->findInt32(kKeySliceHeight, &int32Data)) {
LOG1("displayMetaData kKeySliceHeight %d", int32Data);
}
if (meta->findInt32(kKeyChannelCount, &int32Data)) {
LOG1("displayMetaData kKeyChannelCount %d", int32Data);
}
if (meta->findInt32(kKeySampleRate, &int32Data)) {
LOG1("displayMetaData kKeySampleRate %d", int32Data);
}
if (meta->findInt32(kKeyBitRate, &int32Data)) {
LOG1("displayMetaData kKeyBitRate %d", int32Data);
}
if (meta->findData(kKeyESDS, &type, &data, &size)) {
LOG1("displayMetaData kKeyESDS type=%d size=%d", type, size);
}
if (meta->findData(kKeyAVCC, &type, &data, &size)) {
LOG1("displayMetaData kKeyAVCC data=0x%X type=%d size=%d",
*((unsigned int*)data), type, size);
}
if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
LOG1("displayMetaData kKeyVorbisInfo type=%d size=%d", type, size);
}
if (meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
LOG1("displayMetaData kKeyVorbisBooks type=%d size=%d", type, size);
}
if (meta->findInt32(kKeyWantsNALFragments, &int32Data)) {
LOG1("displayMetaData kKeyWantsNALFragments %d", int32Data);
}
if (meta->findInt32(kKeyIsSyncFrame, &int32Data)) {
LOG1("displayMetaData kKeyIsSyncFrame %d", int32Data);
}
if (meta->findInt32(kKeyIsCodecConfig, &int32Data)) {
LOG1("displayMetaData kKeyIsCodecConfig %d", int32Data);
}
if (meta->findInt64(kKeyTime, &int64Data)) {
LOG1("displayMetaData kKeyTime %lld", int64Data);
}
if (meta->findInt32(kKeyDuration, &int32Data)) {
LOG1("displayMetaData kKeyDuration %d", int32Data);
}
if (meta->findInt32(kKeyColorFormat, &int32Data)) {
LOG1("displayMetaData kKeyColorFormat %d", int32Data);
}
if (meta->findPointer(kKeyPlatformPrivate, &ptr)) {
LOG1("displayMetaData kKeyPlatformPrivate pointer=0x%x", (int32_t) ptr);
}
if (meta->findCString(kKeyDecoderComponent, &charData)) {
LOG1("displayMetaData kKeyDecoderComponent %s", charData);
}
if (meta->findInt32(kKeyBufferID, &int32Data)) {
LOG1("displayMetaData kKeyBufferID %d", int32Data);
}
if (meta->findInt32(kKeyMaxInputSize, &int32Data)) {
LOG1("displayMetaData kKeyMaxInputSize %d", int32Data);
}
if (meta->findInt64(kKeyThumbnailTime, &int64Data)) {
LOG1("displayMetaData kKeyThumbnailTime %lld", int64Data);
}
if (meta->findCString(kKeyAlbum, &charData)) {
LOG1("displayMetaData kKeyAlbum %s", charData);
}
if (meta->findCString(kKeyArtist, &charData)) {
LOG1("displayMetaData kKeyArtist %s", charData);
}
if (meta->findCString(kKeyAlbumArtist, &charData)) {
LOG1("displayMetaData kKeyAlbumArtist %s", charData);
}
if (meta->findCString(kKeyComposer, &charData)) {
LOG1("displayMetaData kKeyComposer %s", charData);
}
if (meta->findCString(kKeyGenre, &charData)) {
LOG1("displayMetaData kKeyGenre %s", charData);
}
if (meta->findCString(kKeyTitle, &charData)) {
LOG1("displayMetaData kKeyTitle %s", charData);
}
if (meta->findCString(kKeyYear, &charData)) {
LOG1("displayMetaData kKeyYear %s", charData);
}
if (meta->findData(kKeyAlbumArt, &type, &data, &size)) {
LOG1("displayMetaData kKeyAlbumArt type=%d size=%d", type, size);
}
if (meta->findCString(kKeyAlbumArtMIME, &charData)) {
LOG1("displayMetaData kKeyAlbumArtMIME %s", charData);
}
if (meta->findCString(kKeyAuthor, &charData)) {
LOG1("displayMetaData kKeyAuthor %s", charData);
}
if (meta->findCString(kKeyCDTrackNumber, &charData)) {
LOG1("displayMetaData kKeyCDTrackNumber %s", charData);
}
if (meta->findCString(kKeyDiscNumber, &charData)) {
LOG1("displayMetaData kKeyDiscNumber %s", charData);
}
if (meta->findCString(kKeyDate, &charData)) {
LOG1("displayMetaData kKeyDate %s", charData);
}
if (meta->findCString(kKeyWriter, &charData)) {
LOG1("displayMetaData kKeyWriter %s", charData);
}
if (meta->findInt32(kKeyTimeScale, &int32Data)) {
LOG1("displayMetaData kKeyTimeScale %d", int32Data);
}
if (meta->findInt32(kKeyVideoProfile, &int32Data)) {
LOG1("displayMetaData kKeyVideoProfile %d", int32Data);
}
if (meta->findInt32(kKeyVideoLevel, &int32Data)) {
LOG1("displayMetaData kKeyVideoLevel %d", int32Data);
}
if (meta->findInt32(kKey64BitFileOffset, &int32Data)) {
LOG1("displayMetaData kKey64BitFileOffset %d", int32Data);
}
if (meta->findInt32(kKeyFileType, &int32Data)) {
LOG1("displayMetaData kKeyFileType %d", int32Data);
}
if (meta->findInt64(kKeyTrackTimeStatus, &int64Data)) {
LOG1("displayMetaData kKeyTrackTimeStatus %lld", int64Data);
}
if (meta->findInt32(kKeyNotRealTime, &int32Data)) {
LOG1("displayMetaData kKeyNotRealTime %d", int32Data);
}
}
/**
* This code was extracted from StageFright MPEG4 writer
* Is is used to parse and format the AVC codec specific info received
* from StageFright encoders
*/
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;
struct AVCParamSet {
AVCParamSet(uint16_t length, const uint8_t *data)
: mLength(length), mData(data) {}
uint16_t mLength;
const uint8_t *mData;
};
struct AVCCodecSpecificContext {
List<AVCParamSet> mSeqParamSets;
List<AVCParamSet> mPicParamSets;
uint8_t mProfileIdc;
uint8_t mProfileCompatible;
uint8_t mLevelIdc;
};
const uint8_t *parseParamSet(AVCCodecSpecificContext* pC,
const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
CHECK(type == kNalUnitTypeSeqParamSet ||
type == kNalUnitTypePicParamSet);
size_t bytesLeft = length;
while (bytesLeft > 4 &&
memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
--bytesLeft;
}
if (bytesLeft <= 4) {
bytesLeft = 0; // Last parameter set
}
const uint8_t *nextStartCode = &data[length - bytesLeft];
*paramSetLen = nextStartCode - data;
if (*paramSetLen == 0) {
ALOGE("Param set is malformed, since its length is 0");
return NULL;
}
AVCParamSet paramSet(*paramSetLen, data);
if (type == kNalUnitTypeSeqParamSet) {
if (*paramSetLen < 4) {
ALOGE("Seq parameter set malformed");
return NULL;
}
if (pC->mSeqParamSets.empty()) {
pC->mProfileIdc = data[1];
pC->mProfileCompatible = data[2];
pC->mLevelIdc = data[3];
} else {
if (pC->mProfileIdc != data[1] ||
pC->mProfileCompatible != data[2] ||
pC->mLevelIdc != data[3]) {
ALOGV("Inconsistent profile/level found in seq parameter sets");
return NULL;
}
}
pC->mSeqParamSets.push_back(paramSet);
} else {
pC->mPicParamSets.push_back(paramSet);
}
return nextStartCode;
}
status_t buildAVCCodecSpecificData(uint8_t **pOutputData, size_t *pOutputSize,
const uint8_t *data, size_t size, MetaData *param)
{
//ALOGV("buildAVCCodecSpecificData");
if ( (pOutputData == NULL) || (pOutputSize == NULL) ) {
ALOGE("output is invalid");
return ERROR_MALFORMED;
}
if (*pOutputData != NULL) {
ALOGE("Already have codec specific data");
return ERROR_MALFORMED;
}
if (size < 4) {
ALOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
// Data is in the form of AVCCodecSpecificData
if (memcmp("\x00\x00\x00\x01", data, 4)) {
// 2 bytes for each of the parameter set length field
// plus the 7 bytes for the header
if (size < 4 + 7) {
ALOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
*pOutputSize = size;
*pOutputData = (uint8_t*)malloc(size);
memcpy(*pOutputData, data, size);
return OK;
}
AVCCodecSpecificContext ctx;
uint8_t *outputData = NULL;
size_t outputSize = 0;
// Check if the data is valid
uint8_t type = kNalUnitTypeSeqParamSet;
bool gotSps = false;
bool gotPps = false;
const uint8_t *tmp = data;
const uint8_t *nextStartCode = data;
size_t bytesLeft = size;
size_t paramSetLen = 0;
outputSize = 0;
while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
type = (*(tmp + 4)) & 0x1F;
if (type == kNalUnitTypeSeqParamSet) {
if (gotPps) {
ALOGE("SPS must come before PPS");
return ERROR_MALFORMED;
}
if (!gotSps) {
gotSps = true;
}
nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type,
&paramSetLen);
} else if (type == kNalUnitTypePicParamSet) {
if (!gotSps) {
ALOGE("SPS must come before PPS");
return ERROR_MALFORMED;
}
if (!gotPps) {
gotPps = true;
}
nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type,
&paramSetLen);
} else {
ALOGE("Only SPS and PPS Nal units are expected");
return ERROR_MALFORMED;
}
if (nextStartCode == NULL) {
return ERROR_MALFORMED;
}
// Move on to find the next parameter set
bytesLeft -= nextStartCode - tmp;
tmp = nextStartCode;
outputSize += (2 + paramSetLen);
if (gotSps && gotPps) {
break;
}
}
{
// Check on the number of seq parameter sets
size_t nSeqParamSets = ctx.mSeqParamSets.size();
if (nSeqParamSets == 0) {
ALOGE("Cound not find sequence parameter set");
return ERROR_MALFORMED;
}
if (nSeqParamSets > 0x1F) {
ALOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
return ERROR_MALFORMED;
}
}
{
// Check on the number of pic parameter sets
size_t nPicParamSets = ctx.mPicParamSets.size();
if (nPicParamSets == 0) {
ALOGE("Cound not find picture parameter set");
return ERROR_MALFORMED;
}
if (nPicParamSets > 0xFF) {
ALOGE("Too many pic parameter sets (%d) found", nPicParamSets);
return ERROR_MALFORMED;
}
}
// ISO 14496-15: AVC file format
outputSize += 7; // 7 more bytes in the header
outputData = (uint8_t *)malloc(outputSize);
uint8_t *header = outputData;
header[0] = 1; // version
header[1] = ctx.mProfileIdc; // profile indication
header[2] = ctx.mProfileCompatible; // profile compatibility
header[3] = ctx.mLevelIdc;
// 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
int32_t use2ByteNalLength = 0;
if (param &&
param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) &&
use2ByteNalLength) {
header[4] = 0xfc | 1; // length size == 2 bytes
} else {
header[4] = 0xfc | 3; // length size == 4 bytes
}
// 3-bit '111' followed by 5-bit numSequenceParameterSets
int nSequenceParamSets = ctx.mSeqParamSets.size();
header[5] = 0xe0 | nSequenceParamSets;
header += 6;
for (List<AVCParamSet>::iterator it = ctx.mSeqParamSets.begin();
it != ctx.mSeqParamSets.end(); ++it) {
// 16-bit sequence parameter set length
uint16_t seqParamSetLength = it->mLength;
header[0] = seqParamSetLength >> 8;
header[1] = seqParamSetLength & 0xff;
//ALOGE("### SPS %d %d %d", seqParamSetLength, header[0], header[1]);
// SPS NAL unit (sequence parameter length bytes)
memcpy(&header[2], it->mData, seqParamSetLength);
header += (2 + seqParamSetLength);
}
// 8-bit nPictureParameterSets
int nPictureParamSets = ctx.mPicParamSets.size();
header[0] = nPictureParamSets;
header += 1;
for (List<AVCParamSet>::iterator it = ctx.mPicParamSets.begin();
it != ctx.mPicParamSets.end(); ++it) {
// 16-bit picture parameter set length
uint16_t picParamSetLength = it->mLength;
header[0] = picParamSetLength >> 8;
header[1] = picParamSetLength & 0xff;
//ALOGE("### PPS %d %d %d", picParamSetLength, header[0], header[1]);
// PPS Nal unit (picture parameter set length bytes)
memcpy(&header[2], it->mData, picParamSetLength);
header += (2 + picParamSetLength);
}
*pOutputSize = outputSize;
*pOutputData = outputData;
return OK;
}
status_t removeAVCCodecSpecificData(uint8_t **pOutputData, size_t *pOutputSize,
const uint8_t *data, size_t size, MetaData *param)
{
LOGV("removeAVCCodecSpecificData begin");
LOGV("Inputdataaddr = %p,Inputsize = %d", data,size);
if ( (pOutputData == NULL) || (pOutputSize == NULL) ) {
LOGE("output is invalid");
return ERROR_MALFORMED;
}
if (size < 4) {
LOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
AVCCodecSpecificContext ctx;
uint8_t *outputData = NULL;
size_t outputSize = 0;
// Check if the data is valid
uint8_t type = kNalUnitTypeSeqParamSet;
bool gotSps = false;
bool gotPps = false;
const uint8_t *tmp = data;
const uint8_t *nextStartCode = data;
size_t bytesLeft = size;
size_t paramSetLen = 0;
outputSize = 0;
while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
type = (*(tmp + 4)) & 0x1F;
if (type == kNalUnitTypeSeqParamSet) {
if (gotPps) {
LOGE("SPS must come before PPS");
return ERROR_MALFORMED;
}
if (!gotSps) {
gotSps = true;
}
nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type,
&paramSetLen);
} else if (type == kNalUnitTypePicParamSet) {
if (!gotSps) {
LOGE("SPS must come before PPS");
return ERROR_MALFORMED;
}
if (!gotPps) {
gotPps = true;
}
nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type,
&paramSetLen);
} else {
LOGE("Only SPS and PPS Nal units are expected");
return ERROR_MALFORMED;
}
if (nextStartCode == NULL) {
return ERROR_MALFORMED;
}
bytesLeft -= nextStartCode - tmp;
tmp = nextStartCode;
outputSize += (4 + paramSetLen);
LOGV("DSI size is %d!",outputSize);
if(gotSps && gotPps)
{
break;
}
}
*pOutputData = (uint8_t *)(data + outputSize);
*pOutputSize = outputSize;
LOGV("Outputdataaddr = %p,Outputsize = %d", *pOutputData, *pOutputSize);
LOGV("removeAVCCodecSpecificData end");
return OK;
}
}// namespace android