diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-05 02:21:25 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-05 02:21:25 +0000 |
commit | 00a5890250f45236a8373f6ec033bd7fff3c69b3 (patch) | |
tree | 212ccd2d43e159dfa80a3a479b1780ff72dc8c79 | |
parent | b5449c06d9457e1e1b2a4425fe115d439627f512 (diff) | |
download | chromium_src-00a5890250f45236a8373f6ec033bd7fff3c69b3.zip chromium_src-00a5890250f45236a8373f6ec033bd7fff3c69b3.tar.gz chromium_src-00a5890250f45236a8373f6ec033bd7fff3c69b3.tar.bz2 |
EME: Handle NO_KEY and resume playback after key is added.
- Now we can notify demuxer ready as soon as the demuxer is ready. We don't need
to wait for a key to be added if the stream is encrypted.
- QueueSeureInputBuffer() et al return MediaCodecStatus to pass detailed status.
- If no key is available the media pipeline pauses.
- After a key is added we try to resume the media pipeline playback.
BUG=277211,255781,281663
TEST=Start decoding without key is added and see no crash. After key is added
playback resumes.
R=acolwell@chromium.org, jschuh@chromium.org, qinmin@chromium.org
Review URL: https://codereview.chromium.org/23545029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221331 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 238 insertions, 113 deletions
diff --git a/content/browser/android/browser_media_player_manager.cc b/content/browser/android/browser_media_player_manager.cc index ec5c2b4..daba884 100644 --- a/content/browser/android/browser_media_player_manager.cc +++ b/content/browser/android/browser_media_player_manager.cc @@ -474,10 +474,17 @@ void BrowserMediaPlayerManager::OnAddKey(int media_keys_id, const std::vector<uint8>& init_data, const std::string& session_id) { MediaDrmBridge* drm_bridge = GetDrmBridge(media_keys_id); - if (drm_bridge) { - drm_bridge->AddKey(&key[0], key.size(), &init_data[0], init_data.size(), - session_id); - } + if (!drm_bridge) + return; + + drm_bridge->AddKey(&key[0], key.size(), &init_data[0], init_data.size(), + session_id); + // In EME v0.1b MediaKeys lives in the media element. So the |media_keys_id| + // is the same as the |player_id|. + // TODO(xhwang): Separate |media_keys_id| and |player_id|. + MediaPlayerAndroid* player = GetPlayer(media_keys_id); + if (player) + player->OnKeyAdded(); } void BrowserMediaPlayerManager::OnCancelKeyRequest( diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc index e21c623..b3c6298 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.cc +++ b/content/common/gpu/media/android_video_decode_accelerator.cc @@ -152,7 +152,7 @@ void AndroidVideoDecodeAccelerator::QueueInput() { media::MediaCodecStatus status = media_codec_->DequeueInputBuffer( NoWaitTimeOut(), &input_buf_index); if (status != media::MEDIA_CODEC_OK) { - DCHECK(status == media::MEDIA_CODEC_ENQUEUE_INPUT_AGAIN_LATER || + DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || status == media::MEDIA_CODEC_ERROR); return; } @@ -417,7 +417,7 @@ bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { void AndroidVideoDecodeAccelerator::Reset() { DCHECK(thread_checker_.CalledOnValidThread()); - while(!pending_bitstream_buffers_.empty()) { + while (!pending_bitstream_buffers_.empty()) { media::BitstreamBuffer& bitstream_buffer = pending_bitstream_buffers_.front(); pending_bitstream_buffers_.pop(); diff --git a/content/common/media/media_player_messages_android.h b/content/common/media/media_player_messages_android.h index 5e076f6..3833283 100644 --- a/content/common/media/media_player_messages_android.h +++ b/content/common/media/media_player_messages_android.h @@ -42,7 +42,9 @@ IPC_STRUCT_TRAITS_BEGIN(media::DemuxerConfigs) IPC_STRUCT_TRAITS_MEMBER(video_extra_data) IPC_STRUCT_TRAITS_MEMBER(duration_ms) +#if defined(GOOGLE_TV) IPC_STRUCT_TRAITS_MEMBER(key_system) +#endif // defined(GOOGLE_TV) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(media::DemuxerData) @@ -74,7 +76,7 @@ IPC_MESSAGE_ROUTED2(MediaPlayerMsg_MediaBufferingUpdate, int /* player_id */, int /* percent */) -// A media playback error has occured. +// A media playback error has occurred. IPC_MESSAGE_ROUTED2(MediaPlayerMsg_MediaError, int /* player_id */, int /* error */) @@ -142,7 +144,7 @@ IPC_MESSAGE_ROUTED2(MediaPlayerMsg_ReadFromDemuxer, IPC_MESSAGE_ROUTED1(MediaPlayerMsg_MediaConfigRequest, int /* player_id */) -// Messages for controllering the media playback in browser process ---------- +// Messages for controlling the media playback in browser process ---------- // Destroy the media player object. IPC_MESSAGE_ROUTED1(MediaPlayerHostMsg_DestroyMediaPlayer, diff --git a/content/renderer/media/android/media_source_delegate.cc b/content/renderer/media/android/media_source_delegate.cc index 6271b71..f4299e4 100644 --- a/content/renderer/media/android/media_source_delegate.cc +++ b/content/renderer/media/android/media_source_delegate.cc @@ -4,6 +4,10 @@ #include "content/renderer/media/android/media_source_delegate.h" +#include <limits> +#include <string> +#include <vector> + #include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_number_conversions.h" #include "content/renderer/media/android/webmediaplayer_proxy_android.h" @@ -83,7 +87,9 @@ MediaSourceDelegate::MediaSourceDelegate( video_stream_(NULL), seeking_(false), last_seek_request_id_(0), +#if defined(GOOGLE_TV) key_added_(false), +#endif access_unit_size_(0) { } @@ -632,19 +638,17 @@ void MediaSourceDelegate::OnMediaConfigRequest() { NotifyDemuxerReady(); } -void MediaSourceDelegate::NotifyKeyAdded(const std::string& key_system) { #if defined(GOOGLE_TV) +// TODO(kjyoun): Enhance logic to detect when to call NotifyDemuxerReady() +// For now, we call it when the first key is added. See http://crbug.com/255781 +void MediaSourceDelegate::NotifyKeyAdded(const std::string& key_system) { if (!media_loop_->BelongsToCurrentThread()) { media_loop_->PostTask(FROM_HERE, base::Bind(&MediaSourceDelegate::NotifyKeyAdded, base::Unretained(this), key_system)); return; } -#endif DVLOG(1) << "NotifyKeyAdded() : " << player_id_; - // TODO(kjyoun): Enhance logic to detect when to call NotifyDemuxerReady() - // For now, we calls it when the first key is added. See - // http://crbug.com/255781 if (key_added_) return; key_added_ = true; @@ -654,6 +658,7 @@ void MediaSourceDelegate::NotifyKeyAdded(const std::string& key_system) { if (HasEncryptedStream()) NotifyDemuxerReady(); } +#endif // defined(GOOGLE_TV) bool MediaSourceDelegate::CanNotifyDemuxerReady() { DCHECK_BELONG_TO_MEDIA_LOOP(); @@ -664,8 +669,10 @@ bool MediaSourceDelegate::CanNotifyDemuxerReady() { // See http://crbug.com/255781 if (!is_demuxer_ready_) return false; +#if defined(GOOGLE_TV) if (HasEncryptedStream() && !key_added_) return false; +#endif // defined(GOOGLE_TV) return true; } @@ -694,9 +701,9 @@ void MediaSourceDelegate::NotifyDemuxerReady() { config.extra_data(), config.extra_data() + config.extra_data_size()); } configs->duration_ms = GetDurationMs(); - configs->key_system = HasEncryptedStream() ? key_system_ : ""; #if defined(GOOGLE_TV) + configs->key_system = HasEncryptedStream() ? key_system_ : ""; send_demuxer_ready_cb_.Run(configs.Pass()); #else SendDemuxerReady(configs.Pass()); diff --git a/content/renderer/media/android/media_source_delegate.h b/content/renderer/media/android/media_source_delegate.h index 5ddc021..134553e 100644 --- a/content/renderer/media/android/media_source_delegate.h +++ b/content/renderer/media/android/media_source_delegate.h @@ -5,6 +5,9 @@ #ifndef CONTENT_RENDERER_MEDIA_ANDROID_MEDIA_SOURCE_DELEGATE_H_ #define CONTENT_RENDERER_MEDIA_ANDROID_MEDIA_SOURCE_DELEGATE_H_ +#include <string> +#include <vector> + #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -233,8 +236,10 @@ class MediaSourceDelegate : public media::DemuxerHost { base::TimeDelta last_seek_time_; unsigned last_seek_request_id_; +#if defined(GOOGLE_TV) bool key_added_; std::string key_system_; +#endif // defined(GOOGLE_TV) size_t access_unit_size_; diff --git a/content/renderer/media/android/webmediaplayer_android.cc b/content/renderer/media/android/webmediaplayer_android.cc index 5adbee7..8eda65f 100644 --- a/content/renderer/media/android/webmediaplayer_android.cc +++ b/content/renderer/media/android/webmediaplayer_android.cc @@ -4,6 +4,8 @@ #include "content/renderer/media/android/webmediaplayer_android.h" +#include <limits> + #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" @@ -146,7 +148,7 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid( proxy_, player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are // separated from WebMediaPlayer. -#endif // defined(ENABLE_PEPPER_CDMS) +#endif // defined(ENABLE_PEPPER_CDMS) // |decryptor_| is owned, so Unretained() is safe here. base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, base::Unretained(this)), base::Bind(&WebMediaPlayerAndroid::OnKeyError, base::Unretained(this)), @@ -363,7 +365,7 @@ bool WebMediaPlayerAndroid::hasVideo() const { if (!url_.has_path()) return false; std::string mime; - if(!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) + if (!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) return true; return mime.find("audio/") == std::string::npos; } @@ -1089,8 +1091,10 @@ WebMediaPlayerAndroid::CancelKeyRequestInternal( void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); +#if defined(GOOGLE_TV) if (media_source_delegate_) media_source_delegate_->NotifyKeyAdded(current_key_system_.utf8()); +#endif // defined(GOOGLE_TV) client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id)); } diff --git a/media/base/android/demuxer_stream_player_params.h b/media/base/android/demuxer_stream_player_params.h index 92ef74f..4a3a04d 100644 --- a/media/base/android/demuxer_stream_player_params.h +++ b/media/base/android/demuxer_stream_player_params.h @@ -5,7 +5,9 @@ #ifndef MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ #define MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ +#if defined(GOOGLE_TV) #include <string> +#endif // defined(GOOGLE_TV) #include <vector> #include "media/base/audio_decoder_config.h" @@ -33,7 +35,10 @@ struct MEDIA_EXPORT DemuxerConfigs { std::vector<uint8> video_extra_data; int duration_ms; + +#if defined(GOOGLE_TV) std::string key_system; +#endif // defined(GOOGLE_TV) }; struct MEDIA_EXPORT AccessUnit { 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 42f1241..7025a1d 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java @@ -27,12 +27,20 @@ import org.chromium.base.JNINamespace; */ @JNINamespace("media") class MediaCodecBridge { - private static final String TAG = "MediaCodecBridge"; // Error code for MediaCodecBridge. Keep this value in sync with - // INFO_MEDIA_CODEC_ERROR in media_codec_bridge.h. - private static final int MEDIA_CODEC_ERROR = -1000; + // MediaCodecStatus in media_codec_bridge.h. + private static final int MEDIA_CODEC_OK = 0; + private static final int MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER = 1; + private static final int MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER = 2; + private static final int MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED = 3; + private static final int MEDIA_CODEC_OUTPUT_FORMAT_CHANGED = 4; + private static final int MEDIA_CODEC_INPUT_END_OF_STREAM = 5; + private static final int MEDIA_CODEC_OUTPUT_END_OF_STREAM = 6; + private static final int MEDIA_CODEC_NO_KEY = 7; + private static final int MEDIA_CODEC_STOPPED = 8; + private static final int MEDIA_CODEC_ERROR = 9; // After a flush(), dequeueOutputBuffer() can often produce empty presentation timestamps // for several frames. As a result, the player may find that the time does not increase @@ -50,15 +58,33 @@ class MediaCodecBridge { private boolean mFlushed; private long mLastPresentationTimeUs; + private static class DequeueInputResult { + private final int mStatus; + private final int mIndex; + + private DequeueInputResult(int status, int index) { + mStatus = status; + mIndex = index; + } + + @CalledByNative("DequeueInputResult") + private int status() { return mStatus; } + + @CalledByNative("DequeueInputResult") + private int index() { return mIndex; } + } + private static class DequeueOutputResult { + private final int mStatus; private final int mIndex; private final int mFlags; private final int mOffset; private final long mPresentationTimeMicroseconds; private final int mNumBytes; - private DequeueOutputResult(int index, int flags, int offset, + private DequeueOutputResult(int status, int index, int flags, int offset, long presentationTimeMicroseconds, int numBytes) { + mStatus = status; mIndex = index; mFlags = flags; mOffset = offset; @@ -67,6 +93,9 @@ class MediaCodecBridge { } @CalledByNative("DequeueOutputResult") + private int status() { return mStatus; } + + @CalledByNative("DequeueOutputResult") private int index() { return mIndex; } @CalledByNative("DequeueOutputResult") @@ -143,13 +172,24 @@ class MediaCodecBridge { } @CalledByNative - private int dequeueInputBuffer(long timeoutUs) { + private DequeueInputResult dequeueInputBuffer(long timeoutUs) { + int status = MEDIA_CODEC_ERROR; + int index = -1; try { - return mMediaCodec.dequeueInputBuffer(timeoutUs); + int index_or_status = mMediaCodec.dequeueInputBuffer(timeoutUs); + if (index_or_status >= 0) { // index! + status = MEDIA_CODEC_OK; + index = index_or_status; + } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { + Log.e(TAG, "dequeueInputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER"); + status = MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; + } else { + assert(false); + } } catch(Exception e) { - Log.e(TAG, "Cannot dequeue Input buffer " + e.toString()); + Log.e(TAG, "Failed to dequeue input buffer: " + e.toString()); } - return MEDIA_CODEC_ERROR; + return new DequeueInputResult(status, index); } @CalledByNative @@ -190,18 +230,20 @@ class MediaCodecBridge { } @CalledByNative - private void queueInputBuffer( + private int queueInputBuffer( int index, int offset, int size, long presentationTimeUs, int flags) { resetLastPresentationTimeIfNeeded(presentationTimeUs); try { mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags); - } catch(IllegalStateException e) { - Log.e(TAG, "Failed to queue input buffer " + e.toString()); + } catch(Exception e) { + Log.e(TAG, "Failed to queue input buffer: " + e.toString()); + return MEDIA_CODEC_ERROR; } + return MEDIA_CODEC_OK; } @CalledByNative - private void queueSecureInputBuffer( + private int queueSecureInputBuffer( int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClearData, int[] numBytesOfEncryptedData, int numSubSamples, long presentationTimeUs) { resetLastPresentationTimeIfNeeded(presentationTimeUs); @@ -210,9 +252,19 @@ class MediaCodecBridge { cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR); mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, 0); + } catch (MediaCodec.CryptoException e) { + Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); + // TODO(xhwang): Replace hard coded value with constant/enum. + if (e.getErrorCode() == 1) { + Log.e(TAG, "No key available."); + return MEDIA_CODEC_NO_KEY; + } + return MEDIA_CODEC_ERROR; } catch(IllegalStateException e) { - Log.e(TAG, "Failed to queue secure input buffer " + e.toString()); + Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); + return MEDIA_CODEC_ERROR; } + return MEDIA_CODEC_OK; } @CalledByNative @@ -228,9 +280,10 @@ class MediaCodecBridge { @CalledByNative private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) { MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); - int index = MEDIA_CODEC_ERROR; + int status = MEDIA_CODEC_ERROR; + int index = -1; try { - index = mMediaCodec.dequeueOutputBuffer(info, timeoutUs); + int index_or_status = mMediaCodec.dequeueOutputBuffer(info, timeoutUs); if (info.presentationTimeUs < mLastPresentationTimeUs) { // TODO(qinmin): return a special code through DequeueOutputResult // to notify the native code the the frame has a wrong presentation @@ -238,11 +291,25 @@ class MediaCodecBridge { info.presentationTimeUs = mLastPresentationTimeUs; } mLastPresentationTimeUs = info.presentationTimeUs; + + if (index_or_status >= 0) { // index! + status = MEDIA_CODEC_OK; + index = index_or_status; + } else if (index_or_status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + status = MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; + } else if (index_or_status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; + } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { + status = MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; + } else { + assert(false); + } } catch (IllegalStateException e) { - Log.e(TAG, "Cannot dequeue output buffer " + e.toString()); + Log.e(TAG, "Failed to dequeue output buffer: " + e.toString()); } + return new DequeueOutputResult( - index, info.flags, info.offset, info.presentationTimeUs, info.size); + status, index, info.flags, info.offset, info.presentationTimeUs, info.size); } @CalledByNative diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc index 8952433..f5c73a0 100644 --- a/media/base/android/media_codec_bridge.cc +++ b/media/base/android/media_codec_bridge.cc @@ -128,23 +128,24 @@ void MediaCodecBridge::GetOutputFormat(int* width, int* height) { *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); } -size_t MediaCodecBridge::QueueInputBuffer( - int index, const uint8* data, int size, +MediaCodecStatus MediaCodecBridge::QueueInputBuffer( + int index, const uint8* data, int data_size, const base::TimeDelta& presentation_time) { - size_t size_to_copy = FillInputBuffer(index, data, size); + int size_to_copy = FillInputBuffer(index, data, data_size); + DCHECK_EQ(size_to_copy, data_size); JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_queueInputBuffer( + return static_cast<MediaCodecStatus>(Java_MediaCodecBridge_queueInputBuffer( env, j_media_codec_.obj(), - index, 0, size_to_copy, presentation_time.InMicroseconds(), 0); - return size_to_copy; + index, 0, size_to_copy, presentation_time.InMicroseconds(), 0)); } -size_t MediaCodecBridge::QueueSecureInputBuffer( +MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( int index, const uint8* data, int data_size, const uint8* key_id, int key_id_size, const uint8* iv, int iv_size, const SubsampleEntry* subsamples, int subsamples_size, const base::TimeDelta& presentation_time) { - size_t size_to_copy = FillInputBuffer(index, data, data_size); + int size_to_copy = FillInputBuffer(index, data, data_size); + DCHECK_EQ(size_to_copy, data_size); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jbyteArray> j_key_id = @@ -162,12 +163,11 @@ size_t MediaCodecBridge::QueueSecureInputBuffer( ScopedJavaLocalRef<jintArray> cypher_array = ToJavaIntArray( env, native_cypher_array.Pass(), subsamples_size); - Java_MediaCodecBridge_queueSecureInputBuffer( - env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(), - clear_array.obj(), cypher_array.obj(), subsamples_size, - presentation_time.InMicroseconds()); - - return size_to_copy; + return static_cast<MediaCodecStatus>( + Java_MediaCodecBridge_queueSecureInputBuffer( + env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(), + clear_array.obj(), cypher_array.obj(), subsamples_size, + presentation_time.InMicroseconds())); } void MediaCodecBridge::QueueEOS(int input_buffer_index) { @@ -180,41 +180,21 @@ void MediaCodecBridge::QueueEOS(int input_buffer_index) { MediaCodecStatus MediaCodecBridge::DequeueInputBuffer( const base::TimeDelta& timeout, int* index) { JNIEnv* env = AttachCurrentThread(); - int result = Java_MediaCodecBridge_dequeueInputBuffer( + ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer( env, j_media_codec_.obj(), timeout.InMicroseconds()); - if (result == INFO_MEDIA_CODEC_ERROR) - return MEDIA_CODEC_ERROR; - else if (result == INFO_TRY_AGAIN_LATER) - return MEDIA_CODEC_ENQUEUE_INPUT_AGAIN_LATER; - - DCHECK_GE(result, 0); - *index = result; - return MEDIA_CODEC_OK; + *index = Java_DequeueInputResult_index(env, result.obj()); + return static_cast<MediaCodecStatus>( + Java_DequeueInputResult_status(env, result.obj())); } MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( const base::TimeDelta& timeout, int* index, size_t* offset, size_t* size, base::TimeDelta* presentation_time, bool* end_of_stream) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(), timeout.InMicroseconds()); - - int j_index = Java_DequeueOutputResult_index(env, result.obj()); - switch (j_index) { - case INFO_OUTPUT_BUFFERS_CHANGED: - return MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; - case INFO_OUTPUT_FORMAT_CHANGED: - return MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; - case INFO_TRY_AGAIN_LATER: - return MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; - case INFO_MEDIA_CODEC_ERROR: - return MEDIA_CODEC_ERROR; - } - - DCHECK_GE(j_index, 0); - *index = j_index; + *index = Java_DequeueOutputResult_index(env, result.obj());; *offset = base::checked_numeric_cast<size_t>( Java_DequeueOutputResult_offset(env, result.obj())); *size = base::checked_numeric_cast<size_t>( @@ -223,7 +203,8 @@ MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( Java_DequeueOutputResult_presentationTimeMicroseconds(env, result.obj())); int flags = Java_DequeueOutputResult_flags(env, result.obj()); *end_of_stream = flags & kBufferFlagEndOfStream; - return MEDIA_CODEC_OK; + return static_cast<MediaCodecStatus>( + Java_DequeueOutputResult_status(env, result.obj())); } void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { @@ -303,7 +284,7 @@ bool AudioCodecBridge::ConfigureMediaFormat( return true; JNIEnv* env = AttachCurrentThread(); - switch(codec) { + switch (codec) { case kCodecVorbis: { if (extra_data[0] != 2) { diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h index 47c3f89..9f855be 100644 --- a/media/base/android/media_codec_bridge.h +++ b/media/base/android/media_codec_bridge.h @@ -18,14 +18,17 @@ namespace media { struct SubsampleEntry; +// These must be in sync with MediaCodecBridge.MEDIA_CODEC_XXX constants in +// MediaCodecBridge.java. enum MediaCodecStatus { MEDIA_CODEC_OK, - MEDIA_CODEC_ENQUEUE_INPUT_AGAIN_LATER, + MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER, MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED, MEDIA_CODEC_OUTPUT_FORMAT_CHANGED, MEDIA_CODEC_INPUT_END_OF_STREAM, MEDIA_CODEC_OUTPUT_END_OF_STREAM, + MEDIA_CODEC_NO_KEY, MEDIA_CODEC_STOPPED, MEDIA_CODEC_ERROR }; @@ -38,13 +41,6 @@ enum MediaCodecStatus { // object. class MEDIA_EXPORT MediaCodecBridge { public: - enum DequeueBufferInfo { - INFO_OUTPUT_BUFFERS_CHANGED = -3, - INFO_OUTPUT_FORMAT_CHANGED = -2, - INFO_TRY_AGAIN_LATER = -1, - INFO_MEDIA_CODEC_ERROR = -1000, - }; - // Returns true if MediaCodec is available on the device. static bool IsAvailable(); @@ -72,14 +68,16 @@ class MEDIA_EXPORT MediaCodecBridge { void GetOutputFormat(int* width, int* height); // Submits a byte array to the given input buffer. Call this after getting an - // available buffer from DequeueInputBuffer(). Returns the number of bytes - // put to the input buffer. - size_t QueueInputBuffer(int index, const uint8* data, int size, - const base::TimeDelta& presentation_time); + // available buffer from DequeueInputBuffer(). + MediaCodecStatus QueueInputBuffer(int index, + const uint8* data, + int size, + const base::TimeDelta& presentation_time); // Similar to the above call, but submits a buffer that is encrypted. - size_t QueueSecureInputBuffer( - int index, const uint8* data, int data_size, + MediaCodecStatus QueueSecureInputBuffer( + int index, + const uint8* data, int data_size, const uint8* key_id, int key_id_size, const uint8* iv, int iv_size, const SubsampleEntry* subsamples, int subsamples_size, diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc index 3230e236..33b48302 100644 --- a/media/base/android/media_decoder_job.cc +++ b/media/base/android/media_decoder_job.cc @@ -29,6 +29,7 @@ MediaDecoderJob::MediaDecoderJob( weak_this_(this), request_data_cb_(request_data_cb), access_unit_index_(0), + input_buf_index_(-1), stop_decode_pending_(false), destroy_pending_(false) { } @@ -36,6 +37,7 @@ MediaDecoderJob::MediaDecoderJob( MediaDecoderJob::~MediaDecoderJob() {} void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { + DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(!on_data_received_cb_.is_null()); @@ -132,44 +134,59 @@ void MediaDecoderJob::Release() { delete this; } -MediaCodecStatus MediaDecoderJob::QueueInputBuffer( - const AccessUnit& unit) { - base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( - kMediaCodecTimeoutInMilliseconds); - int input_buf_index = 0; - MediaCodecStatus status = - media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index); - if (status != MEDIA_CODEC_OK) - return status; +MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) { + DVLOG(1) << __FUNCTION__; + DCHECK(decoder_loop_->BelongsToCurrentThread()); + + int input_buf_index = input_buf_index_; + input_buf_index_ = -1; + + // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge. + if (input_buf_index == -1) { + base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( + kMediaCodecTimeoutInMilliseconds); + MediaCodecStatus status = + media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index); + if (status != MEDIA_CODEC_OK) { + DVLOG(1) << "DequeueInputBuffer fails: " << status; + return status; + } + } // TODO(qinmin): skip frames if video is falling far behind. DCHECK_GE(input_buf_index, 0); if (unit.end_of_stream || unit.data.empty()) { - media_codec_bridge_->QueueEOS(input_buf_index); + media_codec_bridge_->QueueEOS(input_buf_index_); return MEDIA_CODEC_INPUT_END_OF_STREAM; } if (unit.key_id.empty()) { - media_codec_bridge_->QueueInputBuffer( + return media_codec_bridge_->QueueInputBuffer( input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); - return MEDIA_CODEC_OK; } if (unit.iv.empty() || unit.subsamples.empty()) { - LOG(ERROR) << "The access unit doesn't have iv or subsamples while it " + DVLOG(1) << "The access unit doesn't have iv or subsamples while it " << "has key IDs!"; return MEDIA_CODEC_ERROR; } - media_codec_bridge_->QueueSecureInputBuffer( + MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer( input_buf_index, &unit.data[0], unit.data.size(), reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), &unit.subsamples[0], unit.subsamples.size(), unit.timestamp); - return MEDIA_CODEC_OK; + + // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. + // Otherwise MediaDrm will report errors. + if (status == MEDIA_CODEC_NO_KEY) + input_buf_index_ = input_buf_index; + + return status; } void MediaDecoderJob::RequestData(const base::Closure& done_cb) { + DVLOG(1) << __FUNCTION__; DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(on_data_received_cb_.is_null()); @@ -201,6 +218,9 @@ void MediaDecoderJob::DecodeInternal( const base::TimeDelta& start_presentation_timestamp, bool needs_flush, const MediaDecoderJob::DecoderCallback& callback) { + DVLOG(1) << __FUNCTION__; + DCHECK(decoder_loop_->BelongsToCurrentThread()); + if (needs_flush) { DVLOG(1) << "DecodeInternal needs flush."; input_eos_encountered_ = false; @@ -259,7 +279,7 @@ void MediaDecoderJob::DecodeInternal( // TODO(acolwell): Change to > since the else will never run for audio. if (time_to_render >= base::TimeDelta()) { - base::MessageLoop::current()->PostDelayedTask( + decoder_loop_->PostDelayedTask( FROM_HERE, base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, weak_this_.GetWeakPtr(), buffer_index, size, @@ -289,8 +309,9 @@ void MediaDecoderJob::OnDecodeCompleted( DCHECK(!decode_cb_.is_null()); if (status != MEDIA_CODEC_ERROR && - status != MEDIA_CODEC_ENQUEUE_INPUT_AGAIN_LATER && + status != MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER && status != MEDIA_CODEC_INPUT_END_OF_STREAM && + status != MEDIA_CODEC_NO_KEY && status != MEDIA_CODEC_STOPPED) { access_unit_index_++; } diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h index 661f462..e54080e 100644 --- a/media/base/android/media_decoder_job.h +++ b/media/base/android/media_decoder_job.h @@ -151,6 +151,10 @@ class MediaDecoderJob { // Data received over IPC from last RequestData() operation. DemuxerData received_data_; + // The index of input buffer that can be used by QueueInputBuffer(). + // If the index is uninitialized or invalid, it must be -1. + int input_buf_index_; + bool stop_decode_pending_; // Indicates that this object should be destroyed once the current diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc index 7c6c25a..d529977 100644 --- a/media/base/android/media_drm_bridge.cc +++ b/media/base/android/media_drm_bridge.cc @@ -235,6 +235,7 @@ bool MediaDrmBridge::GenerateKeyRequest(const std::string& type, void MediaDrmBridge::AddKey(const uint8* key, int key_length, const uint8* init_data, int init_data_length, const std::string& session_id) { + DVLOG(1) << __FUNCTION__; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jbyteArray> j_key_data = base::android::ToJavaByteArray(env, key, key_length); diff --git a/media/base/android/media_player_android.cc b/media/base/android/media_player_android.cc index 6b1626a..ab249ac 100644 --- a/media/base/android/media_player_android.cc +++ b/media/base/android/media_player_android.cc @@ -92,4 +92,9 @@ void MediaPlayerAndroid::SetDrmBridge(MediaDrmBridge* drm_bridge) { return; } +void MediaPlayerAndroid::OnKeyAdded() { + // Not all players care about the decryption key. Do nothing by default. + return; +} + } // namespace media diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h index 06cb573..a767e5f 100644 --- a/media/base/android/media_player_android.h +++ b/media/base/android/media_player_android.h @@ -102,6 +102,10 @@ class MEDIA_EXPORT MediaPlayerAndroid { // Pass a drm bridge to a player. virtual void SetDrmBridge(MediaDrmBridge* drm_bridge); + // Notifies the player that a decryption key has been added. The player + // may want to start/resume playback if it is waiting for a key. + virtual void OnKeyAdded(); + int player_id() { return player_id_; } protected: diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc index af1f2b1..d657fca 100644 --- a/media/base/android/media_source_player.cc +++ b/media/base/android/media_source_player.cc @@ -4,6 +4,8 @@ #include "media/base/android/media_source_player.h" +#include <limits> + #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/barrier_closure.h" @@ -185,6 +187,12 @@ void MediaSourcePlayer::SetVolume(double volume) { SetVolumeInternal(); } +void MediaSourcePlayer::OnKeyAdded() { + DVLOG(1) << __FUNCTION__; + if (playing_) + StartInternal(); +} + bool MediaSourcePlayer::CanPause() { return Seekable(); } @@ -211,7 +219,6 @@ void MediaSourcePlayer::StartInternal() { ConfigureAudioDecoderJob(); ConfigureVideoDecoderJob(); - // If one of the decoder job is not ready, do nothing. if ((HasAudio() && !audio_decoder_job_) || (HasVideo() && !video_decoder_job_)) { @@ -346,12 +353,15 @@ void MediaSourcePlayer::UpdateTimestamps( } void MediaSourcePlayer::ProcessPendingEvents() { - DVLOG(1) << __FUNCTION__ << " : 0x" - << std::hex << pending_event_; + DVLOG(1) << __FUNCTION__ << " : 0x" << std::hex << pending_event_; // Wait for all the decoding jobs to finish before processing pending tasks. - if ((audio_decoder_job_ && audio_decoder_job_->is_decoding()) || - (video_decoder_job_ && video_decoder_job_->is_decoding())) { - DVLOG(1) << __FUNCTION__ << " : A job is still decoding."; + if (video_decoder_job_ && video_decoder_job_->is_decoding()) { + DVLOG(1) << __FUNCTION__ << " : A video job is still decoding."; + return; + } + + if (audio_decoder_job_ && audio_decoder_job_->is_decoding()) { + DVLOG(1) << __FUNCTION__ << " : An audio job is still decoding."; return; } @@ -412,7 +422,7 @@ void MediaSourcePlayer::ProcessPendingEvents() { void MediaSourcePlayer::MediaDecoderCallback( bool is_audio, MediaCodecStatus status, const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) { - DVLOG(1) << __FUNCTION__; + DVLOG(1) << __FUNCTION__ << ": " << is_audio << ", " << status; if (is_audio) decoder_starvation_callback_.Cancel(); @@ -442,6 +452,9 @@ void MediaSourcePlayer::MediaDecoderCallback( return; } + if (status == MEDIA_CODEC_NO_KEY) + return; + base::TimeDelta current_timestamp = GetCurrentTime(); if (is_audio) { if (status == MEDIA_CODEC_OK) { @@ -449,7 +462,7 @@ void MediaSourcePlayer::MediaDecoderCallback( audio_timestamp_helper_->GetTimestamp() - current_timestamp; StartStarvationCallback(timeout); } - DecodeMoreAudio(); + DecodeMoreAudio(); return; } diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h index 7db76c3..fd262d7 100644 --- a/media/base/android/media_source_player.h +++ b/media/base/android/media_source_player.h @@ -67,6 +67,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid { virtual void ReadFromDemuxerAck(const DemuxerData& data) OVERRIDE; virtual void DurationChanged(const base::TimeDelta& duration) OVERRIDE; virtual void SetDrmBridge(MediaDrmBridge* drm_bridge) OVERRIDE; + virtual void OnKeyAdded() OVERRIDE; private: // Update the current timestamp. |