Support MediaCodec::getOutputFormat for encoders
codec specific data is provided as part of the MediaFormat if available.
Change-Id: I5a79c936e2411fe66ebc694791071faefc33941e
related-to-bug: 8616651
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index a06a8e1..76aa503 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -177,6 +177,8 @@
kFlagDequeueOutputPending = 32,
kFlagIsSecure = 64,
kFlagSawMediaServerDie = 128,
+ kFlagIsEncoder = 256,
+ kFlagGatherCodecSpecificData = 512,
};
struct BufferInfo {
@@ -244,6 +246,8 @@
status_t onSetParameters(const sp<AMessage> ¶ms);
+ status_t amendOutputFormatWithCodecSpecificData(const sp<ABuffer> &buffer);
+
DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
};
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 6d952c3..058852e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1470,24 +1470,47 @@
&format, sizeof(format));
}
+static const struct VideoCodingMapEntry {
+ const char *mMime;
+ OMX_VIDEO_CODINGTYPE mVideoCodingType;
+} kVideoCodingMapEntry[] = {
+ { MEDIA_MIMETYPE_VIDEO_AVC, OMX_VIDEO_CodingAVC },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 },
+ { MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 },
+ { MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 },
+ { MEDIA_MIMETYPE_VIDEO_VPX, OMX_VIDEO_CodingVPX },
+};
+
static status_t GetVideoCodingTypeFromMime(
const char *mime, OMX_VIDEO_CODINGTYPE *codingType) {
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- *codingType = OMX_VIDEO_CodingAVC;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- *codingType = OMX_VIDEO_CodingMPEG4;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- *codingType = OMX_VIDEO_CodingH263;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
- *codingType = OMX_VIDEO_CodingMPEG2;
- } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
- *codingType = OMX_VIDEO_CodingVPX;
- } else {
- *codingType = OMX_VIDEO_CodingUnused;
- return ERROR_UNSUPPORTED;
+ for (size_t i = 0;
+ i < sizeof(kVideoCodingMapEntry) / sizeof(kVideoCodingMapEntry[0]);
+ ++i) {
+ if (!strcasecmp(mime, kVideoCodingMapEntry[i].mMime)) {
+ *codingType = kVideoCodingMapEntry[i].mVideoCodingType;
+ return OK;
+ }
}
- return OK;
+ *codingType = OMX_VIDEO_CodingUnused;
+
+ return ERROR_UNSUPPORTED;
+}
+
+static status_t GetMimeTypeForVideoCoding(
+ OMX_VIDEO_CODINGTYPE codingType, AString *mime) {
+ for (size_t i = 0;
+ i < sizeof(kVideoCodingMapEntry) / sizeof(kVideoCodingMapEntry[0]);
+ ++i) {
+ if (codingType == kVideoCodingMapEntry[i].mVideoCodingType) {
+ *mime = kVideoCodingMapEntry[i].mMime;
+ return OK;
+ }
+ }
+
+ mime->clear();
+
+ return ERROR_UNSUPPORTED;
}
status_t ACodec::setupVideoDecoder(
@@ -2227,49 +2250,61 @@
{
OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
- notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
- notify->setInt32("width", videoDef->nFrameWidth);
- notify->setInt32("height", videoDef->nFrameHeight);
- notify->setInt32("stride", videoDef->nStride);
- notify->setInt32("slice-height", videoDef->nSliceHeight);
- notify->setInt32("color-format", videoDef->eColorFormat);
-
- OMX_CONFIG_RECTTYPE rect;
- InitOMXParams(&rect);
- rect.nPortIndex = kPortIndexOutput;
-
- if (mOMX->getConfig(
- mNode, OMX_IndexConfigCommonOutputCrop,
- &rect, sizeof(rect)) != OK) {
- rect.nLeft = 0;
- rect.nTop = 0;
- rect.nWidth = videoDef->nFrameWidth;
- rect.nHeight = videoDef->nFrameHeight;
+ AString mime;
+ if (!mIsEncoder) {
+ notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
+ } else if (GetMimeTypeForVideoCoding(
+ videoDef->eCompressionFormat, &mime) != OK) {
+ notify->setString("mime", "application/octet-stream");
+ } else {
+ notify->setString("mime", mime.c_str());
}
- CHECK_GE(rect.nLeft, 0);
- CHECK_GE(rect.nTop, 0);
- CHECK_GE(rect.nWidth, 0u);
- CHECK_GE(rect.nHeight, 0u);
- CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
- CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+ notify->setInt32("width", videoDef->nFrameWidth);
+ notify->setInt32("height", videoDef->nFrameHeight);
- notify->setRect(
- "crop",
- rect.nLeft,
- rect.nTop,
- rect.nLeft + rect.nWidth - 1,
- rect.nTop + rect.nHeight - 1);
+ if (!mIsEncoder) {
+ notify->setInt32("stride", videoDef->nStride);
+ notify->setInt32("slice-height", videoDef->nSliceHeight);
+ notify->setInt32("color-format", videoDef->eColorFormat);
- if (mNativeWindow != NULL) {
- android_native_rect_t crop;
- crop.left = rect.nLeft;
- crop.top = rect.nTop;
- crop.right = rect.nLeft + rect.nWidth;
- crop.bottom = rect.nTop + rect.nHeight;
+ OMX_CONFIG_RECTTYPE rect;
+ InitOMXParams(&rect);
+ rect.nPortIndex = kPortIndexOutput;
- CHECK_EQ(0, native_window_set_crop(
- mNativeWindow.get(), &crop));
+ if (mOMX->getConfig(
+ mNode, OMX_IndexConfigCommonOutputCrop,
+ &rect, sizeof(rect)) != OK) {
+ rect.nLeft = 0;
+ rect.nTop = 0;
+ rect.nWidth = videoDef->nFrameWidth;
+ rect.nHeight = videoDef->nFrameHeight;
+ }
+
+ CHECK_GE(rect.nLeft, 0);
+ CHECK_GE(rect.nTop, 0);
+ CHECK_GE(rect.nWidth, 0u);
+ CHECK_GE(rect.nHeight, 0u);
+ CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
+ CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+
+ notify->setRect(
+ "crop",
+ rect.nLeft,
+ rect.nTop,
+ rect.nLeft + rect.nWidth - 1,
+ rect.nTop + rect.nHeight - 1);
+
+ if (mNativeWindow != NULL) {
+ android_native_rect_t crop;
+ crop.left = rect.nLeft;
+ crop.top = rect.nTop;
+ crop.right = rect.nLeft + rect.nWidth;
+ crop.bottom = rect.nTop + rect.nHeight;
+
+ CHECK_EQ(0, native_window_set_crop(
+ mNativeWindow.get(), &crop));
+ }
}
break;
}
@@ -2277,41 +2312,108 @@
case OMX_PortDomainAudio:
{
OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
- CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM);
- OMX_AUDIO_PARAM_PCMMODETYPE params;
- InitOMXParams(¶ms);
- params.nPortIndex = kPortIndexOutput;
+ switch (audioDef->eEncoding) {
+ case OMX_AUDIO_CodingPCM:
+ {
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
- CHECK_EQ(mOMX->getParameter(
- mNode, OMX_IndexParamAudioPcm,
- ¶ms, sizeof(params)),
- (status_t)OK);
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPcm,
+ ¶ms, sizeof(params)),
+ (status_t)OK);
- CHECK(params.nChannels == 1 || params.bInterleaved);
- CHECK_EQ(params.nBitPerSample, 16u);
- CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
- CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
+ CHECK(params.nChannels == 1 || params.bInterleaved);
+ CHECK_EQ(params.nBitPerSample, 16u);
+ CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
+ CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
- notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
- notify->setInt32("channel-count", params.nChannels);
- notify->setInt32("sample-rate", params.nSamplingRate);
- if (mEncoderDelay + mEncoderPadding) {
- size_t frameSize = params.nChannels * sizeof(int16_t);
- if (mSkipCutBuffer != NULL) {
- size_t prevbufsize = mSkipCutBuffer->size();
- if (prevbufsize != 0) {
- ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize);
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSamplingRate);
+ if (mEncoderDelay + mEncoderPadding) {
+ size_t frameSize = params.nChannels * sizeof(int16_t);
+ if (mSkipCutBuffer != NULL) {
+ size_t prevbufsize = mSkipCutBuffer->size();
+ if (prevbufsize != 0) {
+ ALOGW("Replacing SkipCutBuffer holding %d bytes", prevbufsize);
+ }
+ }
+ mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize,
+ mEncoderPadding * frameSize);
}
+
+ if (mChannelMaskPresent) {
+ notify->setInt32("channel-mask", mChannelMask);
+ }
+ break;
}
- mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay * frameSize,
- mEncoderPadding * frameSize);
- }
- if (mChannelMaskPresent) {
- notify->setInt32("channel-mask", mChannelMask);
- }
+ case OMX_AUDIO_CodingAAC:
+ {
+ OMX_AUDIO_PARAM_AACPROFILETYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAac,
+ ¶ms, sizeof(params)),
+ (status_t)OK);
+
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSampleRate);
+ break;
+ }
+
+ case OMX_AUDIO_CodingAMR:
+ {
+ OMX_AUDIO_PARAM_AMRTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAmr,
+ ¶ms, sizeof(params)),
+ (status_t)OK);
+
+ notify->setInt32("channel-count", 1);
+ if (params.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0) {
+ notify->setString(
+ "mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
+
+ notify->setInt32("sample-rate", 16000);
+ } else {
+ notify->setString(
+ "mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
+
+ notify->setInt32("sample-rate", 8000);
+ }
+ break;
+ }
+
+ case OMX_AUDIO_CodingFLAC:
+ {
+ OMX_AUDIO_PARAM_FLACTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+
+ CHECK_EQ(mOMX->getParameter(
+ mNode, OMX_IndexParamAudioFlac,
+ ¶ms, sizeof(params)),
+ (status_t)OK);
+
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_FLAC);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSampleRate);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
break;
}
@@ -2957,7 +3059,7 @@
break;
}
- if (!mCodec->mIsEncoder && !mCodec->mSentFormat) {
+ if (!mCodec->mSentFormat) {
mCodec->sendFormatChange();
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ae7bb17..714da55 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -31,10 +31,13 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/NativeWindowWrapper.h>
+#include "include/avc_utils.h"
+
namespace android {
// static
@@ -741,8 +744,16 @@
}
mOutputFormat = msg;
- mFlags |= kFlagOutputFormatChanged;
- postActivityNotificationIfPossible();
+
+ if (mFlags & kFlagIsEncoder) {
+ // Before we announce the format change we should
+ // collect codec specific data and amend the output
+ // format as necessary.
+ mFlags |= kFlagGatherCodecSpecificData;
+ } else {
+ mFlags |= kFlagOutputFormatChanged;
+ postActivityNotificationIfPossible();
+ }
break;
}
@@ -812,6 +823,25 @@
buffer->meta()->setInt32("omxFlags", omxFlags);
+ if (mFlags & kFlagGatherCodecSpecificData) {
+ // This is the very first output buffer after a
+ // format change was signalled, it'll either contain
+ // the one piece of codec specific data we can expect
+ // or there won't be codec specific data.
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ status_t err =
+ amendOutputFormatWithCodecSpecificData(buffer);
+
+ if (err != OK) {
+ ALOGE("Codec spit out malformed codec "
+ "specific data!");
+ }
+ }
+
+ mFlags &= ~kFlagGatherCodecSpecificData;
+ mFlags |= kFlagOutputFormatChanged;
+ }
+
if (mFlags & kFlagDequeueOutputPending) {
CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
@@ -955,6 +985,7 @@
if (flags & CONFIGURE_FLAG_ENCODE) {
format->setInt32("encoder", true);
+ mFlags |= kFlagIsEncoder;
}
extractCSD(format);
@@ -1413,6 +1444,8 @@
mFlags &= ~kFlagOutputFormatChanged;
mFlags &= ~kFlagOutputBuffersChanged;
mFlags &= ~kFlagStickyError;
+ mFlags &= ~kFlagIsEncoder;
+ mFlags &= ~kFlagGatherCodecSpecificData;
mActivityNotify.clear();
}
@@ -1720,4 +1753,45 @@
return OK;
}
+status_t MediaCodec::amendOutputFormatWithCodecSpecificData(
+ const sp<ABuffer> &buffer) {
+ AString mime;
+ CHECK(mOutputFormat->findString("mime", &mime));
+
+ if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+ // Codec specific data should be SPS and PPS in a single buffer,
+ // each prefixed by a startcode (0x00 0x00 0x00 0x01).
+ // We separate the two and put them into the output format
+ // under the keys "csd-0" and "csd-1".
+
+ unsigned csdIndex = 0;
+
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ sp<ABuffer> csd = new ABuffer(nalSize + 4);
+ memcpy(csd->data(), "\x00\x00\x00\x01", 4);
+ memcpy(csd->data() + 4, nalStart, nalSize);
+
+ mOutputFormat->setBuffer(
+ StringPrintf("csd-%u", csdIndex).c_str(), csd);
+
+ ++csdIndex;
+ }
+
+ if (csdIndex != 2) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ // For everything else we just stash the codec specific data into
+ // the output format as a single piece of csd under "csd-0".
+ mOutputFormat->setBuffer("csd-0", buffer);
+ }
+
+ return OK;
+}
+
} // namespace android