From 6244527af5c55a9887e54bd8cc7e86c8cb6240fd Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Sat, 26 Jan 2019 18:50:29 +0800 Subject: [PATCH] mkv supports more video codec types 1. support MPEG2 video codec 2. support codecs under V_MS/VFW/FOURCC which include H263, AVC, MPEG2, MPEG4 3. handle some codec type with no codec private data 4. add codec type "MJPEG, XVID, DIVX, DIVX3" and support them in matroskaextractor Bug: 123594230 Test: test with file which has added codec type, and check if the codec related track can be played normally Change-Id: I4e0173b80576b1f80bc7a210a8ef029ee3a2af51 --- media/extractors/mkv/MatroskaExtractor.cpp | 211 +++++++++++++++++- media/extractors/mkv/MatroskaExtractor.h | 2 + media/libstagefright/foundation/MediaDefs.cpp | 4 + .../media/stagefright/foundation/MediaDefs.h | 4 + 4 files changed, 217 insertions(+), 4 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index a399940f99..f8aa784829 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -958,6 +958,59 @@ media_status_t MatroskaSource::read( //////////////////////////////////////////////////////////////////////////////// +// trans all FOURCC to lower char +static uint32_t FourCCtoLower(uint32_t fourcc) { + uint8_t ch_1 = tolower((fourcc >> 24) & 0xff); + uint8_t ch_2 = tolower((fourcc >> 16) & 0xff); + uint8_t ch_3 = tolower((fourcc >> 8) & 0xff); + uint8_t ch_4 = tolower((fourcc) & 0xff); + uint32_t fourcc_out = ch_1 << 24 | ch_2 << 16 | ch_3 << 8 | ch_4; + + return fourcc_out; +} + +static const char *MKVFourCC2MIME(uint32_t fourcc) { + ALOGV("MKVFourCC2MIME fourcc 0x%8.8x", fourcc); + uint32_t lowerFourcc = FourCCtoLower(fourcc); + switch (lowerFourcc) { + case FOURCC("mp4v"): + return MEDIA_MIMETYPE_VIDEO_MPEG4; + + case FOURCC("s263"): + case FOURCC("h263"): + return MEDIA_MIMETYPE_VIDEO_H263; + + case FOURCC("avc1"): + case FOURCC("h264"): + return MEDIA_MIMETYPE_VIDEO_AVC; + + case FOURCC("mpg2"): + return MEDIA_MIMETYPE_VIDEO_MPEG2; + + case FOURCC("xvid"): + return MEDIA_MIMETYPE_VIDEO_XVID; + + case FOURCC("divx"): + case FOURCC("dx50"): + return MEDIA_MIMETYPE_VIDEO_DIVX; + + case FOURCC("div3"): + case FOURCC("div4"): + return MEDIA_MIMETYPE_VIDEO_DIVX3; + + case FOURCC("mjpg"): + case FOURCC("mppg"): + return MEDIA_MIMETYPE_VIDEO_MJPEG; + + default: + char fourccString[5]; + MakeFourCCString(fourcc, fourccString); + ALOGW("mkv unsupport fourcc %s", fourccString); + return ""; + } +} + + MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) : mDataSource(source), mReader(new DataSourceBaseReader(mDataSource)), @@ -1308,6 +1361,89 @@ status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) { return OK; } +status_t MatroskaExtractor::synthesizeMPEG2(TrackInfo *trackInfo, size_t index) { + ALOGV("synthesizeMPEG2"); + BlockIterator iter(this, trackInfo->mTrackNum, index); + if (iter.eos()) { + return ERROR_MALFORMED; + } + + const mkvparser::Block *block = iter.block(); + if (block->GetFrameCount() <= 0) { + return ERROR_MALFORMED; + } + + const mkvparser::Block::Frame &frame = block->GetFrame(0); + auto tmpData = heapbuffer(frame.len); + long n = frame.Read(mReader, tmpData.get()); + if (n != 0) { + return ERROR_MALFORMED; + } + + size_t header_start = 0; + size_t header_lenth = 0; + for (header_start = 0; header_start < frame.len - 4; header_start++) { + if (ntohl(0x000001b3) == *(uint32_t*)((uint8_t*)tmpData.get() + header_start)) { + break; + } + } + bool isComplete_csd = false; + for (header_lenth = 0; header_lenth < frame.len - 4 - header_start; header_lenth++) { + if (ntohl(0x000001b8) == *(uint32_t*)((uint8_t*)tmpData.get() + + header_start + header_lenth)) { + isComplete_csd = true; + break; + } + } + if (!isComplete_csd) { + ALOGE("can't parse complete csd for MPEG2!"); + return ERROR_MALFORMED; + } + addESDSFromCodecPrivate(trackInfo->mMeta, false, + (uint8_t*)(tmpData.get()) + header_start, header_lenth); + + return OK; + +} + +status_t MatroskaExtractor::synthesizeMPEG4(TrackInfo *trackInfo, size_t index) { + ALOGV("synthesizeMPEG4"); + BlockIterator iter(this, trackInfo->mTrackNum, index); + if (iter.eos()) { + return ERROR_MALFORMED; + } + + const mkvparser::Block *block = iter.block(); + if (block->GetFrameCount() <= 0) { + return ERROR_MALFORMED; + } + + const mkvparser::Block::Frame &frame = block->GetFrame(0); + auto tmpData = heapbuffer(frame.len); + long n = frame.Read(mReader, tmpData.get()); + if (n != 0) { + return ERROR_MALFORMED; + } + + size_t vosend; + bool isComplete_csd = false; + for (vosend = 0; (long)vosend < frame.len - 4; vosend++) { + if (ntohl(0x000001b6) == *(uint32_t*)((uint8_t*)tmpData.get() + vosend)) { + isComplete_csd = true; + break; // Send VOS until VOP + } + } + if (!isComplete_csd) { + ALOGE("can't parse complete csd for MPEG4!"); + return ERROR_MALFORMED; + } + addESDSFromCodecPrivate(trackInfo->mMeta, false, tmpData.get(), vosend); + + return OK; + +} + + static inline bool isValidInt32ColourValue(long long value) { return value != mkvparser::Colour::kValueNotPresent && value >= INT32_MIN @@ -1490,6 +1626,8 @@ void MatroskaExtractor::addTracks() { status_t err = OK; int32_t nalSize = -1; + bool isSetCsdFrom1stFrame = false; + switch (track->GetType()) { case VIDEO_TRACK: { @@ -1516,15 +1654,15 @@ void MatroskaExtractor::addTracks() { continue; } } else if (!strcmp("V_MPEG4/ISO/ASP", codecID)) { + AMediaFormat_setString(meta, + AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG4); if (codecPrivateSize > 0) { - AMediaFormat_setString(meta, - AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG4); addESDSFromCodecPrivate( meta, false, codecPrivate, codecPrivateSize); } else { ALOGW("%s is detected, but does not have configuration.", codecID); - continue; + isSetCsdFrom1stFrame = true; } } else if (!strcmp("V_VP8", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_VP8); @@ -1538,6 +1676,49 @@ void MatroskaExtractor::addTracks() { } } else if (!strcmp("V_AV1", codecID)) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1); + } else if (!strcmp("V_MPEG2", codecID) || !strcmp("V_MPEG1", codecID)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, + MEDIA_MIMETYPE_VIDEO_MPEG2); + if (codecPrivate != NULL) { + addESDSFromCodecPrivate(meta, false, codecPrivate, codecPrivateSize); + } else { + ALOGW("No specific codec private data, find it from the first frame"); + isSetCsdFrom1stFrame = true; + } + } else if (!strcmp("V_MJPEG", codecID)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, + MEDIA_MIMETYPE_VIDEO_MJPEG); + } else if (!strcmp("V_MS/VFW/FOURCC", codecID)) { + if (NULL == codecPrivate ||codecPrivateSize < 20) { + ALOGE("V_MS/VFW/FOURCC has no valid private data(%p),codecPrivateSize:%zu", + codecPrivate, codecPrivateSize); + continue; + } else { + uint32_t fourcc = *(uint32_t *)(codecPrivate + 16); + fourcc = ntohl(fourcc); + const char* mime = MKVFourCC2MIME(fourcc); + ALOGV("V_MS/VFW/FOURCC type is %s", mime); + if (!strncasecmp("video/", mime, 6)) { + AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, mime); + } else { + ALOGE("V_MS/VFW/FOURCC continue,unsupport video type=%s,fourcc=0x%08x.", + mime, fourcc); + continue; + } + if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_XVID) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DIVX) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DIVX3) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2) || + !strcmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { + isSetCsdFrom1stFrame = true; + } else { + ALOGW("FourCC have unsupport codec, type=%s,fourcc=0x%08x.", + mime, fourcc); + continue; + } + } } else { ALOGW("%s is not supported.", codecID); continue; @@ -1681,13 +1862,35 @@ void MatroskaExtractor::addTracks() { initTrackInfo(track, meta, trackInfo); trackInfo->mNalLengthSize = nalSize; - if (!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) { + const char *mimetype = ""; + AMediaFormat_getString(meta, AMEDIAFORMAT_KEY_MIME, &mimetype); + + if ((!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_AVC) && isSetCsdFrom1stFrame)) { // Attempt to recover from AVC track without codec private data err = synthesizeAVCC(trackInfo, n); if (err != OK) { mTracks.pop(); } + } else if ((!strcmp("V_MPEG2", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG2) && isSetCsdFrom1stFrame)) { + // Attempt to recover from MPEG2 track without codec private data + err = synthesizeMPEG2(trackInfo, n); + if (err != OK) { + mTracks.pop(); + } + } else if ((!strcmp("V_MPEG4/ISO/ASP", codecID) && codecPrivateSize == 0) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_MPEG4) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_XVID) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_DIVX) && isSetCsdFrom1stFrame) || + (!strcmp(mimetype, MEDIA_MIMETYPE_VIDEO_DIVX3) && isSetCsdFrom1stFrame)) { + // Attempt to recover from MPEG4 track without codec private data + err = synthesizeMPEG4(trackInfo, n); + if (err != OK) { + mTracks.pop(); + } } + } } diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h index 3871bdfb9f..d53d9e332b 100644 --- a/media/extractors/mkv/MatroskaExtractor.h +++ b/media/extractors/mkv/MatroskaExtractor.h @@ -95,6 +95,8 @@ private: int64_t mSeekPreRollNs; status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index); + status_t synthesizeMPEG2(TrackInfo *trackInfo, size_t index); + status_t synthesizeMPEG4(TrackInfo *trackInfo, size_t index); status_t initTrackInfo( const mkvparser::Track *track, AMediaFormat *meta, diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp index 9d1ec1fdbb..52b2765fb2 100644 --- a/media/libstagefright/foundation/MediaDefs.cpp +++ b/media/libstagefright/foundation/MediaDefs.cpp @@ -32,6 +32,10 @@ const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw"; const char *MEDIA_MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision"; const char *MEDIA_MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX = "video/divx"; +const char *MEDIA_MIMETYPE_VIDEO_DIVX3 = "video/divx3"; +const char *MEDIA_MIMETYPE_VIDEO_XVID = "video/xvid"; +const char *MEDIA_MIMETYPE_VIDEO_MJPEG = "video/x-motion-jpeg"; const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h index e68852dce6..007a09b581 100644 --- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h +++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h @@ -34,6 +34,10 @@ extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2; extern const char *MEDIA_MIMETYPE_VIDEO_RAW; extern const char *MEDIA_MIMETYPE_VIDEO_DOLBY_VISION; extern const char *MEDIA_MIMETYPE_VIDEO_SCRAMBLED; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX; +extern const char *MEDIA_MIMETYPE_VIDEO_DIVX3; +extern const char *MEDIA_MIMETYPE_VIDEO_XVID; +extern const char *MEDIA_MIMETYPE_VIDEO_MJPEG; extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB; extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;