From d69ffb9cf12a4664584e0b3eea30e23b95db4ad0 Mon Sep 17 00:00:00 2001 From: Leena Winterrowd Date: Wed, 10 Sep 2014 19:22:54 -0700 Subject: libstagefright: Handle MPEG4 DP playback Since the HW decoder doesn't support MPEG4 DP clips, detect DP format clips in the parser and report a new MIME: video/mpeg4-esdp. This MIME is only registered to the SW decoder which supports DP clips. Merges the following change from kitkat: libstagefright: Fix DP Parsing issue with mpeg4 SP,ASP (Change-Id: I69c719011e1a0d2a0b0ae5a9b504b7cce443866b) CRs-Fixed: 722066 Change-Id: I7bc3a7a9f4a6d37e046ed9c8008cb27fb3bc665d --- include/media/stagefright/Utils.h | 4 + media/libstagefright/ACodec.cpp | 2 + media/libstagefright/MPEG4Extractor.cpp | 3 + media/libstagefright/OMXCodec.cpp | 8 +- media/libstagefright/Utils.cpp | 223 +++++++++++++++++++++ .../libstagefright/matroska/MatroskaExtractor.cpp | 2 + media/libstagefright/rtsp/APacketSource.cpp | 3 + 7 files changed, 243 insertions(+), 2 deletions(-) diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h index 1e9de1a..847ea36 100644 --- a/include/media/stagefright/Utils.h +++ b/include/media/stagefright/Utils.h @@ -87,6 +87,10 @@ void readFromAMessage( audio_format_t getPCMFormat(const sp &format); +void updateVideoTrackInfoFromESDS_MPEG4Video(sp meta); +bool checkDPFromVOLHeader(const uint8_t *ptr, size_t size); +bool checkDPFromCodecSpecificData(const uint8_t *ptr, size_t size); + } // namespace android #endif // UTILS_H_ diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 6c4fd76..f7cc4cd 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1590,6 +1590,8 @@ status_t ACodec::setComponentRole( "audio_decoder.ac3", "audio_encoder.ac3" }, { MEDIA_MIMETYPE_AUDIO_EAC3, "audio_decoder.eac3", "audio_encoder.eac3" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4_DP, + "video_decoder.mpeg4", NULL }, }; static const size_t kNumMimeToRole = diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 738b134..d0dba73 100755 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1718,6 +1718,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } } + + updateVideoTrackInfoFromESDS_MPEG4Video(mLastTrack->meta); + break; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 3ec02d4..d387236 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -905,7 +905,8 @@ void OMXCodec::setVideoInputFormat( compressionFormat = OMX_VIDEO_CodingAVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { compressionFormat = OMX_VIDEO_CodingHEVC; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || + !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4_DP, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; @@ -1297,7 +1298,8 @@ status_t OMXCodec::setVideoOutputFormat( OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { compressionFormat = OMX_VIDEO_CodingAVC; - } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || + !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4_DP, mime)) { compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { compressionFormat = OMX_VIDEO_CodingHEVC; @@ -1499,6 +1501,8 @@ void OMXCodec::setComponentRole( "video_decoder.hevc", "video_encoder.hevc" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "video_decoder.mpeg4", "video_encoder.mpeg4" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4_DP, + "video_decoder.mpeg4", NULL }, { MEDIA_MIMETYPE_VIDEO_H263, "video_decoder.h263", "video_encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_VP8, diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 6d62e03..2aaff6c 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -37,6 +37,7 @@ #include #include +#include namespace android { @@ -1057,5 +1058,227 @@ audio_format_t getPCMFormat(const sp &format) { return AUDIO_FORMAT_PCM_16_BIT; } +void updateVideoTrackInfoFromESDS_MPEG4Video(sp meta) { + const char* mime = NULL; + if (meta != NULL && meta->findCString(kKeyMIMEType, &mime) && + mime && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) { + uint32_t type; + const void *data; + size_t size; + if (!meta->findData(kKeyESDS, &type, &data, &size) || !data) { + ALOGW("ESDS atom is invalid"); + return; + } + + if (checkDPFromCodecSpecificData((const uint8_t*) data, size)) { + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4_DP); + } + } +} + +bool checkDPFromCodecSpecificData(const uint8_t *data, size_t size) { + bool retVal = false; + size_t offset = 0, startCodeOffset = 0; + bool isStartCode = false; + const int kVolStartCode = 0x20; + const char kStartCode[] = "\x00\x00\x01"; + // must contain at least 4 bytes for video_object_layer_start_code + const size_t kMinCsdSize = 4; + + if (!data || (size < kMinCsdSize)) { + ALOGV("Invalid CSD (expected at least %zu bytes)", kMinCsdSize); + return retVal; + } + + while (offset < size - 3) { + if ((data[offset + 3] & 0xf0) == kVolStartCode) { + if (!memcmp(&data[offset], kStartCode, 3)) { + startCodeOffset = offset; + isStartCode = true; + break; + } + } + + offset++; + } + + if (isStartCode) { + retVal = checkDPFromVOLHeader((const uint8_t*) &data[startCodeOffset], + (size - startCodeOffset)); + } + + return retVal; +} + +bool checkDPFromVOLHeader(const uint8_t *data, size_t size) { + bool retVal = false; + // must contain at least 4 bytes for video_object_layer_start_code + 9 bits of data + const size_t kMinHeaderSize = 6; + + if (!data || (size < kMinHeaderSize)) { + ALOGV("Invalid VOL header (expected at least %zu bytes)", kMinHeaderSize); + return false; + } + + ALOGV("Checking for MPEG4 DP bit"); + ABitReader br(&data[4], (size - 4)); + br.skipBits(1); // random_accessible_vol + + unsigned videoObjectTypeIndication = br.getBits(8); + if (videoObjectTypeIndication == 0x12u) { + ALOGW("checkDPFromVOLHeader: videoObjectTypeIndication:%u", + videoObjectTypeIndication); + return false; + } + + unsigned videoObjectLayerVerid = 1; + if (br.getBits(1)) { + videoObjectLayerVerid = br.getBits(4); + br.skipBits(3); // video_object_layer_priority + ALOGV("checkDPFromVOLHeader: videoObjectLayerVerid:%u", + videoObjectLayerVerid); + } + + if (br.getBits(4) == 0x0f) { // aspect_ratio_info + ALOGV("checkDPFromVOLHeader: extended PAR"); + br.skipBits(8); // par_width + br.skipBits(8); // par_height + } + + if (br.getBits(1)) { // vol_control_parameters + br.skipBits(2); // chroma_format + br.skipBits(1); // low_delay + if (br.getBits(1)) { // vbv_parameters + br.skipBits(15); // first_half_bit_rate + br.skipBits(1); // marker_bit + br.skipBits(15); // latter_half_bit_rate + br.skipBits(1); // marker_bit + br.skipBits(15); // first_half_vbv_buffer_size + br.skipBits(1); // marker_bit + br.skipBits(3); // latter_half_vbv_buffer_size + br.skipBits(11); // first_half_vbv_occupancy + br.skipBits(1); // marker_bit + br.skipBits(15); // latter_half_vbv_occupancy + br.skipBits(1); // marker_bit + } + } + + unsigned videoObjectLayerShape = br.getBits(2); + if (videoObjectLayerShape != 0x00u /* rectangular */) { + ALOGV("checkDPFromVOLHeader: videoObjectLayerShape:%x", + videoObjectLayerShape); + return false; + } + + br.skipBits(1); // marker_bit + unsigned vopTimeIncrementResolution = br.getBits(16); + br.skipBits(1); // marker_bit + if (br.getBits(1)) { // fixed_vop_rate + // range [0..vopTimeIncrementResolution) + + // vopTimeIncrementResolution + // 2 => 0..1, 1 bit + // 3 => 0..2, 2 bits + // 4 => 0..3, 2 bits + // 5 => 0..4, 3 bits + // ... + + if (vopTimeIncrementResolution <= 0u) { + return BAD_VALUE; + } + + --vopTimeIncrementResolution; + unsigned numBits = 0; + while (vopTimeIncrementResolution > 0) { + ++numBits; + vopTimeIncrementResolution >>= 1; + } + + br.skipBits(numBits); // fixed_vop_time_increment + } + + br.skipBits(1); // marker_bit + br.skipBits(13); // video_object_layer_width + br.skipBits(1); // marker_bit + br.skipBits(13); // video_object_layer_height + br.skipBits(1); // marker_bit + br.skipBits(1); // interlaced + br.skipBits(1); // obmc_disable + unsigned spriteEnable = 0; + if (videoObjectLayerVerid == 1) { + spriteEnable = br.getBits(1); + } else { + spriteEnable = br.getBits(2); + } + + if (spriteEnable == 0x1) { // static + int spriteWidth = br.getBits(13); + ALOGV("checkDPFromVOLHeader: spriteWidth:%d", spriteWidth); + br.skipBits(1) ; // marker_bit + br.skipBits(13); // sprite_height + br.skipBits(1); // marker_bit + br.skipBits(13); // sprite_left_coordinate + br.skipBits(1); // marker_bit + br.skipBits(13); // sprite_top_coordinate + br.skipBits(1); // marker_bit + br.skipBits(6); // no_of_sprite_warping_points + br.skipBits(2); // sprite_warping_accuracy + br.skipBits(1); // sprite_brightness_change + br.skipBits(1); // low_latency_sprite_enable + } else if (spriteEnable == 0x2) { // GMC + br.skipBits(6); // no_of_sprite_warping_points + br.skipBits(2); // sprite_warping_accuracy + br.skipBits(1); // sprite_brightness_change + } + + if (videoObjectLayerVerid != 1 + && videoObjectLayerShape != 0x0u) { + br.skipBits(1); + } + + if (br.getBits(1)) { // not_8_bit + br.skipBits(4); // quant_precision + br.skipBits(4); // bits_per_pixel + } + + if (videoObjectLayerShape == 0x3) { + br.skipBits(1); + br.skipBits(1); + br.skipBits(1); + } + + if (br.getBits(1)) { // quant_type + if (br.getBits(1)) { // load_intra_quant_mat + unsigned IntraQuantMat = 1; + for (int i = 0; i < 64 && IntraQuantMat; i++) { + IntraQuantMat = br.getBits(8); + } + } + + if (br.getBits(1)) { // load_non_intra_quant_matrix + unsigned NonIntraQuantMat = 1; + for (int i = 0; i < 64 && NonIntraQuantMat; i++) { + NonIntraQuantMat = br.getBits(8); + } + } + } /* quantType */ + + if (videoObjectLayerVerid != 1) { + unsigned quarterSample = br.getBits(1); + ALOGV("checkDPFromVOLHeader: quarterSample:%u", + quarterSample); + } + + br.skipBits(1); // complexity_estimation_disable + br.skipBits(1); // resync_marker_disable + unsigned dataPartitioned = br.getBits(1); + if (dataPartitioned) { + retVal = true; + } + + ALOGD("checkDPFromVOLHeader: DP:%u", dataPartitioned); + return retVal; +} + } // namespace android diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 06057d0..f8fa015 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -903,6 +903,8 @@ static void addESDSFromCodecPrivate( meta->setData(kKeyESDS, 0, esds, esdsSize); + updateVideoTrackInfoFromESDS_MPEG4Video(meta); + delete[] esds; esds = NULL; } diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index cfafaa7..e81325d 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include namespace android { @@ -535,6 +536,8 @@ APacketSource::APacketSource( return; } + updateVideoTrackInfoFromESDS_MPEG4Video(mFormat); + mFormat->setInt32(kKeyWidth, width); mFormat->setInt32(kKeyHeight, height); } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) { -- cgit v1.1