diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-06-20 14:52:04 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-06-20 14:52:04 +0100 |
commit | f8ee788a64d60abd8f2d742a5fdedde054ecd910 (patch) | |
tree | 7dc14380200b953c64e0ccd16435cdbd1dbf1205 /media/base | |
parent | fcbbbe23a38088a52492922075e71a419c4b01ec (diff) | |
download | chromium_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')
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; |