summaryrefslogtreecommitdiff
path: root/media/base
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2014-06-20 14:52:04 +0100
committerTorne (Richard Coles) <torne@google.com>2014-06-20 14:52:04 +0100
commitf8ee788a64d60abd8f2d742a5fdedde054ecd910 (patch)
tree7dc14380200b953c64e0ccd16435cdbd1dbf1205 /media/base
parentfcbbbe23a38088a52492922075e71a419c4b01ec (diff)
downloadchromium_org-f8ee788a64d60abd8f2d742a5fdedde054ecd910.tar.gz
Merge from Chromium at DEPS revision 278205
This commit was generated by merge_to_master.py. Change-Id: I23f1e7ea8c154ba72e7fb594436216f861f868ab
Diffstat (limited to 'media/base')
-rw-r--r--media/base/android/browser_cdm_factory_android.cc10
-rw-r--r--media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java87
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecBridge.java67
-rw-r--r--media/base/android/media_codec_bridge.cc13
-rw-r--r--media/base/android/media_codec_bridge.h16
-rw-r--r--media/base/android/media_decoder_job.cc24
-rw-r--r--media/base/android/media_decoder_job.h8
-rw-r--r--media/base/android/media_drm_bridge.cc19
-rw-r--r--media/base/android/media_drm_bridge.h4
-rw-r--r--media/base/android/media_player_android.cc7
-rw-r--r--media/base/android/media_player_android.h8
-rw-r--r--media/base/android/media_player_bridge.cc6
-rw-r--r--media/base/android/media_player_bridge.h3
-rw-r--r--media/base/android/media_player_manager.h42
-rw-r--r--media/base/android/media_source_player.cc18
-rw-r--r--media/base/android/media_source_player.h3
-rw-r--r--media/base/android/media_source_player_unittest.cc127
-rw-r--r--media/base/android/video_decoder_job.cc20
-rw-r--r--media/base/android/video_decoder_job.h2
-rw-r--r--media/base/audio_decoder.cc4
-rw-r--r--media/base/audio_decoder.h42
-rw-r--r--media/base/audio_fifo.cc15
-rw-r--r--media/base/audio_fifo.h8
-rw-r--r--media/base/browser_cdm.h23
-rw-r--r--media/base/browser_cdm_factory.h14
-rw-r--r--media/base/cdm_promise.cc74
-rw-r--r--media/base/cdm_promise.h87
-rw-r--r--media/base/demuxer.h3
-rw-r--r--media/base/media_keys.h77
-rw-r--r--media/base/mock_filters.h12
-rw-r--r--media/base/pipeline.cc9
-rw-r--r--media/base/pipeline_unittest.cc42
-rw-r--r--media/base/video_decoder.cc4
-rw-r--r--media/base/video_decoder.h40
34 files changed, 651 insertions, 287 deletions
diff --git a/media/base/android/browser_cdm_factory_android.cc b/media/base/android/browser_cdm_factory_android.cc
index 71eb0b5469..2f438f054a 100644
--- a/media/base/android/browser_cdm_factory_android.cc
+++ b/media/base/android/browser_cdm_factory_android.cc
@@ -14,11 +14,11 @@ namespace media {
scoped_ptr<BrowserCdm> CreateBrowserCdm(
const std::string& key_system,
- const SessionCreatedCB& session_created_cb,
- const SessionMessageCB& session_message_cb,
- const SessionReadyCB& session_ready_cb,
- const SessionClosedCB& session_closed_cb,
- const SessionErrorCB& session_error_cb) {
+ const BrowserCdm::SessionCreatedCB& session_created_cb,
+ const BrowserCdm::SessionMessageCB& session_message_cb,
+ const BrowserCdm::SessionReadyCB& session_ready_cb,
+ const BrowserCdm::SessionClosedCB& session_closed_cb,
+ const BrowserCdm::SessionErrorCB& session_error_cb) {
if (!MediaDrmBridge::IsKeySystemSupported(key_system)) {
NOTREACHED() << "Unsupported key system: " << key_system;
return scoped_ptr<BrowserCdm>();
diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
index e675883d5c..85baad0333 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -163,6 +163,13 @@ class AudioManagerAndroid {
private final Context mContext;
private final long mNativeAudioManagerAndroid;
+ // Enabled during initialization if MODIFY_AUDIO_SETTINGS permission is
+ // granted. Required to shift system-wide audio settings.
+ private boolean mHasModifyAudioSettingsPermission = false;
+
+ // Enabled during initialization if RECORD_AUDIO permission is granted.
+ private boolean mHasRecordAudioPermission = false;
+
// Enabled during initialization if BLUETOOTH permission is granted.
private boolean mHasBluetoothPermission = false;
@@ -237,6 +244,19 @@ class AudioManagerAndroid {
if (mIsInitialized)
return;
+ // Check if process has MODIFY_AUDIO_SETTINGS and RECORD_AUDIO
+ // permissions. Both are required for full functionality.
+ mHasModifyAudioSettingsPermission = hasPermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS);
+ if (DEBUG && !mHasModifyAudioSettingsPermission) {
+ logd("MODIFY_AUDIO_SETTINGS permission is missing");
+ }
+ mHasRecordAudioPermission = hasPermission(
+ android.Manifest.permission.RECORD_AUDIO);
+ if (DEBUG && !mHasRecordAudioPermission) {
+ logd("RECORD_AUDIO permission is missing");
+ }
+
// Initialize audio device list with things we know is always available.
mAudioDevices[DEVICE_EARPIECE] = hasEarpiece();
mAudioDevices[DEVICE_WIRED_HEADSET] = hasWiredHeadset();
@@ -277,14 +297,23 @@ class AudioManagerAndroid {
* Saves current audio mode and sets audio mode to MODE_IN_COMMUNICATION
* if input parameter is true. Restores saved audio mode if input parameter
* is false.
+ * Required permission: android.Manifest.permission.MODIFY_AUDIO_SETTINGS.
*/
@CalledByNative
private void setCommunicationAudioModeOn(boolean on) {
if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")");
+ // The MODIFY_AUDIO_SETTINGS permission is required to allow an
+ // application to modify global audio settings.
+ if (!mHasModifyAudioSettingsPermission) {
+ Log.w(TAG, "MODIFY_AUDIO_SETTINGS is missing => client will run " +
+ "with reduced functionality");
+ return;
+ }
+
if (on) {
if (mSavedAudioMode != AudioManager.MODE_INVALID) {
- Log.wtf(TAG, "Audio mode has already been set!");
+ Log.wtf(TAG, "Audio mode has already been set");
return;
}
@@ -318,7 +347,7 @@ class AudioManagerAndroid {
} else {
if (mSavedAudioMode == AudioManager.MODE_INVALID) {
- Log.wtf(TAG, "Audio mode has not yet been set!");
+ Log.wtf(TAG, "Audio mode has not yet been set");
return;
}
@@ -346,12 +375,20 @@ class AudioManagerAndroid {
* @param deviceId Unique device ID (integer converted to string)
* representing the selected device. This string is empty if the so-called
* default device is requested.
+ * Required permissions: android.Manifest.permission.MODIFY_AUDIO_SETTINGS
+ * and android.Manifest.permission.RECORD_AUDIO.
*/
@CalledByNative
private boolean setDevice(String deviceId) {
if (DEBUG) logd("setDevice: " + deviceId);
if (!mIsInitialized)
return false;
+ if (!mHasModifyAudioSettingsPermission || !mHasRecordAudioPermission) {
+ Log.w(TAG, "Requires MODIFY_AUDIO_SETTINGS and RECORD_AUDIO");
+ Log.w(TAG, "Selected device will not be available for recording");
+ return false;
+ }
+
int intDeviceId = deviceId.isEmpty() ?
DEVICE_DEFAULT : Integer.parseInt(deviceId);
@@ -383,12 +420,20 @@ class AudioManagerAndroid {
* @return the current list of available audio devices.
* Note that this call does not trigger any update of the list of devices,
* it only copies the current state in to the output array.
+ * Required permissions: android.Manifest.permission.MODIFY_AUDIO_SETTINGS
+ * and android.Manifest.permission.RECORD_AUDIO.
*/
@CalledByNative
private AudioDeviceName[] getAudioInputDeviceNames() {
if (DEBUG) logd("getAudioInputDeviceNames");
if (!mIsInitialized)
return null;
+ if (!mHasModifyAudioSettingsPermission || !mHasRecordAudioPermission) {
+ Log.w(TAG, "Requires MODIFY_AUDIO_SETTINGS and RECORD_AUDIO");
+ Log.w(TAG, "No audio device will be available for recording");
+ return null;
+ }
+
boolean devices[] = null;
synchronized (mLock) {
devices = mAudioDevices.clone();
@@ -501,7 +546,7 @@ class AudioManagerAndroid {
*/
private void checkIfCalledOnValidThread() {
if (DEBUG && !mNonThreadSafe.calledOnValidThread()) {
- Log.wtf(TAG, "Method is not called on valid thread!");
+ Log.wtf(TAG, "Method is not called on valid thread");
}
}
@@ -511,7 +556,8 @@ class AudioManagerAndroid {
*/
private void registerBluetoothIntentsIfNeeded() {
// Check if this process has the BLUETOOTH permission or not.
- mHasBluetoothPermission = hasBluetoothPermission();
+ mHasBluetoothPermission = hasPermission(
+ android.Manifest.permission.BLUETOOTH);
// Add a Bluetooth headset to the list of available devices if a BT
// headset is detected and if we have the BLUETOOTH permission.
@@ -520,6 +566,7 @@ class AudioManagerAndroid {
// is not sticky and will only be received if a BT headset is connected
// after this method has been called.
if (!mHasBluetoothPermission) {
+ Log.w(TAG, "Requires BLUETOOTH permission");
return;
}
mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = hasBluetoothHeadset();
@@ -580,16 +627,12 @@ class AudioManagerAndroid {
return mAudioManager.isWiredHeadsetOn();
}
- /** Checks if the process has BLUETOOTH permission or not. */
- private boolean hasBluetoothPermission() {
- boolean hasBluetooth = mContext.checkPermission(
- android.Manifest.permission.BLUETOOTH,
- Process.myPid(),
- Process.myUid()) == PackageManager.PERMISSION_GRANTED;
- if (DEBUG && !hasBluetooth) {
- logd("BLUETOOTH permission is missing!");
- }
- return hasBluetooth;
+ /** Checks if the process has as specified permission or not. */
+ private boolean hasPermission(String permission) {
+ return mContext.checkPermission(
+ permission,
+ Process.myPid(),
+ Process.myUid()) == PackageManager.PERMISSION_GRANTED;
}
/**
@@ -599,7 +642,7 @@ class AudioManagerAndroid {
*/
private boolean hasBluetoothHeadset() {
if (!mHasBluetoothPermission) {
- Log.wtf(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission!");
+ Log.w(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission");
return false;
}
@@ -695,7 +738,7 @@ class AudioManagerAndroid {
}
break;
default:
- loge("Invalid state!");
+ loge("Invalid state");
break;
}
@@ -770,7 +813,7 @@ class AudioManagerAndroid {
// Bluetooth service is switching from on to off.
break;
default:
- loge("Invalid state!");
+ loge("Invalid state");
break;
}
@@ -824,7 +867,7 @@ class AudioManagerAndroid {
// do nothing
break;
default:
- loge("Invalid state!");
+ loge("Invalid state");
}
if (DEBUG) {
reportUpdate();
@@ -875,7 +918,7 @@ class AudioManagerAndroid {
}
if (!mAudioManager.isBluetoothScoOn()) {
// TODO(henrika): can we do anything else than logging here?
- loge("Unable to stop BT SCO since it is already disabled!");
+ loge("Unable to stop BT SCO since it is already disabled");
return;
}
@@ -913,7 +956,7 @@ class AudioManagerAndroid {
setSpeakerphoneOn(false);
break;
default:
- loge("Invalid audio device selection!");
+ loge("Invalid audio device selection");
break;
}
reportUpdate();
@@ -955,7 +998,7 @@ class AudioManagerAndroid {
devices = mAudioDevices.clone();
}
if (requested == DEVICE_INVALID) {
- loge("Unable to activate device since no device is selected!");
+ loge("Unable to activate device since no device is selected");
return;
}
@@ -1045,7 +1088,7 @@ class AudioManagerAndroid {
// Ensure that the observer is activated during communication mode.
if (mAudioManager.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
- Log.wtf(TAG, "Only enable SettingsObserver in COMM mode!");
+ Log.wtf(TAG, "Only enable SettingsObserver in COMM mode");
return;
}
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 5eb7b5bcc6..2fdd0f00a0 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -50,6 +50,10 @@ class MediaCodecBridge {
private static final int MEDIA_CODEC_DECODER = 0;
private static final int MEDIA_CODEC_ENCODER = 1;
+ // Max adaptive playback size to be supplied to the decoder.
+ private static final int MAX_ADAPTIVE_PLAYBACK_WIDTH = 1920;
+ private static final int MAX_ADAPTIVE_PLAYBACK_HEIGHT = 1080;
+
// 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
// after decoding a frame. To detect this, we check whether the presentation timestamp from
@@ -65,6 +69,8 @@ class MediaCodecBridge {
private AudioTrack mAudioTrack;
private boolean mFlushed;
private long mLastPresentationTimeUs;
+ private String mMime;
+ private boolean mAdaptivePlaybackSupported;
private static class DequeueInputResult {
private final int mStatus;
@@ -174,7 +180,7 @@ class MediaCodecBridge {
return codecInfos.toArray(new CodecInfo[codecInfos.size()]);
}
- private static String getSecureDecoderNameForMime(String mime) {
+ private static String getDecoderNameForMime(String mime) {
int count = MediaCodecList.getCodecCount();
for (int i = 0; i < count; ++i) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
@@ -185,7 +191,7 @@ class MediaCodecBridge {
String[] supportedTypes = info.getSupportedTypes();
for (int j = 0; j < supportedTypes.length; ++j) {
if (supportedTypes[j].equalsIgnoreCase(mime)) {
- return info.getName() + ".secure";
+ return info.getName();
}
}
}
@@ -193,11 +199,14 @@ class MediaCodecBridge {
return null;
}
- private MediaCodecBridge(MediaCodec mediaCodec) {
+ private MediaCodecBridge(
+ MediaCodec mediaCodec, String mime, boolean adaptivePlaybackSupported) {
assert mediaCodec != null;
mMediaCodec = mediaCodec;
+ mMime = mime;
mLastPresentationTimeUs = 0;
mFlushed = true;
+ mAdaptivePlaybackSupported = adaptivePlaybackSupported;
}
@CalledByNative
@@ -208,15 +217,29 @@ class MediaCodecBridge {
return null;
}
MediaCodec mediaCodec = null;
+ boolean adaptivePlaybackSupported = false;
try {
// |isSecure| only applies to video decoders.
if (mime.startsWith("video") && isSecure && direction == MEDIA_CODEC_DECODER) {
- mediaCodec = MediaCodec.createByCodecName(getSecureDecoderNameForMime(mime));
+ String decoderName = getDecoderNameForMime(mime);
+ if (decoderName == null) {
+ return null;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ // To work around an issue that we cannot get the codec info from the secure
+ // decoder, create an insecure decoder first so that we can query its codec
+ // info. http://b/15587335.
+ MediaCodec insecureCodec = MediaCodec.createByCodecName(decoderName);
+ adaptivePlaybackSupported = codecSupportsAdaptivePlayback(insecureCodec, mime);
+ insecureCodec.release();
+ }
+ mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure");
} else {
if (direction == MEDIA_CODEC_ENCODER) {
mediaCodec = MediaCodec.createEncoderByType(mime);
} else {
mediaCodec = MediaCodec.createDecoderByType(mime);
+ adaptivePlaybackSupported = codecSupportsAdaptivePlayback(mediaCodec, mime);
}
}
} catch (Exception e) {
@@ -227,15 +250,14 @@ class MediaCodecBridge {
if (mediaCodec == null) {
return null;
}
-
- return new MediaCodecBridge(mediaCodec);
+ return new MediaCodecBridge(mediaCodec, mime, adaptivePlaybackSupported);
}
@CalledByNative
private void release() {
try {
mMediaCodec.release();
- } catch(IllegalStateException e) {
+ } catch (IllegalStateException e) {
// The MediaCodec is stuck in a wrong state, possibly due to losing
// the surface.
Log.e(TAG, "Cannot release media codec", e);
@@ -407,7 +429,7 @@ class MediaCodecBridge {
private void releaseOutputBuffer(int index, boolean render) {
try {
mMediaCodec.releaseOutputBuffer(index, render);
- } catch(IllegalStateException e) {
+ } catch (IllegalStateException e) {
// TODO(qinmin): May need to report the error to the caller. crbug.com/356498.
Log.e(TAG, "Failed to release output buffer", e);
}
@@ -453,6 +475,10 @@ class MediaCodecBridge {
private boolean configureVideo(MediaFormat format, Surface surface, MediaCrypto crypto,
int flags) {
try {
+ if (mAdaptivePlaybackSupported) {
+ format.setInteger(MediaFormat.KEY_MAX_WIDTH, MAX_ADAPTIVE_PLAYBACK_WIDTH);
+ format.setInteger(MediaFormat.KEY_MAX_HEIGHT, MAX_ADAPTIVE_PLAYBACK_HEIGHT);
+ }
mMediaCodec.configure(format, surface, crypto, flags);
return true;
} catch (IllegalStateException e) {
@@ -483,6 +509,31 @@ class MediaCodecBridge {
}
@CalledByNative
+ private boolean isAdaptivePlaybackSupported(int width, int height) {
+ if (!mAdaptivePlaybackSupported)
+ return false;
+ return width <= MAX_ADAPTIVE_PLAYBACK_WIDTH && height <= MAX_ADAPTIVE_PLAYBACK_HEIGHT;
+ }
+
+ private static boolean codecSupportsAdaptivePlayback(MediaCodec mediaCodec, String mime) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || mediaCodec == null) {
+ return false;
+ }
+ try {
+ MediaCodecInfo info = mediaCodec.getCodecInfo();
+ if (info.isEncoder()) {
+ return false;
+ }
+ MediaCodecInfo.CodecCapabilities capabilities = info.getCapabilitiesForType(mime);
+ return (capabilities != null) && capabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot retrieve codec information", e);
+ }
+ return false;
+ }
+
+ @CalledByNative
private static void setCodecSpecificData(MediaFormat format, int index, byte[] bytes) {
String name = null;
if (index == 0) {
diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc
index 3b50949d55..92b2791506 100644
--- a/media/base/android/media_codec_bridge.cc
+++ b/media/base/android/media_codec_bridge.cc
@@ -716,7 +716,8 @@ VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec,
VideoCodecBridge::VideoCodecBridge(const std::string& mime,
bool is_secure,
MediaCodecDirection direction)
- : MediaCodecBridge(mime, is_secure, direction) {}
+ : MediaCodecBridge(mime, is_secure, direction),
+ adaptive_playback_supported_for_testing_(-1) {}
void VideoCodecBridge::SetVideoBitrate(int bps) {
JNIEnv* env = AttachCurrentThread();
@@ -728,6 +729,16 @@ void VideoCodecBridge::RequestKeyFrameSoon() {
Java_MediaCodecBridge_requestKeyFrameSoon(env, media_codec());
}
+bool VideoCodecBridge::IsAdaptivePlaybackSupported(int width, int height) {
+ if (adaptive_playback_supported_for_testing_ == 0)
+ return false;
+ else if (adaptive_playback_supported_for_testing_ > 0)
+ return true;
+ JNIEnv* env = AttachCurrentThread();
+ return Java_MediaCodecBridge_isAdaptivePlaybackSupported(
+ env, media_codec(), width, height);
+}
+
bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h
index a1a493f1ac..2d046465d4 100644
--- a/media/base/android/media_codec_bridge.h
+++ b/media/base/android/media_codec_bridge.h
@@ -263,10 +263,26 @@ class MEDIA_EXPORT VideoCodecBridge : public MediaCodecBridge {
void SetVideoBitrate(int bps);
void RequestKeyFrameSoon();
+ // Returns whether adaptive playback is supported for this object given
+ // the new size.
+ bool IsAdaptivePlaybackSupported(int width, int height);
+
+ // Test-only method to set the return value of IsAdaptivePlaybackSupported().
+ // Without this function, the return value of that function will be device
+ // dependent. If |adaptive_playback_supported| is equal to 0, the return value
+ // will be false. If |adaptive_playback_supported| is larger than 0, the
+ // return value will be true.
+ void set_adaptive_playback_supported_for_testing(
+ int adaptive_playback_supported) {
+ adaptive_playback_supported_for_testing_ = adaptive_playback_supported;
+ }
+
private:
VideoCodecBridge(const std::string& mime,
bool is_secure,
MediaCodecDirection direction);
+
+ int adaptive_playback_supported_for_testing_;
};
} // namespace media
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index 97fa39b528..4f720f8cbd 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -289,8 +289,9 @@ MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) {
bool MediaDecoderJob::HasData() const {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
// When |input_eos_encountered_| is set, |access_unit_index_| and
- // |current_demuxer_data_index_| must be pointing to an EOS unit.
- // We'll reuse this unit to flush the decoder until we hit output EOS.
+ // |current_demuxer_data_index_| must be pointing to an EOS unit,
+ // or a |kConfigChanged| unit if |drain_decoder_| is true. In both cases,
+ // we'll feed an EOS input unit to drain the decoder until we hit output EOS.
DCHECK(!input_eos_encountered_ || !NoAccessUnitsRemainingInChunk(true));
return !NoAccessUnitsRemainingInChunk(true) ||
!NoAccessUnitsRemainingInChunk(false);
@@ -332,16 +333,18 @@ void MediaDecoderJob::DecodeCurrentAccessUnit(
const AccessUnit& access_unit = CurrentAccessUnit();
if (CurrentAccessUnit().status == DemuxerStream::kConfigChanged) {
int index = CurrentReceivedDataChunkIndex();
- bool config_changed = SetDemuxerConfigs(
- received_data_[index].demuxer_configs[0]);
- if (config_changed)
+ const DemuxerConfigs& configs = received_data_[index].demuxer_configs[0];
+ bool reconfigure_needed = IsCodecReconfigureNeeded(configs);
+ // TODO(qinmin): |config_changed_cb_| should be run after draining finishes.
+ // http://crbug.com/381975.
+ if (SetDemuxerConfigs(configs))
config_changed_cb_.Run();
if (!drain_decoder_) {
// If we haven't decoded any data yet, just skip the current access unit
// and request the MediaCodec to be recreated on next Decode().
- if (skip_eos_enqueue_ || !config_changed) {
+ if (skip_eos_enqueue_ || !reconfigure_needed) {
need_to_reconfig_decoder_job_ =
- need_to_reconfig_decoder_job_ || config_changed;
+ need_to_reconfig_decoder_job_ || reconfigure_needed;
ui_task_runner_->PostTask(FROM_HERE, base::Bind(
&MediaDecoderJob::OnDecodeCompleted, base::Unretained(this),
MEDIA_CODEC_OUTPUT_FORMAT_CHANGED, kNoTimestamp(), kNoTimestamp()));
@@ -632,6 +635,13 @@ bool MediaDecoderJob::CreateMediaCodecBridge() {
return CreateMediaCodecBridgeInternal();
}
+bool MediaDecoderJob::IsCodecReconfigureNeeded(
+ const DemuxerConfigs& configs) const {
+ if (!AreDemuxerConfigsChanged(configs))
+ return false;
+ return true;
+}
+
void MediaDecoderJob::ReleaseMediaCodecBridge() {
if (!media_codec_bridge_)
return;
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index 01379f8fdc..433e035952 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -78,7 +78,7 @@ class MediaDecoderJob {
// Flushes the decoder and abandons all the data that is being decoded.
virtual void Flush();
- // Enter prerolling state. The job must not currently be decoding.
+ // Enters prerolling state. The job must not currently be decoding.
void BeginPrerolling(base::TimeDelta preroll_timestamp);
// Releases all the decoder resources as the current tab is going background.
@@ -229,9 +229,13 @@ class MediaDecoderJob {
virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const = 0;
- // Update the demuxer configs.
+ // Updates the demuxer configs.
virtual void UpdateDemuxerConfigs(const DemuxerConfigs& configs) = 0;
+ // Returns true if |media_codec_bridge_| needs to be reconfigured for the
+ // new DemuxerConfigs, or false otherwise.
+ virtual bool IsCodecReconfigureNeeded(const DemuxerConfigs& configs) const;
+
// Return the index to |received_data_| that is not currently being decoded.
size_t inactive_demuxer_data_index() const {
return 1 - current_demuxer_data_index_;
diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc
index fc0288ab77..60215ea614 100644
--- a/media/base/android/media_drm_bridge.cc
+++ b/media/base/android/media_drm_bridge.cc
@@ -73,6 +73,7 @@ class KeySystemUuidManager {
KeySystemUuidManager();
UUID GetUUID(const std::string& key_system);
void AddMapping(const std::string& key_system, const UUID& uuid);
+ std::vector<std::string> GetPlatformKeySystemNames();
private:
typedef base::hash_map<std::string, UUID> KeySystemUuidMap;
@@ -105,6 +106,17 @@ void KeySystemUuidManager::AddMapping(const std::string& key_system,
key_system_uuid_map_[key_system] = uuid;
}
+std::vector<std::string> KeySystemUuidManager::GetPlatformKeySystemNames() {
+ std::vector<std::string> key_systems;
+ for (KeySystemUuidMap::iterator it = key_system_uuid_map_.begin();
+ it != key_system_uuid_map_.end(); ++it) {
+ // Rule out the key system handled by Chrome explicitly.
+ if (it->first != kWidevineKeySystem)
+ key_systems.push_back(it->first);
+ }
+ return key_systems;
+}
+
base::LazyInstance<KeySystemUuidManager>::Leaky g_key_system_uuid_manager =
LAZY_INSTANCE_INITIALIZER;
@@ -264,13 +276,18 @@ bool MediaDrmBridge::IsSecurityLevelSupported(const std::string& key_system,
return media_drm_bridge->SetSecurityLevel(security_level);
}
-//static
+// static
void MediaDrmBridge::AddKeySystemUuidMapping(const std::string& key_system,
const std::vector<uint8>& uuid) {
g_key_system_uuid_manager.Get().AddMapping(key_system, uuid);
}
// static
+std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
+ return g_key_system_uuid_manager.Get().GetPlatformKeySystemNames();
+}
+
+// static
bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
DCHECK(!key_system.empty());
return IsKeySystemSupportedWithTypeImpl(key_system, "");
diff --git a/media/base/android/media_drm_bridge.h b/media/base/android/media_drm_bridge.h
index a7a28bd3b6..8c33a33461 100644
--- a/media/base/android/media_drm_bridge.h
+++ b/media/base/android/media_drm_bridge.h
@@ -48,6 +48,10 @@ class MEDIA_EXPORT MediaDrmBridge : public BrowserCdm {
// Checks whether |key_system| is supported.
static bool IsKeySystemSupported(const std::string& key_system);
+ // Returns the list of the platform-supported key system names that
+ // are not handled by Chrome explicitly.
+ static std::vector<std::string> GetPlatformKeySystemNames();
+
// Adds a new |key_system| with the associated |uuid|.
// This is used for other platforms to have a chance to register their
// own UUID mapping.
diff --git a/media/base/android/media_player_android.cc b/media/base/android/media_player_android.cc
index b515d08e57..dc6e0752b3 100644
--- a/media/base/android/media_player_android.cc
+++ b/media/base/android/media_player_android.cc
@@ -5,6 +5,7 @@
#include "media/base/android/media_player_android.h"
#include "base/logging.h"
+#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
namespace media {
@@ -13,11 +14,13 @@ MediaPlayerAndroid::MediaPlayerAndroid(
int player_id,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
- const ReleaseMediaResourcesCB& release_media_resources_cb)
+ const ReleaseMediaResourcesCB& release_media_resources_cb,
+ const GURL& frame_url)
: request_media_resources_cb_(request_media_resources_cb),
release_media_resources_cb_(release_media_resources_cb),
player_id_(player_id),
- manager_(manager) {
+ manager_(manager),
+ frame_url_(frame_url) {
}
MediaPlayerAndroid::~MediaPlayerAndroid() {}
diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h
index 7a7a471dcc..879ba756e2 100644
--- a/media/base/android/media_player_android.h
+++ b/media/base/android/media_player_android.h
@@ -81,11 +81,14 @@ class MEDIA_EXPORT MediaPlayerAndroid {
int player_id() { return player_id_; }
+ GURL frame_url() { return frame_url_; }
+
protected:
MediaPlayerAndroid(int player_id,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
- const ReleaseMediaResourcesCB& release_media_resources_cb);
+ const ReleaseMediaResourcesCB& release_media_resources_cb,
+ const GURL& frame_url);
MediaPlayerManager* manager() { return manager_; }
@@ -100,6 +103,9 @@ class MEDIA_EXPORT MediaPlayerAndroid {
// Resource manager for all the media players.
MediaPlayerManager* manager_;
+ // Url for the frame that contains this player.
+ GURL frame_url_;
+
DISALLOW_COPY_AND_ASSIGN(MediaPlayerAndroid);
};
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
index 174aaf9cdf..214b52b486 100644
--- a/media/base/android/media_player_bridge.cc
+++ b/media/base/android/media_player_bridge.cc
@@ -33,11 +33,13 @@ MediaPlayerBridge::MediaPlayerBridge(
bool hide_url_log,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
- const ReleaseMediaResourcesCB& release_media_resources_cb)
+ const ReleaseMediaResourcesCB& release_media_resources_cb,
+ const GURL& frame_url)
: MediaPlayerAndroid(player_id,
manager,
request_media_resources_cb,
- release_media_resources_cb),
+ release_media_resources_cb,
+ frame_url),
prepared_(false),
pending_play_(false),
url_(url),
diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h
index 33ee11892e..f63d626885 100644
--- a/media/base/android/media_player_bridge.h
+++ b/media/base/android/media_player_bridge.h
@@ -49,7 +49,8 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
bool hide_url_log,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
- const ReleaseMediaResourcesCB& release_media_resources_cb);
+ const ReleaseMediaResourcesCB& release_media_resources_cb,
+ const GURL& frame_url);
virtual ~MediaPlayerBridge();
// Initialize this object and extract the metadata from the media.
diff --git a/media/base/android/media_player_manager.h b/media/base/android/media_player_manager.h
index d7ee79149a..50e6f06dee 100644
--- a/media/base/android/media_player_manager.h
+++ b/media/base/android/media_player_manager.h
@@ -5,21 +5,13 @@
#ifndef MEDIA_BASE_ANDROID_MEDIA_PLAYER_MANAGER_H_
#define MEDIA_BASE_ANDROID_MEDIA_PLAYER_MANAGER_H_
-#include <string>
-#include <vector>
-
#include "base/basictypes.h"
#include "base/time/time.h"
#include "media/base/android/demuxer_stream_player_params.h"
#include "media/base/media_export.h"
-#include "media/base/media_keys.h"
-#include "url/gurl.h"
-
-class GURL;
namespace media {
-class BrowserCdm;
class MediaPlayerAndroid;
class MediaResourceGetter;
@@ -71,42 +63,8 @@ class MEDIA_EXPORT MediaPlayerManager {
// Returns the player with the specified id.
virtual MediaPlayerAndroid* GetPlayer(int player_id) = 0;
- // Release all the players managed by this object.
- virtual void DestroyAllMediaPlayers() = 0;
-
- // Get the CDM for the given CDM ID.
- virtual BrowserCdm* GetCdm(int cdm_id) = 0;
-
// Called by the player to get a hardware protected surface.
virtual void RequestFullScreen(int player_id) = 0;
-
- // The following five methods are related to EME.
- // TODO(xhwang): These methods needs to be decoupled from MediaPlayerManager
- // to support the W3C Working Draft version of the EME spec.
- // http://crbug.com/315312
-
- // Called when CDM creates a session.
- virtual void OnSessionCreated(int cdm_id,
- uint32 session_id,
- const std::string& web_session_id) = 0;
-
- // Called when CDM wants to send a Message event.
- virtual void OnSessionMessage(int cdm_id,
- uint32 session_id,
- const std::vector<uint8>& message,
- const GURL& destination_url) = 0;
-
- // Called when CDM wants to send a Ready event.
- virtual void OnSessionReady(int cdm_id, uint32 session_id) = 0;
-
- // Called when CDM wants to send a Closed event.
- virtual void OnSessionClosed(int cdm_id, uint32 session_id) = 0;
-
- // Called when CDM wants to send an Error event.
- virtual void OnSessionError(int cdm_id,
- uint32 session_id,
- MediaKeys::KeyError error_code,
- uint32 system_code) = 0;
};
} // namespace media
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index 9b10b7850f..6eb5677b2c 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -28,11 +28,13 @@ MediaSourcePlayer::MediaSourcePlayer(
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb,
- scoped_ptr<DemuxerAndroid> demuxer)
+ scoped_ptr<DemuxerAndroid> demuxer,
+ const GURL& frame_url)
: MediaPlayerAndroid(player_id,
manager,
request_media_resources_cb,
- release_media_resources_cb),
+ release_media_resources_cb,
+ frame_url),
demuxer_(demuxer.Pass()),
pending_event_(NO_EVENT_PENDING),
playing_(false),
@@ -744,11 +746,15 @@ void MediaSourcePlayer::OnKeyAdded() {
void MediaSourcePlayer::OnCdmUnset() {
DVLOG(1) << __FUNCTION__;
- DCHECK(drm_bridge_);
// TODO(xhwang): Support detachment of CDM. This will be needed when we start
- // to support setMediaKeys(0), or when we release MediaDrm when the video is
- // paused, or when the device goes to sleep. See http://crbug.com/272421
- DVLOG(1) << "CDM detachment not supported.";
+ // to support setMediaKeys(0) (see http://crbug.com/330324), or when we
+ // release MediaDrm when the video is paused, or when the device goes to
+ // sleep (see http://crbug.com/272421).
+ NOTREACHED() << "CDM detachment not supported.";
+ DCHECK(drm_bridge_);
+ audio_decoder_job_->SetDrmBridge(NULL);
+ video_decoder_job_->SetDrmBridge(NULL);
+ drm_bridge_ = NULL;
}
} // namespace media
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index 819ae05b68..689c41eb54 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -42,7 +42,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb,
- scoped_ptr<DemuxerAndroid> demuxer);
+ scoped_ptr<DemuxerAndroid> demuxer,
+ const GURL& frame_url);
virtual ~MediaSourcePlayer();
// MediaPlayerAndroid implementation.
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 0d6c514a32..b3eaeafdb1 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -73,22 +73,7 @@ class MockMediaPlayerManager : public MediaPlayerManager {
int height) OVERRIDE {}
virtual MediaPlayerAndroid* GetFullscreenPlayer() OVERRIDE { return NULL; }
virtual MediaPlayerAndroid* GetPlayer(int player_id) OVERRIDE { return NULL; }
- virtual void DestroyAllMediaPlayers() OVERRIDE {}
- virtual BrowserCdm* GetCdm(int cdm_id) OVERRIDE { return NULL; }
virtual void RequestFullScreen(int player_id) OVERRIDE {}
- virtual void OnSessionCreated(int cdm_id,
- uint32 session_id,
- const std::string& web_session_id) OVERRIDE {}
- virtual void OnSessionMessage(int cdm_id,
- uint32 session_id,
- const std::vector<uint8>& message,
- const GURL& destination_url) OVERRIDE {}
- virtual void OnSessionReady(int cdm_id, uint32 session_id) OVERRIDE {}
- virtual void OnSessionClosed(int cdm_id, uint32 session_id) OVERRIDE {}
- virtual void OnSessionError(int cdm_id,
- uint32 session_id,
- MediaKeys::KeyError error_code,
- uint32 system_code) OVERRIDE {}
bool playback_completed() const {
return playback_completed_;
@@ -182,7 +167,8 @@ class MediaSourcePlayerTest : public testing::Test {
base::Unretained(&manager_)),
base::Bind(&MockMediaPlayerManager::OnMediaResourcesReleased,
base::Unretained(&manager_)),
- scoped_ptr<DemuxerAndroid>(demuxer_)),
+ scoped_ptr<DemuxerAndroid>(demuxer_),
+ GURL()),
decoder_callback_hook_executed_(false),
surface_texture_a_is_next_(true) {}
virtual ~MediaSourcePlayerTest() {}
@@ -616,10 +602,13 @@ class MediaSourcePlayerTest : public testing::Test {
// (prefetch). If false, regular data is fed and decoded prior to feeding the
// config change AU in response to the second data request (after prefetch
// completed). |config_unit_index| controls which access unit is
- // |kConfigChanged|.
+ // |kConfigChanged|. If |enable_adaptive_playback| is true, config change will
+ // not cause the decoder to recreate the media codec bridge. Otherwise, the
+ // decoder has to drain all its data before recreating the new codec.
void SendConfigChangeToDecoder(bool is_audio,
bool config_unit_in_prefetch,
- int config_unit_index) {
+ int config_unit_index,
+ bool enable_adaptive_playback) {
EXPECT_FALSE(GetMediaCodecBridge(is_audio));
if (is_audio) {
StartAudioDecoderJob();
@@ -631,10 +620,12 @@ class MediaSourcePlayerTest : public testing::Test {
int expected_num_data_requests = demuxer_->num_data_requests();
// Feed and decode a standalone access unit so the player exits prefetch.
if (!config_unit_in_prefetch) {
- if (is_audio)
+ if (is_audio) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
- else
+ } else {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ EnableAdaptiveVideoPlayback(enable_adaptive_playback);
+ }
WaitForDecodeDone(is_audio, !is_audio);
@@ -659,17 +650,24 @@ class MediaSourcePlayerTest : public testing::Test {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
else
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+
+ // If the adaptive playback setting was not passed to the MediaCodecBridge
+ // earlier, do it here.
+ if (config_unit_in_prefetch && !is_audio)
+ EnableAdaptiveVideoPlayback(enable_adaptive_playback);
}
// Send a config change to the decoder job and drain the decoder so that the
// config change is processed.
void StartConfigChange(bool is_audio,
bool config_unit_in_prefetch,
- int config_unit_index) {
+ int config_unit_index,
+ bool enable_adaptive_playback) {
SendConfigChangeToDecoder(is_audio, config_unit_in_prefetch,
- config_unit_index);
- EXPECT_EQ(!config_unit_in_prefetch && config_unit_index == 0,
- IsDrainingDecoder(is_audio));
+ config_unit_index, enable_adaptive_playback);
+
+ EXPECT_EQ(!config_unit_in_prefetch && !enable_adaptive_playback &&
+ config_unit_index == 0, IsDrainingDecoder(is_audio));
int expected_num_data_requests = demuxer_->num_data_requests();
// Run until decoder starts to request new data.
while (demuxer_->num_data_requests() == expected_num_data_requests)
@@ -677,6 +675,13 @@ class MediaSourcePlayerTest : public testing::Test {
EXPECT_FALSE(IsDrainingDecoder(is_audio));
}
+ void EnableAdaptiveVideoPlayback(bool enable) {
+ EXPECT_TRUE(GetMediaCodecBridge(false));
+ static_cast<VideoCodecBridge*>(GetMediaCodecBridge(false))->
+ set_adaptive_playback_supported_for_testing(
+ enable ? 1 : 0);
+ }
+
void CreateNextTextureAndSetVideoSurface() {
gfx::SurfaceTexture* surface_texture;
if (surface_texture_a_is_next_) {
@@ -1659,6 +1664,7 @@ TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
EXPECT_TRUE(GetMediaCodecBridge(true));
EXPECT_TRUE(GetMediaCodecBridge(false));
+ EnableAdaptiveVideoPlayback(false);
WaitForAudioVideoDecodeDone();
// Simulate audio |kConfigChanged| prefetched as standalone access unit.
@@ -1679,6 +1685,39 @@ TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) {
message_loop_.RunUntilIdle();
}
+TEST_F(MediaSourcePlayerTest,
+ SimultaneousAudioVideoConfigChangeWithAdaptivePlayback) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test that the player allows simultaneous audio and video config change with
+ // adaptive video playback enabled.
+ CreateNextTextureAndSetVideoSurface();
+ Start(CreateAudioVideoDemuxerConfigs());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ EXPECT_TRUE(GetMediaCodecBridge(true));
+ EXPECT_TRUE(GetMediaCodecBridge(false));
+ EnableAdaptiveVideoPlayback(true);
+ WaitForAudioVideoDecodeDone();
+
+ // Simulate audio |kConfigChanged| prefetched as standalone access unit.
+ DemuxerConfigs audio_configs = CreateAudioDemuxerConfigs(kCodecVorbis, true);
+ player_.OnDemuxerDataAvailable(
+ CreateReadFromDemuxerAckWithConfigChanged(true, 0, audio_configs));
+
+ // Simulate video |kConfigChanged| prefetched as standalone access unit.
+ player_.OnDemuxerDataAvailable(
+ CreateReadFromDemuxerAckWithConfigChanged(
+ false, 0, CreateVideoDemuxerConfigs(true)));
+ EXPECT_EQ(6, demuxer_->num_data_requests());
+ EXPECT_TRUE(IsDrainingDecoder(true));
+ EXPECT_FALSE(IsDrainingDecoder(false));
+
+ // Waiting for audio decoder to finish draining.
+ while (IsDrainingDecoder(true))
+ message_loop_.RunUntilIdle();
+}
+
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit0) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
@@ -1686,7 +1725,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit0) {
// the |kConfigChanged| unit is the very first unit in the set of units
// received in OnDemuxerDataAvailable() ostensibly while
// |PREFETCH_DONE_EVENT_PENDING|.
- StartConfigChange(true, true, 0);
+ StartConfigChange(true, true, 0, false);
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit1) {
@@ -1696,7 +1735,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit1) {
// the |kConfigChanged| unit is not the first unit in the set of units
// received in OnDemuxerDataAvailable() ostensibly while
// |PREFETCH_DONE_EVENT_PENDING|.
- StartConfigChange(true, true, 1);
+ StartConfigChange(true, true, 1, false);
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit0AfterPrefetch) {
@@ -1706,7 +1745,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit0AfterPrefetch) {
// the |kConfigChanged| unit is the very first unit in the set of units
// received in OnDemuxerDataAvailable() from data requested ostensibly while
// not prefetching.
- StartConfigChange(true, false, 0);
+ StartConfigChange(true, false, 0, false);
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit1AfterPrefetch) {
@@ -1716,7 +1755,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit1AfterPrefetch) {
// the |kConfigChanged| unit is not the first unit in the set of units
// received in OnDemuxerDataAvailable() from data requested ostensibly while
// not prefetching.
- StartConfigChange(true, false, 1);
+ StartConfigChange(true, false, 1, false);
}
TEST_F(MediaSourcePlayerTest, BrowserSeek_PrerollAfterBrowserSeek) {
@@ -1744,13 +1783,35 @@ TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChange) {
// Test that video config change notification results in creating a new
// video codec without any browser seek.
- StartConfigChange(false, true, 1);
+ StartConfigChange(false, true, 1, false);
// New video codec should have been created and configured, without any
// browser seek.
EXPECT_TRUE(GetMediaCodecBridge(false));
EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(0, demuxer_->num_seek_requests());
+
+ // 2 codecs should have been created, one before the config change, and one
+ // after it.
+ EXPECT_EQ(2, manager_.num_resources_requested());
+ EXPECT_EQ(1, manager_.num_resources_released());
+}
+
+TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChangeWithAdaptivePlayback) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test that if codec supports adaptive playback, no new codec should be
+ // created beyond the one used to decode the prefetch media data prior to
+ // the kConfigChanged.
+ StartConfigChange(false, true, 1, true);
+
+ // No browser seek should be needed.
+ EXPECT_TRUE(GetMediaCodecBridge(false));
+ EXPECT_EQ(3, demuxer_->num_data_requests());
+ EXPECT_EQ(0, demuxer_->num_seek_requests());
+
+ // Only 1 codec should have been created so far.
+ EXPECT_EQ(1, manager_.num_resources_requested());
}
TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedBySeek) {
@@ -1758,7 +1819,7 @@ TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedBySeek) {
// Test if a decoder is being drained while receiving a seek request, draining
// is canceled.
- SendConfigChangeToDecoder(true, false, 0);
+ SendConfigChangeToDecoder(true, false, 0, false);
EXPECT_TRUE(IsDrainingDecoder(true));
player_.SeekTo(base::TimeDelta::FromMilliseconds(100));
@@ -1775,7 +1836,7 @@ TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedByRelease) {
// Test if a decoder is being drained while receiving a release request,
// draining is canceled.
- SendConfigChangeToDecoder(true, false, 0);
+ SendConfigChangeToDecoder(true, false, 0, false);
EXPECT_TRUE(IsDrainingDecoder(true));
ReleasePlayer();
@@ -1796,7 +1857,7 @@ TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedBySurfaceChange) {
// Test if a video decoder is being drained while surface changes, draining
// is canceled.
- SendConfigChangeToDecoder(false, false, 0);
+ SendConfigChangeToDecoder(false, false, 0, false);
EXPECT_TRUE(IsDrainingDecoder(false));
CreateNextTextureAndSetVideoSurface();
@@ -2002,7 +2063,7 @@ TEST_F(MediaSourcePlayerTest, ConfigChangedThenReleaseThenStart) {
// Test if Release() occurs after |kConfigChanged| is processed, new data
// requested of demuxer, and the requested data arrive before the next
// Start(), then the player starts to decode the new data without any seek.
- StartConfigChange(true, true, 0);
+ StartConfigChange(true, true, 0, false);
ReleasePlayer();
EXPECT_TRUE(GetMediaCodecBridge(true));
diff --git a/media/base/android/video_decoder_job.cc b/media/base/android/video_decoder_job.cc
index 3e4f684163..d4e5f1e298 100644
--- a/media/base/android/video_decoder_job.cc
+++ b/media/base/android/video_decoder_job.cc
@@ -92,6 +92,26 @@ void VideoDecoderJob::UpdateDemuxerConfigs(const DemuxerConfigs& configs) {
set_is_content_encrypted(configs.is_video_encrypted);
}
+bool VideoDecoderJob::IsCodecReconfigureNeeded(
+ const DemuxerConfigs& configs) const {
+ if (!media_codec_bridge_)
+ return true;
+
+ if (!AreDemuxerConfigsChanged(configs))
+ return false;
+
+ bool only_size_changed = false;
+ if (video_codec_ == configs.video_codec &&
+ is_content_encrypted() == configs.is_video_encrypted) {
+ only_size_changed = true;
+ }
+
+ return !only_size_changed ||
+ !static_cast<VideoCodecBridge*>(media_codec_bridge_.get())->
+ IsAdaptivePlaybackSupported(configs.video_size.width(),
+ configs.video_size.height());
+}
+
bool VideoDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const {
return video_codec_ != configs.video_codec ||
diff --git a/media/base/android/video_decoder_job.h b/media/base/android/video_decoder_job.h
index 5193d586e9..aef2cc53c8 100644
--- a/media/base/android/video_decoder_job.h
+++ b/media/base/android/video_decoder_job.h
@@ -55,6 +55,8 @@ class VideoDecoderJob : public MediaDecoderJob {
const ReleaseOutputCompletionCallback& callback) OVERRIDE;
virtual bool ComputeTimeToRender() const OVERRIDE;
virtual void UpdateDemuxerConfigs(const DemuxerConfigs& configs) OVERRIDE;
+ virtual bool IsCodecReconfigureNeeded(
+ const DemuxerConfigs& configs) const OVERRIDE;
virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const OVERRIDE;
virtual bool CreateMediaCodecBridgeInternal() OVERRIDE;
diff --git a/media/base/audio_decoder.cc b/media/base/audio_decoder.cc
index 523fc01f61..5212794d98 100644
--- a/media/base/audio_decoder.cc
+++ b/media/base/audio_decoder.cc
@@ -12,8 +12,4 @@ AudioDecoder::AudioDecoder() {}
AudioDecoder::~AudioDecoder() {}
-scoped_refptr<AudioBuffer> AudioDecoder::GetDecodeOutput() {
- return NULL;
-}
-
} // namespace media
diff --git a/media/base/audio_decoder.h b/media/base/audio_decoder.h
index 5c5e294117..285d91b952 100644
--- a/media/base/audio_decoder.h
+++ b/media/base/audio_decoder.h
@@ -26,45 +26,51 @@ class MEDIA_EXPORT AudioDecoder {
enum Status {
kOk, // We're all good.
kAborted, // We aborted as a result of Stop() or Reset().
- kNotEnoughData, // Not enough data to produce a video frame.
kDecodeError, // A decoding error occurred.
kDecryptError // Decrypting error happened.
};
+ // Callback to return decoded buffers.
+ typedef base::Callback<void(const scoped_refptr<AudioBuffer>&)> OutputCB;
+
+ // Callback for Decode(). Called after the decoder has completed decoding
+ // corresponding DecoderBuffer, indicating that it's ready to accept another
+ // buffer to decode.
+ typedef base::Callback<void(Status)> DecodeCB;
+
AudioDecoder();
virtual ~AudioDecoder();
// Initializes an AudioDecoder with the given DemuxerStream, executing the
// callback upon completion.
- // statistics_cb is used to update global pipeline statistics.
+ // |statistics_cb| is used to update global pipeline statistics.
+ // |output_cb| is called for decoded audio buffers (see Decode()).
virtual void Initialize(const AudioDecoderConfig& config,
- const PipelineStatusCB& status_cb) = 0;
+ const PipelineStatusCB& status_cb,
+ const OutputCB& output_cb) = 0;
- // Requests samples to be decoded and returned via the provided callback.
- // Only one decode may be in flight at any given time.
+ // Requests samples to be decoded. Only one decode may be in flight at any
+ // given time. Once the buffer is decoded the decoder calls |decode_cb|.
+ // |output_cb| specified in Initialize() is called for each decoded buffer,
+ // before or after |decode_cb|.
//
- // Implementations guarantee that the callback will not be called from within
+ // Implementations guarantee that the callbacks will not be called from within
// this method.
//
- // Non-NULL sample buffer pointers will contain decoded audio data or may
- // indicate the end of the stream. A NULL buffer pointer indicates an aborted
- // Decode().
- typedef base::Callback<void(Status, const scoped_refptr<AudioBuffer>&)>
- DecodeCB;
+ // If |buffer| is an EOS buffer then the decoder must be flushed, i.e.
+ // |output_cb| must be called for each frame pending in the queue and
+ // |decode_cb| must be called after that.
virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB& decode_cb) = 0;
- // Some AudioDecoders will queue up multiple AudioBuffers from a single
- // DecoderBuffer, if we have any such queued buffers this will return the next
- // one. Otherwise we return a NULL AudioBuffer.
- virtual scoped_refptr<AudioBuffer> GetDecodeOutput();
-
- // Resets decoder state, dropping any queued encoded data.
+ // Resets decoder state. All pending Decode() requests will be finished or
+ // aborted before |closure| is called.
virtual void Reset(const base::Closure& closure) = 0;
// Stops decoder, fires any pending callbacks and sets the decoder to an
// uninitialized state. An AudioDecoder cannot be re-initialized after it has
- // been stopped.
+ // been stopped. DecodeCB and OutputCB may still be called for older buffers
+ // if they were scheduled before this method is called.
// Note that if Initialize() is pending or has finished successfully, Stop()
// must be called before destructing the decoder.
virtual void Stop() = 0;
diff --git a/media/base/audio_fifo.cc b/media/base/audio_fifo.cc
index b6e8f806e0..bdc7ddf78d 100644
--- a/media/base/audio_fifo.cc
+++ b/media/base/audio_fifo.cc
@@ -6,9 +6,6 @@
#include "base/logging.h"
-using base::subtle::Atomic32;
-using base::subtle::NoBarrier_Store;
-
namespace media {
// Given current position in the FIFO, the maximum number of elements in the
@@ -52,7 +49,6 @@ AudioFifo::~AudioFifo() {}
int AudioFifo::frames() const {
int delta = frames_pushed_ - frames_consumed_;
- base::subtle::MemoryBarrier();
return delta;
}
@@ -83,12 +79,7 @@ void AudioFifo::Push(const AudioBus* source) {
}
}
- // Ensure the data is *really* written before updating |frames_pushed_|.
- base::subtle::MemoryBarrier();
-
- Atomic32 new_frames_pushed = frames_pushed_ + source_size;
- NoBarrier_Store(&frames_pushed_, new_frames_pushed);
-
+ frames_pushed_ += source_size;
DCHECK_LE(frames(), max_frames());
write_pos_ = UpdatePos(write_pos_, source_size, max_frames());
}
@@ -128,9 +119,7 @@ void AudioFifo::Consume(AudioBus* destination,
}
}
- Atomic32 new_frames_consumed = frames_consumed_ + frames_to_consume;
- NoBarrier_Store(&frames_consumed_, new_frames_consumed);
-
+ frames_consumed_ += frames_to_consume;
read_pos_ = UpdatePos(read_pos_, frames_to_consume, max_frames());
}
diff --git a/media/base/audio_fifo.h b/media/base/audio_fifo.h
index e978ace05b..c00dd40fef 100644
--- a/media/base/audio_fifo.h
+++ b/media/base/audio_fifo.h
@@ -5,7 +5,6 @@
#ifndef MEDIA_BASE_AUDIO_FIFO_H_
#define MEDIA_BASE_AUDIO_FIFO_H_
-#include "base/atomicops.h"
#include "media/base/audio_bus.h"
#include "media/base/media_export.h"
@@ -15,8 +14,7 @@ namespace media {
// The maximum number of audio frames in the FIFO is set at construction and
// can not be extended dynamically. The allocated memory is utilized as a
// ring buffer.
-// This class is thread-safe in the limited sense that one thread may call
-// Push(), while a second thread calls Consume().
+// This class is thread-unsafe.
class MEDIA_EXPORT AudioFifo {
public:
// Creates a new AudioFifo and allocates |channels| of length |frames|.
@@ -51,8 +49,8 @@ class MEDIA_EXPORT AudioFifo {
const int max_frames_;
// Number of actual elements in the FIFO.
- volatile base::subtle::Atomic32 frames_pushed_;
- volatile base::subtle::Atomic32 frames_consumed_;
+ int frames_pushed_;
+ int frames_consumed_;
// Current read position.
int read_pos_;
diff --git a/media/base/browser_cdm.h b/media/base/browser_cdm.h
index 6e4e5cd39a..f009779324 100644
--- a/media/base/browser_cdm.h
+++ b/media/base/browser_cdm.h
@@ -5,17 +5,36 @@
#ifndef MEDIA_BASE_BROWSER_CDM_H_
#define MEDIA_BASE_BROWSER_CDM_H_
+#include "media/base/media_export.h"
#include "media/base/media_keys.h"
#include "media/base/player_tracker.h"
namespace media {
// Interface for browser side CDMs.
-class BrowserCdm : public MediaKeys, public PlayerTracker {
+class MEDIA_EXPORT BrowserCdm : public PlayerTracker {
public:
+ // TODO(jrummell): Update this to actually derive from MediaKeys
+ // (Use web_session_id rather than session_id).
+ typedef base::Callback<
+ void(uint32 session_id, const std::string& web_session_id)>
+ SessionCreatedCB;
+
+ typedef base::Callback<void(uint32 session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url)> SessionMessageCB;
+
+ typedef base::Callback<void(uint32 session_id)> SessionReadyCB;
+
+ typedef base::Callback<void(uint32 session_id)> SessionClosedCB;
+
+ typedef base::Callback<void(uint32 session_id,
+ media::MediaKeys::KeyError error_code,
+ uint32 system_code)> SessionErrorCB;
+
virtual ~BrowserCdm();
- // MediaKeys implementation.
+ // MediaKeys-like implementation.
virtual bool CreateSession(uint32 session_id,
const std::string& content_type,
const uint8* init_data,
diff --git a/media/base/browser_cdm_factory.h b/media/base/browser_cdm_factory.h
index 4e86c8ba59..e6fa47bcaf 100644
--- a/media/base/browser_cdm_factory.h
+++ b/media/base/browser_cdm_factory.h
@@ -8,23 +8,21 @@
#include <string>
#include "base/memory/scoped_ptr.h"
+#include "media/base/browser_cdm.h"
#include "media/base/media_export.h"
-#include "media/base/media_keys.h"
namespace media {
-class BrowserCdm;
-
// Creates a BrowserCdm for |key_system|. Returns NULL if the CDM cannot be
// created.
// TODO(xhwang): Add ifdef for IPC based CDM.
scoped_ptr<BrowserCdm> MEDIA_EXPORT
CreateBrowserCdm(const std::string& key_system,
- const SessionCreatedCB& session_created_cb,
- const SessionMessageCB& session_message_cb,
- const SessionReadyCB& session_ready_cb,
- const SessionClosedCB& session_closed_cb,
- const SessionErrorCB& session_error_cb);
+ const BrowserCdm::SessionCreatedCB& session_created_cb,
+ const BrowserCdm::SessionMessageCB& session_message_cb,
+ const BrowserCdm::SessionReadyCB& session_ready_cb,
+ const BrowserCdm::SessionClosedCB& session_closed_cb,
+ const BrowserCdm::SessionErrorCB& session_error_cb);
} // namespace media
diff --git a/media/base/cdm_promise.cc b/media/base/cdm_promise.cc
new file mode 100644
index 0000000000..ec5e913dbb
--- /dev/null
+++ b/media/base/cdm_promise.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/cdm_promise.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace media {
+
+CdmPromise::CdmPromise() : is_pending_(true) {
+}
+
+CdmPromise::CdmPromise(PromiseRejectedCB reject_cb)
+ : reject_cb_(reject_cb), is_pending_(true) {
+ DCHECK(!reject_cb_.is_null());
+}
+
+CdmPromise::~CdmPromise() {
+ DCHECK(!is_pending_);
+}
+
+void CdmPromise::reject(MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message) {
+ DCHECK(is_pending_);
+ is_pending_ = false;
+ reject_cb_.Run(exception_code, system_code, error_message);
+}
+
+template <typename T>
+CdmPromiseTemplate<T>::CdmPromiseTemplate(
+ base::Callback<void(const T&)> resolve_cb,
+ PromiseRejectedCB reject_cb)
+ : CdmPromise(reject_cb), resolve_cb_(resolve_cb) {
+ DCHECK(!resolve_cb_.is_null());
+}
+
+template <typename T>
+CdmPromiseTemplate<T>::~CdmPromiseTemplate() {
+ DCHECK(!is_pending_);
+}
+
+template <typename T>
+void CdmPromiseTemplate<T>::resolve(const T& result) {
+ DCHECK(is_pending_);
+ is_pending_ = false;
+ resolve_cb_.Run(result);
+}
+
+CdmPromiseTemplate<void>::CdmPromiseTemplate(base::Callback<void()> resolve_cb,
+ PromiseRejectedCB reject_cb)
+ : CdmPromise(reject_cb), resolve_cb_(resolve_cb) {
+ DCHECK(!resolve_cb_.is_null());
+}
+
+CdmPromiseTemplate<void>::CdmPromiseTemplate() {
+}
+
+CdmPromiseTemplate<void>::~CdmPromiseTemplate() {
+ DCHECK(!is_pending_);
+}
+
+void CdmPromiseTemplate<void>::resolve() {
+ DCHECK(is_pending_);
+ is_pending_ = false;
+ resolve_cb_.Run();
+}
+
+// Explicit template instantiation for the Promises needed.
+template class MEDIA_EXPORT CdmPromiseTemplate<std::string>;
+
+} // namespace media
diff --git a/media/base/cdm_promise.h b/media/base/cdm_promise.h
new file mode 100644
index 0000000000..ad1d196ad6
--- /dev/null
+++ b/media/base/cdm_promise.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_CDM_PROMISE_H_
+#define MEDIA_BASE_CDM_PROMISE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "media/base/media_export.h"
+#include "media/base/media_keys.h"
+
+namespace media {
+
+// Interface for promises being resolved/rejected in response to various
+// session actions. These may be called synchronously or asynchronously.
+// The promise must be resolved or rejected exactly once. It is expected that
+// the caller free the promise once it is resolved/rejected.
+//
+// This is only the base class, as parameter to resolve() varies.
+class MEDIA_EXPORT CdmPromise {
+ public:
+ typedef base::Callback<void(MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message)>
+ PromiseRejectedCB;
+
+ virtual ~CdmPromise();
+
+ // Used to indicate that the operation failed. |exception_code| must be
+ // specified. |system_code| is a Key System-specific value for the error
+ // that occurred, or 0 if there is no associated status code or such status
+ // codes are not supported by the Key System. |error_message| is optional.
+ virtual void reject(MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message);
+
+ protected:
+ CdmPromise();
+ CdmPromise(PromiseRejectedCB reject_cb);
+
+ PromiseRejectedCB reject_cb_;
+
+ // Keep track of whether the promise hasn't been resolved or rejected yet.
+ bool is_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(CdmPromise);
+};
+
+template <typename T>
+class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise {
+ public:
+ CdmPromiseTemplate(base::Callback<void(const T&)> resolve_cb,
+ PromiseRejectedCB rejected_cb);
+ virtual ~CdmPromiseTemplate();
+ virtual void resolve(const T& result);
+
+ private:
+ base::Callback<void(const T&)> resolve_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate);
+};
+
+// Specialization for no parameter to resolve().
+template <>
+class MEDIA_EXPORT CdmPromiseTemplate<void> : public CdmPromise {
+ public:
+ CdmPromiseTemplate(base::Callback<void(void)> resolve_cb,
+ PromiseRejectedCB rejected_cb);
+ virtual ~CdmPromiseTemplate();
+ virtual void resolve();
+
+ protected:
+ // Allow subclasses to completely override the implementation.
+ CdmPromiseTemplate();
+
+ private:
+ base::Callback<void(void)> resolve_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_CDM_PROMISE_H_
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index c9c851c1bd..c6b2515f17 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -82,9 +82,6 @@ class MEDIA_EXPORT Demuxer {
// to be DemuxerStream::TEXT), or NULL if that type of stream is not present.
virtual DemuxerStream* GetStream(DemuxerStream::Type type) = 0;
- // Returns the starting time for the media file.
- virtual base::TimeDelta GetStartTime() const = 0;
-
// Returns Time represented by presentation timestamp 0.
// If the timstamps are not associated with a Time, then
// a null Time is returned.
diff --git a/media/base/media_keys.h b/media/base/media_keys.h
index 3166063dd2..d581ae4e8b 100644
--- a/media/base/media_keys.h
+++ b/media/base/media_keys.h
@@ -18,6 +18,12 @@ namespace media {
class Decryptor;
+template <typename T>
+class CdmPromiseTemplate;
+
+typedef CdmPromiseTemplate<std::string> NewSessionCdmPromise;
+typedef CdmPromiseTemplate<void> SimpleCdmPromise;
+
// Performs media key operations.
//
// All key operations are called on the renderer thread. Therefore, these calls
@@ -27,6 +33,8 @@ class MEDIA_EXPORT MediaKeys {
// Reported to UMA, so never reuse a value!
// Must be kept in sync with blink::WebMediaPlayerClient::MediaKeyErrorCode
// (enforced in webmediaplayer_impl.cc).
+ // TODO(jrummell): Can this be moved to proxy_decryptor as it should only be
+ // used by the prefixed EME code?
enum KeyError {
kUnknownError = 1,
kClientError,
@@ -38,35 +46,55 @@ class MEDIA_EXPORT MediaKeys {
kMaxKeyError // Must be last and greater than any legit value.
};
+ // Must be a superset of cdm::MediaKeyException.
+ enum Exception {
+ NOT_SUPPORTED_ERROR,
+ INVALID_STATE_ERROR,
+ INVALID_ACCESS_ERROR,
+ QUOTA_EXCEEDED_ERROR,
+ UNKNOWN_ERROR,
+ CLIENT_ERROR,
+ OUTPUT_ERROR
+ };
+
+ // Type of license required when creating/loading a session.
+ // Must be consistent with the values specified in the spec:
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#extensions
+ enum SessionType {
+ TEMPORARY_SESSION,
+ PERSISTENT_SESSION
+ };
+
const static uint32 kInvalidSessionId = 0;
MediaKeys();
virtual ~MediaKeys();
- // Creates a session with the |content_type| and |init_data| provided.
- // Returns true if a session is successfully created, false otherwise.
+ // Creates a session with the |init_data_type|, |init_data| and |session_type|
+ // provided.
// Note: UpdateSession() and ReleaseSession() should only be called after
- // SessionCreatedCB is fired.
- // TODO(jrummell): Remove return value when prefixed API is removed.
- // See http://crbug.com/342510
- virtual bool CreateSession(uint32 session_id,
- const std::string& content_type,
+ // |promise| is resolved.
+ virtual void CreateSession(const std::string& init_data_type,
const uint8* init_data,
- int init_data_length) = 0;
+ int init_data_length,
+ SessionType session_type,
+ scoped_ptr<NewSessionCdmPromise> promise) = 0;
// Loads a session with the |web_session_id| provided.
// Note: UpdateSession() and ReleaseSession() should only be called after
- // SessionCreatedCB is fired.
- virtual void LoadSession(uint32 session_id,
- const std::string& web_session_id) = 0;
+ // |promise| is resolved.
+ virtual void LoadSession(const std::string& web_session_id,
+ scoped_ptr<NewSessionCdmPromise> promise) = 0;
- // Updates a session specified by |session_id| with |response|.
- virtual void UpdateSession(uint32 session_id,
+ // Updates a session specified by |web_session_id| with |response|.
+ virtual void UpdateSession(const std::string& web_session_id,
const uint8* response,
- int response_length) = 0;
+ int response_length,
+ scoped_ptr<SimpleCdmPromise> promise) = 0;
- // Releases the session specified by |session_id|.
- virtual void ReleaseSession(uint32 session_id) = 0;
+ // Releases the session specified by |web_session_id|.
+ virtual void ReleaseSession(const std::string& web_session_id,
+ scoped_ptr<SimpleCdmPromise> promise) = 0;
// Gets the Decryptor object associated with the MediaKeys. Returns NULL if
// no Decryptor object is associated. The returned object is only guaranteed
@@ -79,21 +107,18 @@ class MEDIA_EXPORT MediaKeys {
// Key event callbacks. See the spec for details:
// https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#event-summary
-typedef base::Callback<
- void(uint32 session_id, const std::string& web_session_id)>
- SessionCreatedCB;
-
-typedef base::Callback<void(uint32 session_id,
+typedef base::Callback<void(const std::string& web_session_id,
const std::vector<uint8>& message,
const GURL& destination_url)> SessionMessageCB;
-typedef base::Callback<void(uint32 session_id)> SessionReadyCB;
+typedef base::Callback<void(const std::string& web_session_id)> SessionReadyCB;
-typedef base::Callback<void(uint32 session_id)> SessionClosedCB;
+typedef base::Callback<void(const std::string& web_session_id)> SessionClosedCB;
-typedef base::Callback<void(uint32 session_id,
- media::MediaKeys::KeyError error_code,
- uint32 system_code)> SessionErrorCB;
+typedef base::Callback<void(const std::string& web_session_id,
+ MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message)> SessionErrorCB;
} // namespace media
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index c8548b5e1f..9c5353312f 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -38,7 +38,6 @@ class MockDemuxer : public Demuxer {
MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD0(OnAudioRendererDisabled, void());
MOCK_METHOD1(GetStream, DemuxerStream*(DemuxerStream::Type));
- MOCK_CONST_METHOD0(GetStartTime, base::TimeDelta());
MOCK_CONST_METHOD0(GetTimelineOffset, base::Time());
MOCK_CONST_METHOD0(GetLiveness, Liveness());
@@ -76,9 +75,10 @@ class MockVideoDecoder : public VideoDecoder {
virtual ~MockVideoDecoder();
// VideoDecoder implementation.
- MOCK_METHOD3(Initialize, void(const VideoDecoderConfig& config,
+ MOCK_METHOD4(Initialize, void(const VideoDecoderConfig& config,
bool low_delay,
- const PipelineStatusCB&));
+ const PipelineStatusCB& status_cb,
+ const OutputCB& output_cb));
MOCK_METHOD2(Decode, void(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB&));
MOCK_METHOD1(Reset, void(const base::Closure&));
@@ -95,8 +95,10 @@ class MockAudioDecoder : public AudioDecoder {
virtual ~MockAudioDecoder();
// AudioDecoder implementation.
- MOCK_METHOD2(Initialize, void(const AudioDecoderConfig& config,
- const PipelineStatusCB&));
+ MOCK_METHOD3(Initialize,
+ void(const AudioDecoderConfig& config,
+ const PipelineStatusCB& status_cb,
+ const OutputCB& output_cb));
MOCK_METHOD2(Decode,
void(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB&));
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index 46be2b86e6..2884c38993 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -386,7 +386,7 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
base::AutoLock l(lock_);
// We do not want to start the clock running. We only want to set the
// base media time so our timestamp calculations will be correct.
- clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime());
+ clock_->SetTime(base::TimeDelta(), base::TimeDelta());
}
if (!audio_renderer_ && !video_renderer_) {
done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
@@ -445,7 +445,7 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
DCHECK(!pending_callbacks_.get());
SerialRunner::Queue bound_fns;
- base::TimeDelta seek_timestamp = demuxer_->GetStartTime();
+ const base::TimeDelta seek_timestamp = base::TimeDelta();
// Preroll renderers.
if (audio_renderer_) {
@@ -743,7 +743,6 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
DCHECK(seek_cb_.is_null());
SetState(kSeeking);
- base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
seek_cb_ = seek_cb;
audio_ended_ = false;
video_ended_ = false;
@@ -753,9 +752,9 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
{
base::AutoLock auto_lock(lock_);
PauseClockAndStopRendering_Locked();
- clock_->SetTime(seek_timestamp, seek_timestamp);
+ clock_->SetTime(time, time);
}
- DoSeek(seek_timestamp, base::Bind(
+ DoSeek(time, base::Bind(
&Pipeline::OnStateTransition, base::Unretained(this)));
}
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index 45cc73ba36..05ffc8c391 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -107,9 +107,6 @@ class PipelineTest : public ::testing::Test {
EXPECT_CALL(*demuxer_, GetStream(_))
.WillRepeatedly(Return(null_pointer));
- EXPECT_CALL(*demuxer_, GetStartTime())
- .WillRepeatedly(Return(base::TimeDelta()));
-
EXPECT_CALL(*demuxer_, GetTimelineOffset())
.WillRepeatedly(Return(base::Time()));
@@ -174,7 +171,7 @@ class PipelineTest : public ::testing::Test {
EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
// Startup sequence.
- EXPECT_CALL(*video_renderer_, Preroll(demuxer_->GetStartTime(), _))
+ EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*video_renderer_, Play(_))
.WillOnce(RunClosure<0>());
@@ -739,43 +736,6 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, StartTimeIsZero) {
- CreateVideoStream();
- MockDemuxerStreamVector streams;
- streams.push_back(video_stream());
-
- const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
-
- InitializePipeline(PIPELINE_OK);
- EXPECT_FALSE(metadata_.has_audio);
- EXPECT_TRUE(metadata_.has_video);
-
- EXPECT_EQ(base::TimeDelta(), pipeline_->GetMediaTime());
-}
-
-TEST_F(PipelineTest, StartTimeIsNonZero) {
- const base::TimeDelta kStartTime = base::TimeDelta::FromSeconds(4);
- const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
-
- EXPECT_CALL(*demuxer_, GetStartTime())
- .WillRepeatedly(Return(kStartTime));
-
- CreateVideoStream();
- MockDemuxerStreamVector streams;
- streams.push_back(video_stream());
-
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
-
- InitializePipeline(PIPELINE_OK);
- EXPECT_FALSE(metadata_.has_audio);
- EXPECT_TRUE(metadata_.has_video);
-
- EXPECT_EQ(kStartTime, pipeline_->GetMediaTime());
-}
-
static void RunTimeCB(const AudioRenderer::TimeCB& time_cb,
int time_in_ms,
int max_time_in_ms) {
diff --git a/media/base/video_decoder.cc b/media/base/video_decoder.cc
index 118e2a3e3a..9a6de2fdd3 100644
--- a/media/base/video_decoder.cc
+++ b/media/base/video_decoder.cc
@@ -12,10 +12,6 @@ VideoDecoder::VideoDecoder() {}
VideoDecoder::~VideoDecoder() {}
-scoped_refptr<VideoFrame> VideoDecoder::GetDecodeOutput() {
- return NULL;
-}
-
bool VideoDecoder::NeedsBitstreamConversion() const {
return false;
}
diff --git a/media/base/video_decoder.h b/media/base/video_decoder.h
index 226b7b2f01..5111f53e9f 100644
--- a/media/base/video_decoder.h
+++ b/media/base/video_decoder.h
@@ -25,16 +25,24 @@ class MEDIA_EXPORT VideoDecoder {
enum Status {
kOk, // Everything went as planned.
kAborted, // Decode was aborted as a result of Reset() being called.
- kNotEnoughData, // Not enough data to produce a video frame.
kDecodeError, // Decoding error happened.
kDecryptError // Decrypting error happened.
};
+ // Callback to return decode frames.
+ typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> OutputCB;
+
+ // Callback type for Decode(). Called after the decoder has completed decoding
+ // corresponding DecoderBuffer, indicating that it's ready to accept another
+ // buffer to decode.
+ typedef base::Callback<void(Status status)> DecodeCB;
+
VideoDecoder();
virtual ~VideoDecoder();
// Initializes a VideoDecoder with the given |config|, executing the
- // |status_cb| upon completion.
+ // |status_cb| upon completion. |output_cb| is called for each output frame
+ // decoded by Decode().
//
// Note:
// 1) The VideoDecoder will be reinitialized if it was initialized before.
@@ -44,7 +52,8 @@ class MEDIA_EXPORT VideoDecoder {
// |status_cb| is executed.
virtual void Initialize(const VideoDecoderConfig& config,
bool low_delay,
- const PipelineStatusCB& status_cb) = 0;
+ const PipelineStatusCB& status_cb,
+ const OutputCB& output_cb) = 0;
// Requests a |buffer| to be decoded. The status of the decoder and decoded
// frame are returned via the provided callback. Some decoders may allow
@@ -58,25 +67,18 @@ class MEDIA_EXPORT VideoDecoder {
// Decode() calls (i.e. |decode_cb| will be called even Decode() is never
// called again).
//
- // If the returned status is kOk:
- // - Non-EOS (end of stream) frame contains decoded video data.
- // - EOS frame indicates the end of the stream.
- // Otherwise the returned frame must be NULL.
- typedef base::Callback<void(Status,
- const scoped_refptr<VideoFrame>&)> DecodeCB;
+ // After decoding is finished the decoder calls |output_cb| specified in
+ // Initialize() for each decoded frame. |output_cb| may be called before or
+ // after |decode_cb|.
+ //
+ // If |buffer| is an EOS buffer then the decoder must be flushed, i.e.
+ // |output_cb| must be called for each frame pending in the queue and
+ // |decode_cb| must be called after that.
virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB& decode_cb) = 0;
- // Some VideoDecoders may queue up multiple VideoFrames from a single
- // DecoderBuffer, if we have any such queued frames this will return the next
- // one. Otherwise we return a NULL VideoFrame.
- //
- // TODO(xhwang): Revisit this method.
- virtual scoped_refptr<VideoFrame> GetDecodeOutput();
-
- // Resets decoder state, fulfilling all pending DecodeCB and dropping extra
- // queued decoded data. After this call, the decoder is back to an initialized
- // clean state.
+ // Resets decoder state. All pending Decode() requests will be finished or
+ // aborted before |closure| is called.
// Note: No VideoDecoder calls should be made before |closure| is executed.
virtual void Reset(const base::Closure& closure) = 0;