From 386d609dc513e838c7e7c4c46c604493ccd560be Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 19 May 2011 08:37:39 -0700 Subject: Support mpeg1,2 audio and mpeg1,2,4 video content extraction from .ts streams. Change-Id: I9d2ee63495f161e30daba7c3aab16cb9d8ced6a5 --- include/media/stagefright/MediaDefs.h | 1 + .../nuplayer/HTTPLiveSource.cpp | 4 +- .../nuplayer/StreamingSource.cpp | 4 +- media/libstagefright/ACodec.cpp | 9 +- media/libstagefright/MP3Extractor.cpp | 164 +------ media/libstagefright/MediaDefs.cpp | 1 + media/libstagefright/OMXCodec.cpp | 8 +- media/libstagefright/VBRISeeker.cpp | 3 +- media/libstagefright/avc_utils.cpp | 231 ++++++++++ media/libstagefright/codecs/mp3dec/SoftMP3.cpp | 1 + media/libstagefright/include/MP3Extractor.h | 5 - media/libstagefright/include/avc_utils.h | 10 + media/libstagefright/mpeg2ts/ATSParser.cpp | 130 +++++- media/libstagefright/mpeg2ts/ATSParser.h | 15 +- media/libstagefright/mpeg2ts/ESQueue.cpp | 485 ++++++++++++++++++++- media/libstagefright/mpeg2ts/ESQueue.h | 8 +- media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 6 +- media/libstagefright/rtsp/APacketSource.cpp | 88 +--- 18 files changed, 893 insertions(+), 280 deletions(-) diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index 5e471c1..3e48459 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -26,6 +26,7 @@ extern const char *MEDIA_MIMETYPE_VIDEO_VPX; extern const char *MEDIA_MIMETYPE_VIDEO_AVC; extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4; extern const char *MEDIA_MIMETYPE_VIDEO_H263; +extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2; extern const char *MEDIA_MIMETYPE_VIDEO_RAW; extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB; diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 576a850..b3b3af5 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -77,7 +77,7 @@ void NuPlayer::HTTPLiveSource::start() { sp NuPlayer::HTTPLiveSource::getFormat(bool audio) { ATSParser::SourceType type = - audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO; + audio ? ATSParser::AUDIO : ATSParser::VIDEO; sp source = static_cast(mTSParser->getSource(type).get()); @@ -131,7 +131,7 @@ bool NuPlayer::HTTPLiveSource::feedMoreTSData() { status_t NuPlayer::HTTPLiveSource::dequeueAccessUnit( bool audio, sp *accessUnit) { ATSParser::SourceType type = - audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO; + audio ? ATSParser::AUDIO : ATSParser::VIDEO; sp source = static_cast(mTSParser->getSource(type).get()); diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index 2016282..bbc8a6e 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -87,7 +87,7 @@ bool NuPlayer::StreamingSource::feedMoreTSData() { sp NuPlayer::StreamingSource::getFormat(bool audio) { ATSParser::SourceType type = - audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO; + audio ? ATSParser::AUDIO : ATSParser::VIDEO; sp source = static_cast(mTSParser->getSource(type).get()); @@ -102,7 +102,7 @@ sp NuPlayer::StreamingSource::getFormat(bool audio) { status_t NuPlayer::StreamingSource::dequeueAccessUnit( bool audio, sp *accessUnit) { ATSParser::SourceType type = - audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO; + audio ? ATSParser::AUDIO : ATSParser::VIDEO; sp source = static_cast(mTSParser->getSource(type).get()); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 4189354..642b3a3 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -909,6 +909,8 @@ status_t ACodec::setupVideoDecoder( compressionFormat = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { compressionFormat = OMX_VIDEO_CodingH263; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { + compressionFormat = OMX_VIDEO_CodingMPEG2; } else { TRESPASS(); } @@ -1647,6 +1649,10 @@ void ACodec::UninitializedState::onSetup( componentName = "OMX.google.aac.decoder"; } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) { componentName = "OMX.Nvidia.mp3.decoder"; + } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG2)) { + componentName = "OMX.Nvidia.mpeg2v.decode"; + } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) { + componentName = "OMX.google.mpeg4.decoder"; } else { TRESPASS(); } @@ -1670,7 +1676,8 @@ void ACodec::UninitializedState::onSetup( mCodec->configureCodec(mime.c_str(), msg); sp obj; - if (msg->findObject("native-window", &obj)) { + if (msg->findObject("native-window", &obj) + && strncmp("OMX.google.", componentName.c_str(), 11)) { sp nativeWindow( static_cast(obj.get())); CHECK(nativeWindow != NULL); diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 4bdfc6f..5bbed5d 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -20,6 +20,7 @@ #include "include/MP3Extractor.h" +#include "include/avc_utils.h" #include "include/ID3.h" #include "include/VBRISeeker.h" #include "include/XINGSeeker.h" @@ -44,158 +45,6 @@ namespace android { // Yes ... there are things that must indeed match... static const uint32_t kMask = 0xfffe0c00; -// static -bool MP3Extractor::get_mp3_frame_size( - uint32_t header, size_t *frame_size, - int *out_sampling_rate, int *out_channels, - int *out_bitrate, int *out_num_samples) { - *frame_size = 0; - - if (out_sampling_rate) { - *out_sampling_rate = 0; - } - - if (out_channels) { - *out_channels = 0; - } - - if (out_bitrate) { - *out_bitrate = 0; - } - - if (out_num_samples) { - *out_num_samples = 1152; - } - - if ((header & 0xffe00000) != 0xffe00000) { - return false; - } - - unsigned version = (header >> 19) & 3; - - if (version == 0x01) { - return false; - } - - unsigned layer = (header >> 17) & 3; - - if (layer == 0x00) { - return false; - } - - unsigned protection = (header >> 16) & 1; - - unsigned bitrate_index = (header >> 12) & 0x0f; - - if (bitrate_index == 0 || bitrate_index == 0x0f) { - // Disallow "free" bitrate. - return false; - } - - unsigned sampling_rate_index = (header >> 10) & 3; - - if (sampling_rate_index == 3) { - return false; - } - - static const int kSamplingRateV1[] = { 44100, 48000, 32000 }; - int sampling_rate = kSamplingRateV1[sampling_rate_index]; - if (version == 2 /* V2 */) { - sampling_rate /= 2; - } else if (version == 0 /* V2.5 */) { - sampling_rate /= 4; - } - - unsigned padding = (header >> 9) & 1; - - if (layer == 3) { - // layer I - - static const int kBitrateV1[] = { - 32, 64, 96, 128, 160, 192, 224, 256, - 288, 320, 352, 384, 416, 448 - }; - - static const int kBitrateV2[] = { - 32, 48, 56, 64, 80, 96, 112, 128, - 144, 160, 176, 192, 224, 256 - }; - - int bitrate = - (version == 3 /* V1 */) - ? kBitrateV1[bitrate_index - 1] - : kBitrateV2[bitrate_index - 1]; - - if (out_bitrate) { - *out_bitrate = bitrate; - } - - *frame_size = (12000 * bitrate / sampling_rate + padding) * 4; - - if (out_num_samples) { - *out_num_samples = 384; - } - } else { - // layer II or III - - static const int kBitrateV1L2[] = { - 32, 48, 56, 64, 80, 96, 112, 128, - 160, 192, 224, 256, 320, 384 - }; - - static const int kBitrateV1L3[] = { - 32, 40, 48, 56, 64, 80, 96, 112, - 128, 160, 192, 224, 256, 320 - }; - - static const int kBitrateV2[] = { - 8, 16, 24, 32, 40, 48, 56, 64, - 80, 96, 112, 128, 144, 160 - }; - - int bitrate; - if (version == 3 /* V1 */) { - bitrate = (layer == 2 /* L2 */) - ? kBitrateV1L2[bitrate_index - 1] - : kBitrateV1L3[bitrate_index - 1]; - - if (out_num_samples) { - *out_num_samples = 1152; - } - } else { - // V2 (or 2.5) - - bitrate = kBitrateV2[bitrate_index - 1]; - if (out_num_samples) { - *out_num_samples = 576; - } - } - - if (out_bitrate) { - *out_bitrate = bitrate; - } - - if (version == 3 /* V1 */) { - *frame_size = 144000 * bitrate / sampling_rate + padding; - } else { - // V2 or V2.5 - *frame_size = 72000 * bitrate / sampling_rate + padding; - } - } - - if (out_sampling_rate) { - *out_sampling_rate = sampling_rate; - } - - if (out_channels) { - int channel_mode = (header >> 6) & 3; - - *out_channels = (channel_mode == 3) ? 1 : 2; - } - - return true; -} - static bool Resync( const sp &source, uint32_t match_header, off64_t *inout_pos, off64_t *post_id3_pos, uint32_t *out_header) { @@ -297,7 +146,7 @@ static bool Resync( size_t frame_size; int sample_rate, num_channels, bitrate; - if (!MP3Extractor::get_mp3_frame_size( + if (!GetMPEGAudioFrameSize( header, &frame_size, &sample_rate, &num_channels, &bitrate)) { ++pos; @@ -331,7 +180,7 @@ static bool Resync( } size_t test_frame_size; - if (!MP3Extractor::get_mp3_frame_size( + if (!GetMPEGAudioFrameSize( test_header, &test_frame_size)) { valid = false; break; @@ -437,7 +286,7 @@ MP3Extractor::MP3Extractor( int sample_rate; int num_channels; int bitrate; - get_mp3_frame_size( + GetMPEGAudioFrameSize( header, &frame_size, &sample_rate, &num_channels, &bitrate); mMeta = new MetaData; @@ -602,8 +451,9 @@ status_t MP3Source::read( uint32_t header = U32_AT((const uint8_t *)buffer->data()); if ((header & kMask) == (mFixedHeader & kMask) - && MP3Extractor::get_mp3_frame_size( - header, &frame_size, &sample_rate, NULL, &bitrate, &num_samples)) { + && GetMPEGAudioFrameSize( + header, &frame_size, &sample_rate, NULL, + &bitrate, &num_samples)) { break; } diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 8cd08bc..01f1fba 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -24,6 +24,7 @@ const char *MEDIA_MIMETYPE_VIDEO_VPX = "video/x-vnd.on2.vp8"; const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp"; +const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw"; const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index ba495cc..6339710 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -163,7 +163,6 @@ static sp InstantiateSoftwareCodec( static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" }, -// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" }, // { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" }, { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" }, { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" }, @@ -207,6 +206,7 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" }, { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" }, { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" }, + { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.Nvidia.mpeg2v.decode" }, }; static const CodecInfo kEncoderInfo[] = { @@ -534,6 +534,10 @@ sp OMXCodec::Create( err = codec->configureCodec(meta, flags); if (err == OK) { + if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) { + codec->mOnlySubmitOneBufferAtOneTime = true; + } + return codec; } @@ -1357,6 +1361,8 @@ status_t OMXCodec::setVideoOutputFormat( compressionFormat = OMX_VIDEO_CodingH263; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { compressionFormat = OMX_VIDEO_CodingVPX; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { + compressionFormat = OMX_VIDEO_CodingMPEG2; } else { LOGE("Not a supported video mime type: %s", mime); CHECK(!"Should not be here. Not a supported video mime type."); diff --git a/media/libstagefright/VBRISeeker.cpp b/media/libstagefright/VBRISeeker.cpp index 48bddc2..6f968be 100644 --- a/media/libstagefright/VBRISeeker.cpp +++ b/media/libstagefright/VBRISeeker.cpp @@ -20,6 +20,7 @@ #include "include/VBRISeeker.h" +#include "include/avc_utils.h" #include "include/MP3Extractor.h" #include @@ -46,7 +47,7 @@ sp VBRISeeker::CreateFromSource( uint32_t tmp = U32_AT(&header[0]); size_t frameSize; int sampleRate; - if (!MP3Extractor::get_mp3_frame_size(tmp, &frameSize, &sampleRate)) { + if (!GetMPEGAudioFrameSize(tmp, &frameSize, &sampleRate)) { return NULL; } diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 95cf2d3..020e947 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -376,5 +376,236 @@ sp MakeAACCodecSpecificData( return meta; } +bool ExtractDimensionsFromVOLHeader( + const uint8_t *data, size_t size, int32_t *width, int32_t *height) { + ABitReader br(&data[4], size - 4); + br.skipBits(1); // random_accessible_vol + unsigned video_object_type_indication = br.getBits(8); + + CHECK_NE(video_object_type_indication, + 0x21u /* Fine Granularity Scalable */); + + unsigned video_object_layer_verid; + unsigned video_object_layer_priority; + if (br.getBits(1)) { + video_object_layer_verid = br.getBits(4); + video_object_layer_priority = br.getBits(3); + } + unsigned aspect_ratio_info = br.getBits(4); + if (aspect_ratio_info == 0x0f /* 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 + CHECK(br.getBits(1)); // marker_bit + br.skipBits(15); // latter_half_bit_rate + CHECK(br.getBits(1)); // marker_bit + br.skipBits(15); // first_half_vbv_buffer_size + CHECK(br.getBits(1)); // marker_bit + br.skipBits(3); // latter_half_vbv_buffer_size + br.skipBits(11); // first_half_vbv_occupancy + CHECK(br.getBits(1)); // marker_bit + br.skipBits(15); // latter_half_vbv_occupancy + CHECK(br.getBits(1)); // marker_bit + } + } + unsigned video_object_layer_shape = br.getBits(2); + CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */); + + CHECK(br.getBits(1)); // marker_bit + unsigned vop_time_increment_resolution = br.getBits(16); + CHECK(br.getBits(1)); // marker_bit + + if (br.getBits(1)) { // fixed_vop_rate + // range [0..vop_time_increment_resolution) + + // vop_time_increment_resolution + // 2 => 0..1, 1 bit + // 3 => 0..2, 2 bits + // 4 => 0..3, 2 bits + // 5 => 0..4, 3 bits + // ... + + CHECK_GT(vop_time_increment_resolution, 0u); + --vop_time_increment_resolution; + + unsigned numBits = 0; + while (vop_time_increment_resolution > 0) { + ++numBits; + vop_time_increment_resolution >>= 1; + } + + br.skipBits(numBits); // fixed_vop_time_increment + } + + CHECK(br.getBits(1)); // marker_bit + unsigned video_object_layer_width = br.getBits(13); + CHECK(br.getBits(1)); // marker_bit + unsigned video_object_layer_height = br.getBits(13); + CHECK(br.getBits(1)); // marker_bit + + unsigned interlaced = br.getBits(1); + + *width = video_object_layer_width; + *height = video_object_layer_height; + + return true; +} + +bool GetMPEGAudioFrameSize( + uint32_t header, size_t *frame_size, + int *out_sampling_rate, int *out_channels, + int *out_bitrate, int *out_num_samples) { + *frame_size = 0; + + if (out_sampling_rate) { + *out_sampling_rate = 0; + } + + if (out_channels) { + *out_channels = 0; + } + + if (out_bitrate) { + *out_bitrate = 0; + } + + if (out_num_samples) { + *out_num_samples = 1152; + } + + if ((header & 0xffe00000) != 0xffe00000) { + return false; + } + + unsigned version = (header >> 19) & 3; + + if (version == 0x01) { + return false; + } + + unsigned layer = (header >> 17) & 3; + + if (layer == 0x00) { + return false; + } + + unsigned protection = (header >> 16) & 1; + + unsigned bitrate_index = (header >> 12) & 0x0f; + + if (bitrate_index == 0 || bitrate_index == 0x0f) { + // Disallow "free" bitrate. + return false; + } + + unsigned sampling_rate_index = (header >> 10) & 3; + + if (sampling_rate_index == 3) { + return false; + } + + static const int kSamplingRateV1[] = { 44100, 48000, 32000 }; + int sampling_rate = kSamplingRateV1[sampling_rate_index]; + if (version == 2 /* V2 */) { + sampling_rate /= 2; + } else if (version == 0 /* V2.5 */) { + sampling_rate /= 4; + } + + unsigned padding = (header >> 9) & 1; + + if (layer == 3) { + // layer I + + static const int kBitrateV1[] = { + 32, 64, 96, 128, 160, 192, 224, 256, + 288, 320, 352, 384, 416, 448 + }; + + static const int kBitrateV2[] = { + 32, 48, 56, 64, 80, 96, 112, 128, + 144, 160, 176, 192, 224, 256 + }; + + int bitrate = + (version == 3 /* V1 */) + ? kBitrateV1[bitrate_index - 1] + : kBitrateV2[bitrate_index - 1]; + + if (out_bitrate) { + *out_bitrate = bitrate; + } + + *frame_size = (12000 * bitrate / sampling_rate + padding) * 4; + + if (out_num_samples) { + *out_num_samples = 384; + } + } else { + // layer II or III + + static const int kBitrateV1L2[] = { + 32, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384 + }; + + static const int kBitrateV1L3[] = { + 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320 + }; + + static const int kBitrateV2[] = { + 8, 16, 24, 32, 40, 48, 56, 64, + 80, 96, 112, 128, 144, 160 + }; + + int bitrate; + if (version == 3 /* V1 */) { + bitrate = (layer == 2 /* L2 */) + ? kBitrateV1L2[bitrate_index - 1] + : kBitrateV1L3[bitrate_index - 1]; + + if (out_num_samples) { + *out_num_samples = 1152; + } + } else { + // V2 (or 2.5) + + bitrate = kBitrateV2[bitrate_index - 1]; + if (out_num_samples) { + *out_num_samples = 576; + } + } + + if (out_bitrate) { + *out_bitrate = bitrate; + } + + if (version == 3 /* V1 */) { + *frame_size = 144000 * bitrate / sampling_rate + padding; + } else { + // V2 or V2.5 + *frame_size = 72000 * bitrate / sampling_rate + padding; + } + } + + if (out_sampling_rate) { + *out_sampling_rate = sampling_rate; + } + + if (out_channels) { + int channel_mode = (header >> 6) & 3; + + *out_channels = (channel_mode == 3) ? 1 : 2; + } + + return true; +} + } // namespace android diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp index f6770b0..066c88e 100644 --- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -223,6 +223,7 @@ void SoftMP3::onQueueFilled(OMX_U32 portIndex) { if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR || mConfig->outputFrameSize == 0) { + LOGE("mp3 decoder returned error %d", decoderErr); if (mConfig->outputFrameSize == 0) { LOGE("Output frame size is 0"); diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h index cf1146b..c83d9e8 100644 --- a/media/libstagefright/include/MP3Extractor.h +++ b/media/libstagefright/include/MP3Extractor.h @@ -39,11 +39,6 @@ public: virtual sp getMetaData(); - static bool get_mp3_frame_size( - uint32_t header, size_t *frame_size, - int *out_sampling_rate = NULL, int *out_channels = NULL, - int *out_bitrate = NULL, int *out_num_samples = NULL); - private: status_t mInitCheck; diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index afff824..15cd4d4 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -57,6 +57,16 @@ sp MakeAACCodecSpecificData( unsigned profile, unsigned sampling_freq_index, unsigned channel_configuration); +// Given an MPEG4 video VOL-header chunk (starting with 0x00 0x00 0x01 0x2?) +// parse it and fill in dimensions, returns true iff successful. +bool ExtractDimensionsFromVOLHeader( + const uint8_t *data, size_t size, int32_t *width, int32_t *height); + +bool GetMPEGAudioFrameSize( + uint32_t header, size_t *frame_size, + int *out_sampling_rate = NULL, int *out_channels = NULL, + int *out_bitrate = NULL, int *out_num_samples = NULL); + } // namespace android #endif // AVC_UTILS_H_ diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 7d4bc6e..5bbc2b4 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -44,7 +44,7 @@ namespace android { static const size_t kTSPacketSize = 188; struct ATSParser::Program : public RefBase { - Program(ATSParser *parser, unsigned programMapPID); + Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID); bool parsePID( unsigned pid, unsigned payload_unit_start_indicator, @@ -63,8 +63,15 @@ struct ATSParser::Program : public RefBase { return mFirstPTSValid; } + unsigned number() const { return mProgramNumber; } + + void updateProgramMapPID(unsigned programMapPID) { + mProgramMapPID = programMapPID; + } + private: ATSParser *mParser; + unsigned mProgramNumber; unsigned mProgramMapPID; KeyedVector > mStreams; bool mFirstPTSValid; @@ -107,7 +114,7 @@ private: DiscontinuityType mPendingDiscontinuity; sp mPendingDiscontinuityExtra; - ElementaryStreamQueue mQueue; + ElementaryStreamQueue *mQueue; void flush(); void parsePES(ABitReader *br); @@ -126,11 +133,14 @@ private: //////////////////////////////////////////////////////////////////////////////// -ATSParser::Program::Program(ATSParser *parser, unsigned programMapPID) +ATSParser::Program::Program( + ATSParser *parser, unsigned programNumber, unsigned programMapPID) : mParser(parser), + mProgramNumber(programNumber), mProgramMapPID(programMapPID), mFirstPTSValid(false), mFirstPTS(0) { + LOGV("new program number %u", programNumber); } bool ATSParser::Program::parsePID( @@ -299,7 +309,7 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) { } sp ATSParser::Program::getSource(SourceType type) { - size_t index = (type == MPEG2ADTS_AUDIO) ? 0 : 0; + size_t index = (type == AUDIO) ? 0 : 0; for (size_t i = 0; i < mStreams.size(); ++i) { sp source = mStreams.editValueAt(i)->getSource(type); @@ -338,14 +348,43 @@ ATSParser::Stream::Stream( mBuffer(new ABuffer(192 * 1024)), mPayloadStarted(false), mPendingDiscontinuity(DISCONTINUITY_NONE), - mQueue(streamType == 0x1b - ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) { + mQueue(NULL) { mBuffer->setRange(0, 0); + switch (mStreamType) { + case STREAMTYPE_H264: + mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264); + break; + case STREAMTYPE_MPEG2_AUDIO_ATDS: + mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC); + break; + case STREAMTYPE_MPEG1_AUDIO: + case STREAMTYPE_MPEG2_AUDIO: + mQueue = new ElementaryStreamQueue( + ElementaryStreamQueue::MPEG_AUDIO); + break; + + case STREAMTYPE_MPEG1_VIDEO: + case STREAMTYPE_MPEG2_VIDEO: + mQueue = new ElementaryStreamQueue( + ElementaryStreamQueue::MPEG_VIDEO); + break; + + case STREAMTYPE_MPEG4_VIDEO: + mQueue = new ElementaryStreamQueue( + ElementaryStreamQueue::MPEG4_VIDEO); + break; + + default: + break; + } + LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType); } ATSParser::Stream::~Stream() { + delete mQueue; + mQueue = NULL; } void ATSParser::Stream::parse( @@ -397,7 +436,7 @@ void ATSParser::Stream::signalDiscontinuity( { bool isASeek = (type == DISCONTINUITY_SEEK); - mQueue.clear(!isASeek); + mQueue->clear(!isASeek); uint64_t resumeAtPTS; if (extra != NULL @@ -444,6 +483,12 @@ void ATSParser::Stream::parsePES(ABitReader *br) { LOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix); + if (packet_startcode_prefix != 1) { + LOGV("Supposedly payload_unit_start=1 unit does not start " + "with startcode."); + return; + } + CHECK_EQ(packet_startcode_prefix, 0x000001u); unsigned stream_id = br->getBits(8); @@ -611,22 +656,28 @@ void ATSParser::Stream::onPayloadData( const uint8_t *data, size_t size) { LOGV("onPayloadData mStreamType=0x%02x", mStreamType); + if (mQueue == NULL) { + return; + } + CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3); int64_t timeUs = mProgram->convertPTSToTimestamp(PTS); - status_t err = mQueue.appendData(data, size, timeUs); + status_t err = mQueue->appendData(data, size, timeUs); if (err != OK) { return; } sp accessUnit; - while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) { + while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) { if (mSource == NULL) { - sp meta = mQueue.getFormat(); + sp meta = mQueue->getFormat(); if (meta != NULL) { - LOGV("created source!"); + LOGV("Stream PID 0x%08x of type 0x%02x now has data.", + mElementaryPID, mStreamType); + mSource = new AnotherPacketSource(meta); if (mPendingDiscontinuity != DISCONTINUITY_NONE) { @@ -638,13 +689,13 @@ void ATSParser::Stream::onPayloadData( mSource->queueAccessUnit(accessUnit); } - } else if (mQueue.getFormat() != NULL) { + } else if (mQueue->getFormat() != NULL) { // After a discontinuity we invalidate the queue's format // and won't enqueue any access units to the source until // the queue has reestablished the new format. if (mSource->getFormat() == NULL) { - mSource->setFormat(mQueue.getFormat()); + mSource->setFormat(mQueue->getFormat()); } mSource->queueAccessUnit(accessUnit); } @@ -652,9 +703,30 @@ void ATSParser::Stream::onPayloadData( } sp ATSParser::Stream::getSource(SourceType type) { - if ((type == AVC_VIDEO && mStreamType == 0x1b) - || (type == MPEG2ADTS_AUDIO && mStreamType == 0x0f)) { - return mSource; + switch (type) { + case VIDEO: + { + if (mStreamType == STREAMTYPE_H264 + || mStreamType == STREAMTYPE_MPEG1_VIDEO + || mStreamType == STREAMTYPE_MPEG2_VIDEO + || mStreamType == STREAMTYPE_MPEG4_VIDEO) { + return mSource; + } + break; + } + + case AUDIO: + { + if (mStreamType == STREAMTYPE_MPEG1_AUDIO + || mStreamType == STREAMTYPE_MPEG2_AUDIO + || mStreamType == STREAMTYPE_MPEG2_AUDIO_ATDS) { + return mSource; + } + break; + } + + default: + break; } return NULL; @@ -729,7 +801,21 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) { LOGV(" program_map_PID = 0x%04x", programMapPID); - mPrograms.push(new Program(this, programMapPID)); + bool found = false; + for (size_t index = 0; index < mPrograms.size(); ++index) { + const sp &program = mPrograms.itemAt(index); + + if (program->number() == program_number) { + program->updateProgramMapPID(programMapPID); + found = true; + break; + } + } + + if (!found) { + mPrograms.push( + new Program(this, program_number, programMapPID)); + } } } @@ -805,8 +891,16 @@ void ATSParser::parseTS(ABitReader *br) { } sp ATSParser::getSource(SourceType type) { + int which = -1; // any + for (size_t i = 0; i < mPrograms.size(); ++i) { - sp source = mPrograms.editItemAt(i)->getSource(type); + const sp &program = mPrograms.editItemAt(i); + + if (which >= 0 && (int)program->number() != which) { + continue; + } + + sp source = program->getSource(type); if (source != NULL) { return source; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 3936f05..1e6451d 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -48,8 +48,8 @@ struct ATSParser : public RefBase { void signalEOS(status_t finalResult); enum SourceType { - AVC_VIDEO, - MPEG2ADTS_AUDIO + VIDEO, + AUDIO }; sp getSource(SourceType type); @@ -59,6 +59,17 @@ protected: virtual ~ATSParser(); private: + enum { + // From ISO/IEC 13818-1: 2000 (E), Table 2-29 + STREAMTYPE_MPEG1_VIDEO = 0x01, + STREAMTYPE_MPEG2_VIDEO = 0x02, + STREAMTYPE_MPEG1_AUDIO = 0x03, + STREAMTYPE_MPEG2_AUDIO = 0x04, + STREAMTYPE_MPEG2_AUDIO_ATDS = 0x0f, + STREAMTYPE_MPEG4_VIDEO = 0x10, + STREAMTYPE_H264 = 0x1b, + }; + struct Program; struct Stream; diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index dcaf9f7..f8a1d84 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "include/avc_utils.h" @@ -79,11 +80,49 @@ static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { return true; } +static bool IsSeeminglyValidMPEGAudioHeader(const uint8_t *ptr, size_t size) { + if (size < 3) { + // Not enough data to verify header. + return false; + } + + if (ptr[0] != 0xff || (ptr[1] >> 5) != 0x07) { + return false; + } + + unsigned ID = (ptr[1] >> 3) & 3; + + if (ID == 1) { + return false; // reserved + } + + unsigned layer = (ptr[1] >> 1) & 3; + + if (layer == 0) { + return false; // reserved + } + + unsigned bitrateIndex = (ptr[2] >> 4); + + if (bitrateIndex == 0x0f) { + return false; // reserved + } + + unsigned samplingRateIndex = (ptr[2] >> 2) & 3; + + if (samplingRateIndex == 3) { + return false; // reserved + } + + return true; +} + status_t ElementaryStreamQueue::appendData( const void *data, size_t size, int64_t timeUs) { if (mBuffer == NULL || mBuffer->size() == 0) { switch (mMode) { case H264: + case MPEG_VIDEO: { #if 0 if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { @@ -105,7 +144,40 @@ status_t ElementaryStreamQueue::appendData( } if (startOffset > 0) { - LOGI("found something resembling an H.264 syncword at " + LOGI("found something resembling an H.264/MPEG syncword at " + "offset %ld", + startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; +#endif + break; + } + + case MPEG4_VIDEO: + { +#if 0 + if (size < 3 || memcmp("\x00\x00\x01", data, 3)) { + return ERROR_MALFORMED; + } +#else + uint8_t *ptr = (uint8_t *)data; + + ssize_t startOffset = -1; + for (size_t i = 0; i + 2 < size; ++i) { + if (!memcmp("\x00\x00\x01", &ptr[i], 3)) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + LOGI("found something resembling an H.264/MPEG syncword at " "offset %ld", startOffset); } @@ -148,6 +220,33 @@ status_t ElementaryStreamQueue::appendData( break; } + case MPEG_AUDIO: + { + uint8_t *ptr = (uint8_t *)data; + + ssize_t startOffset = -1; + for (size_t i = 0; i < size; ++i) { + if (IsSeeminglyValidMPEGAudioHeader(&ptr[i], size - i)) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + LOGI("found something resembling an MPEG audio " + "syncword at offset %ld", + startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; + break; + } + default: TRESPASS(); break; @@ -190,11 +289,18 @@ status_t ElementaryStreamQueue::appendData( } sp ElementaryStreamQueue::dequeueAccessUnit() { - if (mMode == H264) { - return dequeueAccessUnitH264(); - } else { - CHECK_EQ((unsigned)mMode, (unsigned)AAC); - return dequeueAccessUnitAAC(); + switch (mMode) { + case H264: + return dequeueAccessUnitH264(); + case AAC: + return dequeueAccessUnitAAC(); + case MPEG_VIDEO: + return dequeueAccessUnitMPEGVideo(); + case MPEG4_VIDEO: + return dequeueAccessUnitMPEG4Video(); + default: + CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO); + return dequeueAccessUnitMPEGAudio(); } } @@ -455,4 +561,371 @@ sp ElementaryStreamQueue::dequeueAccessUnitH264() { return NULL; } +sp ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() { + const uint8_t *data = mBuffer->data(); + size_t size = mBuffer->size(); + + if (size < 4) { + return NULL; + } + + uint32_t header = U32_AT(data); + + size_t frameSize; + int samplingRate, numChannels, bitrate, numSamples; + CHECK(GetMPEGAudioFrameSize( + header, &frameSize, &samplingRate, &numChannels, + &bitrate, &numSamples)); + + if (size < frameSize) { + return NULL; + } + + sp accessUnit = new ABuffer(frameSize); + memcpy(accessUnit->data(), data, frameSize); + + memmove(mBuffer->data(), + mBuffer->data() + frameSize, + mBuffer->size() - frameSize); + + mBuffer->setRange(0, mBuffer->size() - frameSize); + + int64_t timeUs = fetchTimestamp(frameSize); + CHECK_GE(timeUs, 0ll); + + accessUnit->meta()->setInt64("timeUs", timeUs); + + if (mFormat == NULL) { + mFormat = new MetaData; + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + mFormat->setInt32(kKeySampleRate, samplingRate); + mFormat->setInt32(kKeyChannelCount, numChannels); + } + + return accessUnit; +} + +static void EncodeSize14(uint8_t **_ptr, size_t size) { + CHECK_LE(size, 0x3fff); + + uint8_t *ptr = *_ptr; + + *ptr++ = 0x80 | (size >> 7); + *ptr++ = size & 0x7f; + + *_ptr = ptr; +} + +static sp MakeMPEGVideoESDS(const sp &csd) { + sp esds = new ABuffer(csd->size() + 25); + + uint8_t *ptr = esds->data(); + *ptr++ = 0x03; + EncodeSize14(&ptr, 22 + csd->size()); + + *ptr++ = 0x00; // ES_ID + *ptr++ = 0x00; + + *ptr++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag + + *ptr++ = 0x04; + EncodeSize14(&ptr, 16 + csd->size()); + + *ptr++ = 0x40; // Audio ISO/IEC 14496-3 + + for (size_t i = 0; i < 12; ++i) { + *ptr++ = 0x00; + } + + *ptr++ = 0x05; + EncodeSize14(&ptr, csd->size()); + + memcpy(ptr, csd->data(), csd->size()); + + return esds; +} + +sp ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() { + const uint8_t *data = mBuffer->data(); + size_t size = mBuffer->size(); + + bool sawPictureStart = false; + int pprevStartCode = -1; + int prevStartCode = -1; + int currentStartCode = -1; + + size_t offset = 0; + while (offset + 3 < size) { + if (memcmp(&data[offset], "\x00\x00\x01", 3)) { + ++offset; + continue; + } + + pprevStartCode = prevStartCode; + prevStartCode = currentStartCode; + currentStartCode = data[offset + 3]; + + if (currentStartCode == 0xb3 && mFormat == NULL) { + memmove(mBuffer->data(), mBuffer->data() + offset, size - offset); + size -= offset; + (void)fetchTimestamp(offset); + offset = 0; + mBuffer->setRange(0, size); + } + + if ((prevStartCode == 0xb3 && currentStartCode != 0xb5) + || (pprevStartCode == 0xb3 && prevStartCode == 0xb5)) { + // seqHeader without/with extension + + if (mFormat == NULL) { + CHECK_GE(size, 7u); + + unsigned width = + (data[4] << 4) | data[5] >> 4; + + unsigned height = + ((data[5] & 0x0f) << 8) | data[6]; + + mFormat = new MetaData; + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); + mFormat->setInt32(kKeyWidth, width); + mFormat->setInt32(kKeyHeight, height); + + LOGI("found MPEG2 video codec config (%d x %d)", width, height); + + sp csd = new ABuffer(offset); + memcpy(csd->data(), data, offset); + + memmove(mBuffer->data(), + mBuffer->data() + offset, + mBuffer->size() - offset); + + mBuffer->setRange(0, mBuffer->size() - offset); + size -= offset; + (void)fetchTimestamp(offset); + offset = 0; + + // hexdump(csd->data(), csd->size()); + + sp esds = MakeMPEGVideoESDS(csd); + mFormat->setData( + kKeyESDS, kTypeESDS, esds->data(), esds->size()); + + return NULL; + } + } + + if (mFormat != NULL && currentStartCode == 0x00) { + // Picture start + + if (!sawPictureStart) { + sawPictureStart = true; + } else { + sp accessUnit = new ABuffer(offset); + memcpy(accessUnit->data(), data, offset); + + memmove(mBuffer->data(), + mBuffer->data() + offset, + mBuffer->size() - offset); + + mBuffer->setRange(0, mBuffer->size() - offset); + + int64_t timeUs = fetchTimestamp(offset); + CHECK_GE(timeUs, 0ll); + + offset = 0; + + accessUnit->meta()->setInt64("timeUs", timeUs); + + LOGV("returning MPEG video access unit at time %lld us", + timeUs); + + // hexdump(accessUnit->data(), accessUnit->size()); + + return accessUnit; + } + } + + ++offset; + } + + return NULL; +} + +static ssize_t getNextChunkSize( + const uint8_t *data, size_t size) { + static const char kStartCode[] = "\x00\x00\x01"; + + if (size < 3) { + return -EAGAIN; + } + + if (memcmp(kStartCode, data, 3)) { + TRESPASS(); + } + + size_t offset = 3; + while (offset + 2 < size) { + if (!memcmp(&data[offset], kStartCode, 3)) { + return offset; + } + + ++offset; + } + + return -EAGAIN; +} + +sp ElementaryStreamQueue::dequeueAccessUnitMPEG4Video() { + uint8_t *data = mBuffer->data(); + size_t size = mBuffer->size(); + + enum { + SKIP_TO_VISUAL_OBJECT_SEQ_START, + EXPECT_VISUAL_OBJECT_START, + EXPECT_VO_START, + EXPECT_VOL_START, + WAIT_FOR_VOP_START, + SKIP_TO_VOP_START, + + } state; + + if (mFormat == NULL) { + state = SKIP_TO_VISUAL_OBJECT_SEQ_START; + } else { + state = SKIP_TO_VOP_START; + } + + int32_t width = -1, height = -1; + + size_t offset = 0; + ssize_t chunkSize; + while ((chunkSize = getNextChunkSize( + &data[offset], size - offset)) > 0) { + bool discard = false; + + unsigned chunkType = data[offset + 3]; + + switch (state) { + case SKIP_TO_VISUAL_OBJECT_SEQ_START: + { + if (chunkType == 0xb0) { + // Discard anything before this marker. + + state = EXPECT_VISUAL_OBJECT_START; + } else { + discard = true; + } + break; + } + + case EXPECT_VISUAL_OBJECT_START: + { + CHECK_EQ(chunkType, 0xb5); + state = EXPECT_VO_START; + break; + } + + case EXPECT_VO_START: + { + CHECK_LE(chunkType, 0x1f); + state = EXPECT_VOL_START; + break; + } + + case EXPECT_VOL_START: + { + CHECK((chunkType & 0xf0) == 0x20); + + CHECK(ExtractDimensionsFromVOLHeader( + &data[offset], chunkSize, + &width, &height)); + + state = WAIT_FOR_VOP_START; + break; + } + + case WAIT_FOR_VOP_START: + { + if (chunkType == 0xb3 || chunkType == 0xb6) { + // group of VOP or VOP start. + + mFormat = new MetaData; + mFormat->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + + mFormat->setInt32(kKeyWidth, width); + mFormat->setInt32(kKeyHeight, height); + + LOGI("found MPEG4 video codec config (%d x %d)", + width, height); + + sp csd = new ABuffer(offset); + memcpy(csd->data(), data, offset); + + // hexdump(csd->data(), csd->size()); + + sp esds = MakeMPEGVideoESDS(csd); + mFormat->setData( + kKeyESDS, kTypeESDS, + esds->data(), esds->size()); + + discard = true; + state = SKIP_TO_VOP_START; + } + + break; + } + + case SKIP_TO_VOP_START: + { + if (chunkType == 0xb6) { + offset += chunkSize; + + sp accessUnit = new ABuffer(offset); + memcpy(accessUnit->data(), data, offset); + + memmove(data, &data[offset], size - offset); + size -= offset; + mBuffer->setRange(0, size); + + int64_t timeUs = fetchTimestamp(offset); + CHECK_GE(timeUs, 0ll); + + offset = 0; + + accessUnit->meta()->setInt64("timeUs", timeUs); + + LOGV("returning MPEG4 video access unit at time %lld us", + timeUs); + + // hexdump(accessUnit->data(), accessUnit->size()); + + return accessUnit; + } else if (chunkType != 0xb3) { + offset += chunkSize; + discard = true; + } + + break; + } + + default: + TRESPASS(); + } + + if (discard) { + (void)fetchTimestamp(offset); + memmove(data, &data[offset], size - offset); + size -= offset; + offset = 0; + mBuffer->setRange(0, size); + } else { + offset += chunkSize; + } + } + + return NULL; +} + } // namespace android diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index 153cfe6..4035ed3 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -31,7 +31,10 @@ struct MetaData; struct ElementaryStreamQueue { enum Mode { H264, - AAC + AAC, + MPEG_AUDIO, + MPEG_VIDEO, + MPEG4_VIDEO, }; ElementaryStreamQueue(Mode mode); @@ -57,6 +60,9 @@ private: sp dequeueAccessUnitH264(); sp dequeueAccessUnitAAC(); + sp dequeueAccessUnitMPEGAudio(); + sp dequeueAccessUnitMPEGVideo(); + sp dequeueAccessUnitMPEG4Video(); // consume a logical (compressed) access unit of size "size", // returns its timestamp in us (or -1 if no time information). diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index dfec47f..8250ad1 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -175,7 +175,7 @@ void MPEG2TSExtractor::init() { if (!haveVideo) { sp impl = (AnotherPacketSource *)mParser->getSource( - ATSParser::AVC_VIDEO).get(); + ATSParser::VIDEO).get(); if (impl != NULL) { haveVideo = true; @@ -186,7 +186,7 @@ void MPEG2TSExtractor::init() { if (!haveAudio) { sp impl = (AnotherPacketSource *)mParser->getSource( - ATSParser::MPEG2ADTS_AUDIO).get(); + ATSParser::AUDIO).get(); if (impl != NULL) { haveAudio = true; @@ -194,7 +194,7 @@ void MPEG2TSExtractor::init() { } } - if (++numPacketsParsed > 2500) { + if (++numPacketsParsed > 10000) { break; } } diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index 6819fef..a02591f 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -329,7 +329,7 @@ static uint8_t *EncodeSize(uint8_t *dst, size_t x) { return dst; } -static bool ExtractDimensionsFromVOLHeader( +static bool ExtractDimensionsMPEG4Config( const sp &config, int32_t *width, int32_t *height) { *width = 0; *height = 0; @@ -352,87 +352,11 @@ static bool ExtractDimensionsFromVOLHeader( return false; } - ABitReader br(&ptr[offset + 4], config->size() - offset - 4); - br.skipBits(1); // random_accessible_vol - unsigned video_object_type_indication = br.getBits(8); - - CHECK_NE(video_object_type_indication, - 0x21u /* Fine Granularity Scalable */); - - unsigned video_object_layer_verid; - unsigned video_object_layer_priority; - if (br.getBits(1)) { - video_object_layer_verid = br.getBits(4); - video_object_layer_priority = br.getBits(3); - } - unsigned aspect_ratio_info = br.getBits(4); - if (aspect_ratio_info == 0x0f /* 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 - CHECK(br.getBits(1)); // marker_bit - br.skipBits(15); // latter_half_bit_rate - CHECK(br.getBits(1)); // marker_bit - br.skipBits(15); // first_half_vbv_buffer_size - CHECK(br.getBits(1)); // marker_bit - br.skipBits(3); // latter_half_vbv_buffer_size - br.skipBits(11); // first_half_vbv_occupancy - CHECK(br.getBits(1)); // marker_bit - br.skipBits(15); // latter_half_vbv_occupancy - CHECK(br.getBits(1)); // marker_bit - } - } - unsigned video_object_layer_shape = br.getBits(2); - CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */); - - CHECK(br.getBits(1)); // marker_bit - unsigned vop_time_increment_resolution = br.getBits(16); - CHECK(br.getBits(1)); // marker_bit - - if (br.getBits(1)) { // fixed_vop_rate - // range [0..vop_time_increment_resolution) - - // vop_time_increment_resolution - // 2 => 0..1, 1 bit - // 3 => 0..2, 2 bits - // 4 => 0..3, 2 bits - // 5 => 0..4, 3 bits - // ... - - CHECK_GT(vop_time_increment_resolution, 0u); - --vop_time_increment_resolution; - - unsigned numBits = 0; - while (vop_time_increment_resolution > 0) { - ++numBits; - vop_time_increment_resolution >>= 1; - } - - br.skipBits(numBits); // fixed_vop_time_increment - } - - CHECK(br.getBits(1)); // marker_bit - unsigned video_object_layer_width = br.getBits(13); - CHECK(br.getBits(1)); // marker_bit - unsigned video_object_layer_height = br.getBits(13); - CHECK(br.getBits(1)); // marker_bit - - unsigned interlaced = br.getBits(1); - - *width = video_object_layer_width; - *height = video_object_layer_height; - - LOGI("VOL dimensions = %dx%d", *width, *height); - - return true; + return ExtractDimensionsFromVOLHeader( + &ptr[offset], config->size() - offset, width, height); } -sp MakeMPEG4VideoCodecSpecificData( +static sp MakeMPEG4VideoCodecSpecificData( const char *params, int32_t *width, int32_t *height) { *width = 0; *height = 0; @@ -443,10 +367,12 @@ sp MakeMPEG4VideoCodecSpecificData( sp config = decodeHex(val); CHECK(config != NULL); - if (!ExtractDimensionsFromVOLHeader(config, width, height)) { + if (!ExtractDimensionsMPEG4Config(config, width, height)) { return NULL; } + LOGI("VOL dimensions = %dx%d", *width, *height); + size_t len1 = config->size() + GetSizeWidth(config->size()) + 1; size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13; size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3; -- cgit v1.1