diff options
-rw-r--r-- | media/base/android/java/src/org/chromium/media/MediaCodecBridge.java | 5 | ||||
-rw-r--r-- | media/base/android/media_codec_bridge.cc | 148 | ||||
-rw-r--r-- | media/base/android/media_codec_bridge.h | 4 | ||||
-rw-r--r-- | media/mp4/aac.cc | 3 | ||||
-rw-r--r-- | media/mp4/aac.h | 12 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.cc | 12 |
6 files changed, 136 insertions, 48 deletions
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java index 399ae27..ac3a3bf 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java @@ -193,6 +193,11 @@ class MediaCodecBridge { } @CalledByNative + private static void setFrameHasADTSHeader(MediaFormat format) { + format.setInteger(MediaFormat.KEY_IS_ADTS, 1); + } + + @CalledByNative private void configureAudio(MediaFormat format, MediaCrypto crypto, int flags, boolean playAudio) { mMediaCodec.configure(format, null, crypto, flags); diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc index 2587fc4..d64528d 100644 --- a/media/base/android/media_codec_bridge.cc +++ b/media/base/android/media_codec_bridge.cc @@ -15,8 +15,8 @@ #include "base/logging.h" #include "base/safe_numerics.h" #include "base/stringprintf.h" - #include "jni/MediaCodecBridge_jni.h" +#include "media/base/bit_reader.h" using base::android::AttachCurrentThread; using base::android::ConvertUTF8ToJavaString; @@ -83,6 +83,7 @@ MediaCodecBridge::~MediaCodecBridge() { void MediaCodecBridge::StartInternal() { JNIEnv* env = AttachCurrentThread(); Java_MediaCodecBridge_start(env, j_media_codec_.obj()); + GetOutputBuffers(); } void MediaCodecBridge::Reset() { @@ -193,57 +194,116 @@ bool AudioCodecBridge::Start( env, j_mime.obj(), sample_rate, channel_count)); DCHECK(!j_format.is_null()); - if (extra_data_size > 0) { - DCHECK_EQ(kCodecVorbis, codec); - if (extra_data[0] != 2) { - LOG(ERROR) << "Invalid number of headers before the codec header: " - << extra_data[0]; - return false; - } + if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size)) + return false; + + Java_MediaCodecBridge_configureAudio( + env, media_codec(), j_format.obj(), NULL, 0, play_audio); + StartInternal(); + return true; +} + +bool AudioCodecBridge::ConfigureMediaFormat( + jobject j_format, const AudioCodec codec, const uint8* extra_data, + size_t extra_data_size) { + if (extra_data_size == 0) + return true; + + JNIEnv* env = AttachCurrentThread(); + switch(codec) { + case kCodecVorbis: + { + if (extra_data[0] != 2) { + LOG(ERROR) << "Invalid number of vorbis headers before the codec " + << "header: " << extra_data[0]; + return false; + } - size_t header_length[2]; - // |total_length| keeps track of the total number of bytes before the last - // header. - size_t total_length = 1; - const uint8* current_pos = extra_data; - // Calculate the length of the first 2 headers. - for (int i = 0; i < 2; ++i) { - header_length[i] = 0; - while (total_length < extra_data_size) { - size_t size = *(++current_pos); - total_length += 1 + size; - if (total_length > 0x80000000) { - LOG(ERROR) << "Header size too large"; + size_t header_length[2]; + // |total_length| keeps track of the total number of bytes before the last + // header. + size_t total_length = 1; + const uint8* current_pos = extra_data; + // Calculate the length of the first 2 headers. + for (int i = 0; i < 2; ++i) { + header_length[i] = 0; + while (total_length < extra_data_size) { + size_t size = *(++current_pos); + total_length += 1 + size; + if (total_length > 0x80000000) { + LOG(ERROR) << "Vorbis header size too large"; + return false; + } + header_length[i] += size; + if (size < 0xFF) + break; + } + if (total_length >= extra_data_size) { + LOG(ERROR) << "Invalid vorbis header size in the extra data"; return false; } - header_length[i] += size; - if (size < 0xFF) - break; } - if (total_length >= extra_data_size) { - LOG(ERROR) << "Invalid header size in the extra data"; + current_pos++; + // The first header is identification header. + jobject identification_header = env->NewDirectByteBuffer( + const_cast<uint8*>(current_pos), header_length[0]); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 0, identification_header); + // The last header is codec header. + jobject codec_header = env->NewDirectByteBuffer( + const_cast<uint8*>(extra_data + total_length), + extra_data_size - total_length); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 1, codec_header); + env->DeleteLocalRef(codec_header); + env->DeleteLocalRef(identification_header); + break; + } + case kCodecAAC: + { + media::BitReader reader(extra_data, extra_data_size); + + // The following code is copied from aac.cc + // TODO(qinmin): refactor the code in aac.cc to make it more reusable. + uint8 profile = 0; + uint8 frequency_index = 0; + uint8 channel_config = 0; + if (!reader.ReadBits(5, &profile) || + !reader.ReadBits(4, &frequency_index)) { + LOG(ERROR) << "Unable to parse AAC header"; + return false; + } + if (0xf == frequency_index && !reader.SkipBits(24)) { + LOG(ERROR) << "Unable to parse AAC header"; return false; } + if (!reader.ReadBits(4, &channel_config)) { + LOG(ERROR) << "Unable to parse AAC header"; + return false; + } + + if (profile < 1 || profile > 4 || frequency_index == 0xf || + channel_config > 7) { + LOG(ERROR) << "Invalid AAC header"; + return false; + } + uint8 csd[2]; + csd[0] = profile << 3 | frequency_index >> 1; + csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3; + jobject header = env->NewDirectByteBuffer(csd, 2); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 0, header); + // TODO(qinmin): pass an extra variable to this function to determine + // whether we need to call this. + Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format); + env->DeleteLocalRef(header); + break; } - current_pos++; - // The first header is identification header. - jobject identification_header = env->NewDirectByteBuffer( - const_cast<uint8*>(current_pos), header_length[0]); - Java_MediaCodecBridge_setCodecSpecificData( - env, j_format.obj(), 0, identification_header); - // The last header is codec header. - jobject codec_header = env->NewDirectByteBuffer( - const_cast<uint8*>(extra_data + total_length), - extra_data_size - total_length); - Java_MediaCodecBridge_setCodecSpecificData( - env, j_format.obj(), 1, codec_header); - env->DeleteLocalRef(codec_header); - env->DeleteLocalRef(identification_header); + default: + LOG(ERROR) << "Invalid header encountered for codec: " + << AudioCodecToMimeType(codec); + return false; } - - Java_MediaCodecBridge_configureAudio( - env, media_codec(), j_format.obj(), NULL, 0, play_audio); - StartInternal(); return true; } diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h index b8a30a8..74e2d67 100644 --- a/media/base/android/media_codec_bridge.h +++ b/media/base/android/media_codec_bridge.h @@ -121,6 +121,10 @@ class AudioCodecBridge : public MediaCodecBridge { private: explicit AudioCodecBridge(const char* mime); + + // Configure the java MediaFormat object with the extra codec data passed in. + bool ConfigureMediaFormat(jobject j_format, const AudioCodec codec, + const uint8* extra_data, size_t extra_data_size); }; class VideoCodecBridge : public MediaCodecBridge { diff --git a/media/mp4/aac.cc b/media/mp4/aac.cc index f92dd2f..6604c50 100644 --- a/media/mp4/aac.cc +++ b/media/mp4/aac.cc @@ -53,6 +53,9 @@ AAC::~AAC() { } bool AAC::Parse(const std::vector<uint8>& data) { +#if defined(OS_ANDROID) + codec_specific_data_ = data; +#endif if (data.empty()) return false; diff --git a/media/mp4/aac.h b/media/mp4/aac.h index 2d4b6f4..1a546b7 100644 --- a/media/mp4/aac.h +++ b/media/mp4/aac.h @@ -52,6 +52,13 @@ class MEDIA_EXPORT AAC { // unchanged. bool ConvertEsdsToADTS(std::vector<uint8>* buffer) const; +#if defined(OS_ANDROID) + // Returns the codec specific data needed by android MediaCodec. + std::vector<uint8> codec_specific_data() const { + return codec_specific_data_; + } +#endif + // Size in bytes of the ADTS header added by ConvertEsdsToADTS(). static const size_t kADTSHeaderSize = 7; @@ -66,6 +73,11 @@ class MEDIA_EXPORT AAC { uint8 frequency_index_; uint8 channel_config_; +#if defined(OS_ANDROID) + // The codec specific data needed by the android MediaCodec. + std::vector<uint8> codec_specific_data_; +#endif + // The following variables store audio configuration information that // can be used by Chromium. They are based on the AAC specific // configuration but can be overridden by extensions in elementary diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc index 77fa2b8..9f808ea 100644 --- a/media/mp4/mp4_stream_parser.cc +++ b/media/mp4/mp4_stream_parser.cc @@ -222,12 +222,16 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { AudioCodec codec = kUnknownAudioCodec; ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; int sample_per_second = 0; + std::vector<uint8> extra_data; // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or // supported MPEG2 AAC varients. if (ESDescriptor::IsAAC(audio_type)) { codec = kCodecAAC; channel_layout = aac.GetChannelLayout(has_sbr_); sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); +#if defined(OS_ANDROID) + extra_data = aac.codec_specific_data(); +#endif } else if (audio_type == kEAC3) { codec = kCodecEAC3; channel_layout = GuessChannelLayout(entry.channelcount); @@ -252,10 +256,10 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { is_audio_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; - audio_config.Initialize(codec, sample_format, - channel_layout, - sample_per_second, - NULL, 0, is_audio_track_encrypted_, false); + audio_config.Initialize( + codec, sample_format, channel_layout, sample_per_second, + extra_data.size() ? &extra_data[0] : NULL, extra_data.size(), + is_audio_track_encrypted_, false); has_audio_ = true; audio_track_id_ = track->header.track_id; } |