summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-05 02:21:25 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-05 02:21:25 +0000
commit00a5890250f45236a8373f6ec033bd7fff3c69b3 (patch)
tree212ccd2d43e159dfa80a3a479b1780ff72dc8c79
parentb5449c06d9457e1e1b2a4425fe115d439627f512 (diff)
downloadchromium_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
-rw-r--r--content/browser/android/browser_media_player_manager.cc15
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator.cc4
-rw-r--r--content/common/media/media_player_messages_android.h6
-rw-r--r--content/renderer/media/android/media_source_delegate.cc19
-rw-r--r--content/renderer/media/android/media_source_delegate.h5
-rw-r--r--content/renderer/media/android/webmediaplayer_android.cc8
-rw-r--r--media/base/android/demuxer_stream_player_params.h5
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecBridge.java101
-rw-r--r--media/base/android/media_codec_bridge.cc63
-rw-r--r--media/base/android/media_codec_bridge.h26
-rw-r--r--media/base/android/media_decoder_job.cc55
-rw-r--r--media/base/android/media_decoder_job.h4
-rw-r--r--media/base/android/media_drm_bridge.cc1
-rw-r--r--media/base/android/media_player_android.cc5
-rw-r--r--media/base/android/media_player_android.h4
-rw-r--r--media/base/android/media_source_player.cc29
-rw-r--r--media/base/android/media_source_player.h1
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.