diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-08-05 13:57:33 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-08-05 13:57:33 +0100 |
commit | a36e5920737c6adbddd3e43b760e5de8431db6e0 (patch) | |
tree | 347d048bb8c8828d50113bf94ace40bf0613f2cd /media/base | |
parent | 34378da0e9429d394aafdaa771301aff58447cb1 (diff) | |
download | chromium_org-a36e5920737c6adbddd3e43b760e5de8431db6e0.tar.gz |
Merge from Chromium at DEPS revision r215573
This commit was generated by merge_to_master.py.
Change-Id: Ib95814f98e5765b459dd32425f9bf9138edf2bca
Diffstat (limited to 'media/base')
19 files changed, 324 insertions, 126 deletions
diff --git a/media/base/android/demuxer_stream_player_params.cc b/media/base/android/demuxer_stream_player_params.cc index 3ed1a8c446..827be11956 100644 --- a/media/base/android/demuxer_stream_player_params.cc +++ b/media/base/android/demuxer_stream_player_params.cc @@ -19,6 +19,10 @@ MediaPlayerHostMsg_DemuxerReady_Params:: MediaPlayerHostMsg_DemuxerReady_Params:: ~MediaPlayerHostMsg_DemuxerReady_Params() {} +AccessUnit::AccessUnit() : end_of_stream(false) {} + +AccessUnit::~AccessUnit() {} + MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: MediaPlayerHostMsg_ReadFromDemuxerAck_Params() : type(DemuxerStream::UNKNOWN) {} @@ -26,9 +30,4 @@ MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params() {} -MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::AccessUnit() - : end_of_stream(false) {} - -MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::~AccessUnit() {} - } // namespace media diff --git a/media/base/android/demuxer_stream_player_params.h b/media/base/android/demuxer_stream_player_params.h index 585af2b4c7..a9fb0520ae 100644 --- a/media/base/android/demuxer_stream_player_params.h +++ b/media/base/android/demuxer_stream_player_params.h @@ -36,21 +36,21 @@ struct MEDIA_EXPORT MediaPlayerHostMsg_DemuxerReady_Params { std::string key_system; }; -struct MEDIA_EXPORT MediaPlayerHostMsg_ReadFromDemuxerAck_Params { - struct MEDIA_EXPORT AccessUnit { - AccessUnit(); - ~AccessUnit(); - - DemuxerStream::Status status; - bool end_of_stream; - // TODO(ycheo): Use the shared memory to transfer the block data. - std::vector<uint8> data; - base::TimeDelta timestamp; - std::vector<char> key_id; - std::vector<char> iv; - std::vector<media::SubsampleEntry> subsamples; - }; +struct MEDIA_EXPORT AccessUnit { + AccessUnit(); + ~AccessUnit(); + + DemuxerStream::Status status; + bool end_of_stream; + // TODO(ycheo): Use the shared memory to transfer the block data. + std::vector<uint8> data; + base::TimeDelta timestamp; + std::vector<char> key_id; + std::vector<char> iv; + std::vector<media::SubsampleEntry> subsamples; +}; +struct MEDIA_EXPORT MediaPlayerHostMsg_ReadFromDemuxerAck_Params { MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); 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 26c0731ce2..ed5d9478c5 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java @@ -285,6 +285,13 @@ class MediaCodecBridge { } } + @CalledByNative + private void setVolume(double volume) { + if (mAudioTrack != null) { + mAudioTrack.setStereoVolume((float) volume, (float) volume); + } + } + private void resetLastPresentationTimeIfNeeded(long presentationTimeUs) { if (mFlushed) { mLastPresentationTimeUs = diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java index e1b0e09475..4b0a1aa6d1 100644 --- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java @@ -85,8 +85,8 @@ public class MediaPlayerBridge { } @CalledByNative - protected void setVolume(float leftVolume, float rightVolume) { - getLocalPlayer().setVolume(leftVolume, rightVolume); + protected void setVolume(double volume) { + getLocalPlayer().setVolume((float) volume, (float) volume); } @CalledByNative diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java index 8d67f5ed9f..f055f35ed6 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCapture.java +++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java @@ -31,9 +31,35 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { public int mDesiredFps = 0; } + // Some devices with OS older than JELLY_BEAN don't support YV12 format correctly. + // Some devices don't support YV12 format correctly even with JELLY_BEAN or newer OS. + // To work around the issues on those devices, we'd have to request NV21. + // This is a temporary hack till device manufacturers fix the problem or + // we don't need to support those devices any more. + private static class DeviceImageFormatHack { + private static final String[] sBUGGY_DEVICE_LIST = { + "SAMSUNG-SGH-I747", + }; + + static int getImageFormat() { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { + return ImageFormat.NV21; + } + + for (String buggyDevice : sBUGGY_DEVICE_LIST) { + if (buggyDevice.contentEquals(android.os.Build.MODEL)) { + return ImageFormat.NV21; + } + } + + return ImageFormat.YV12; + } + } + private Camera mCamera; public ReentrantLock mPreviewBufferLock = new ReentrantLock(); - private int mPixelFormat = ImageFormat.YV12; + private int mImageFormat = ImageFormat.YV12; + private byte[] mColorPlane = null; private Context mContext = null; // True when native code has started capture. private boolean mIsRunning = false; @@ -147,8 +173,10 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { Log.d(TAG, "allocate: matched width=" + matchedWidth + ", height=" + matchedHeight); + calculateImageFormat(matchedWidth, matchedHeight); + parameters.setPreviewSize(matchedWidth, matchedHeight); - parameters.setPreviewFormat(mPixelFormat); + parameters.setPreviewFormat(mImageFormat); parameters.setPreviewFpsRange(fpsMin, fpsMax); mCamera.setParameters(parameters); @@ -174,7 +202,7 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { mCamera.setPreviewTexture(mSurfaceTexture); int bufSize = matchedWidth * matchedHeight * - ImageFormat.getBitsPerPixel(mPixelFormat) / 8; + ImageFormat.getBitsPerPixel(mImageFormat) / 8; for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) { byte[] buffer = new byte[bufSize]; mCamera.addCallbackBuffer(buffer); @@ -291,6 +319,9 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { } else { rotation = (mCameraOrientation - rotation + 360) % 360; } + if (mImageFormat == ImageFormat.NV21) { + convertNV21ToYV12(data); + } nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, data, mExpectedFrameSize, rotation, flipVertical, flipHorizontal); @@ -377,4 +408,22 @@ public class VideoCapture implements PreviewCallback, OnFrameAvailableListener { } return orientation; } + + private void calculateImageFormat(int width, int height) { + mImageFormat = DeviceImageFormatHack.getImageFormat(); + if (mImageFormat == ImageFormat.NV21) { + mColorPlane = new byte[width * height / 4]; + } + } + + private void convertNV21ToYV12(byte[] data) { + final int ySize = mCurrentCapability.mWidth * mCurrentCapability.mHeight; + final int uvSize = ySize / 4; + for (int i = 0; i < uvSize; i++) { + final int index = ySize + i * 2; + data[ySize + i] = data[index]; + mColorPlane[i] = data[index + 1]; + } + System.arraycopy(mColorPlane, 0, data, ySize + uvSize, uvSize); + } } diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc index a3c15c0b89..ab54936780 100644 --- a/media/base/android/media_codec_bridge.cc +++ b/media/base/android/media_codec_bridge.cc @@ -373,6 +373,11 @@ void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { env, media_codec(), byte_array.obj()); } +void AudioCodecBridge::SetVolume(double volume) { + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_setVolume(env, media_codec(), volume); +} + VideoCodecBridge::VideoCodecBridge(const char* mime) : MediaCodecBridge(mime) { } diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h index d28d39df12..3469b1804e 100644 --- a/media/base/android/media_codec_bridge.h +++ b/media/base/android/media_codec_bridge.h @@ -75,8 +75,9 @@ class MEDIA_EXPORT MediaCodecBridge { // Submits an empty buffer with a EOS (END OF STREAM) flag. void QueueEOS(int input_buffer_index); - // Returns the index of an input buffer to be filled with valid data or - // INFO_TRY_AGAIN_LATER if no such buffer is currently available. + // Returns an index (>=0) of an input buffer to be filled with valid data, + // INFO_TRY_AGAIN_LATER if no such buffer is currently available, or + // INFO_MEDIA_CODEC_ERROR if unexpected error happens. // Use kTimeOutInfinity for infinite timeout. int DequeueInputBuffer(base::TimeDelta timeout); @@ -132,6 +133,9 @@ class AudioCodecBridge : public MediaCodecBridge { // DequeueOutputBuffer() and before ReleaseOutputBuffer. void PlayOutputBuffer(int index, size_t size); + // Set the volume of the audio output. + void SetVolume(double volume); + private: explicit AudioCodecBridge(const char* mime); diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h index 18831b668d..f1c9c37ee0 100644 --- a/media/base/android/media_player_android.h +++ b/media/base/android/media_player_android.h @@ -71,7 +71,7 @@ class MEDIA_EXPORT MediaPlayerAndroid { virtual void Release() = 0; // Set the player volume. - virtual void SetVolume(float leftVolume, float rightVolume) = 0; + virtual void SetVolume(double volume) = 0; // Get the media information from the player. virtual int GetVideoWidth() = 0; diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index 15a26ece3f..e0d9868827 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc @@ -300,14 +300,14 @@ void MediaPlayerBridge::Release() { listener_.ReleaseMediaPlayerListenerResources(); } -void MediaPlayerBridge::SetVolume(float left_volume, float right_volume) { +void MediaPlayerBridge::SetVolume(double volume) { if (j_media_player_bridge_.is_null()) return; JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); Java_MediaPlayerBridge_setVolume( - env, j_media_player_bridge_.obj(), left_volume, right_volume); + env, j_media_player_bridge_.obj(), volume); } void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) { diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index 421bcb3e01..85a2960405 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h @@ -58,7 +58,7 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid { virtual void Pause() OVERRIDE; virtual void SeekTo(base::TimeDelta time) OVERRIDE; virtual void Release() OVERRIDE; - virtual void SetVolume(float leftVolume, float rightVolume) OVERRIDE; + virtual void SetVolume(double volume) OVERRIDE; virtual int GetVideoWidth() OVERRIDE; virtual int GetVideoHeight() OVERRIDE; virtual base::TimeDelta GetCurrentTime() OVERRIDE; diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc index 5673b926a0..5d4aa98b66 100644 --- a/media/base/android/media_source_player.cc +++ b/media/base/android/media_source_player.cc @@ -22,7 +22,7 @@ namespace { // Timeout value for media codec operations. Because the first // DequeInputBuffer() can take about 150 milliseconds, use 250 milliseconds // here. See b/9357571. -const int kMediaCodecTimeoutInMicroseconds = 250000; +const int kMediaCodecTimeoutInMilliseconds = 250; // Use 16bit PCM for audio output. Keep this value in sync with the output // format we passed to AudioTrack in MediaCodecBridge. @@ -82,6 +82,8 @@ class AudioDecoderJob : public MediaDecoderJob { const AudioCodec audio_codec, int sample_rate, int channel_count, const uint8* extra_data, size_t extra_data_size, jobject media_crypto); + void SetVolume(double volume); + private: AudioDecoderJob(MediaCodecBridge* media_codec_bridge); }; @@ -100,7 +102,7 @@ class VideoDecoderJob : public MediaDecoderJob { }; void MediaDecoderJob::Decode( - const MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit& unit, + const AccessUnit& unit, const base::TimeTicks& start_time_ticks, const base::TimeDelta& start_presentation_timestamp, const MediaDecoderJob::DecoderCallback& callback) { @@ -114,43 +116,55 @@ void MediaDecoderJob::Decode( needs_flush_ = false; } +MediaDecoderJob::DecodeStatus MediaDecoderJob::QueueInputBuffer( + const AccessUnit& unit) { + base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( + kMediaCodecTimeoutInMilliseconds); + int input_buf_index = media_codec_bridge_->DequeueInputBuffer(timeout); + if (input_buf_index == MediaCodecBridge::INFO_MEDIA_CODEC_ERROR) + return DECODE_FAILED; + if (input_buf_index == MediaCodecBridge::INFO_TRY_AGAIN_LATER) + return DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER; + + // TODO(qinmin): skip frames if video is falling far behind. + DCHECK(input_buf_index >= 0); + if (unit.end_of_stream || unit.data.empty()) { + media_codec_bridge_->QueueEOS(input_buf_index); + } else if (unit.key_id.empty()) { + media_codec_bridge_->QueueInputBuffer( + input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); + } else { + if (unit.iv.empty() || unit.subsamples.empty()) { + LOG(ERROR) << "The access unit doesn't have iv or subsamples while it " + << "has key IDs!"; + return DECODE_FAILED; + } + media_codec_bridge_->QueueSecureInputBuffer( + input_buf_index, &unit.data[0], unit.data.size(), + reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), + reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), + &unit.subsamples[0], unit.subsamples.size(), unit.timestamp); + } + + return DECODE_SUCCEEDED; +} + void MediaDecoderJob::DecodeInternal( - const MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit& unit, + const AccessUnit& unit, const base::TimeTicks& start_time_ticks, const base::TimeDelta& start_presentation_timestamp, bool needs_flush, const MediaDecoderJob::DecoderCallback& callback) { - if (needs_flush) + if (needs_flush) { + DVLOG(1) << "DecodeInternal needs flush."; media_codec_bridge_->Reset(); - base::TimeDelta timeout = base::TimeDelta::FromMicroseconds( - kMediaCodecTimeoutInMicroseconds); - int input_buf_index = media_codec_bridge_->DequeueInputBuffer(timeout); - if (input_buf_index == MediaCodecBridge::INFO_MEDIA_CODEC_ERROR) { - ui_loop_->PostTask(FROM_HERE, base::Bind( - callback, DECODE_FAILED, start_presentation_timestamp, 0)); - return; } - // TODO(qinmin): skip frames if video is falling far behind. - if (input_buf_index >= 0) { - if (unit.end_of_stream || unit.data.empty()) { - media_codec_bridge_->QueueEOS(input_buf_index); - } else if (unit.key_id.empty()){ - media_codec_bridge_->QueueInputBuffer( - input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); - } else { - if (unit.iv.empty() || unit.subsamples.empty()) { - LOG(ERROR) << "The access unit doesn't have iv or subsamples while it " - << "has key IDs!"; - ui_loop_->PostTask(FROM_HERE, base::Bind( - callback, DECODE_FAILED, start_presentation_timestamp, 0)); - return; - } - media_codec_bridge_->QueueSecureInputBuffer( - input_buf_index, &unit.data[0], unit.data.size(), - reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), - reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), - &unit.subsamples[0], unit.subsamples.size(), unit.timestamp); - } + + DecodeStatus decode_status = QueueInputBuffer(unit); + if (decode_status != DECODE_SUCCEEDED) { + ui_loop_->PostTask(FROM_HERE, + base::Bind(callback, decode_status, start_presentation_timestamp, 0)); + return; } size_t offset = 0; @@ -158,9 +172,11 @@ void MediaDecoderJob::DecodeInternal( base::TimeDelta presentation_timestamp; bool end_of_stream = false; + base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( + kMediaCodecTimeoutInMilliseconds); int outputBufferIndex = media_codec_bridge_->DequeueOutputBuffer( timeout, &offset, &size, &presentation_timestamp, &end_of_stream); - DecodeStatus decode_status = DECODE_SUCCEEDED; + if (end_of_stream) decode_status = DECODE_END_OF_STREAM; switch (outputBufferIndex) { @@ -172,7 +188,7 @@ void MediaDecoderJob::DecodeInternal( decode_status = DECODE_FORMAT_CHANGED; break; case MediaCodecBridge::INFO_TRY_AGAIN_LATER: - decode_status = DECODE_TRY_AGAIN_LATER; + decode_status = DECODE_TRY_DEQUEUE_OUTPUT_AGAIN_LATER; break; case MediaCodecBridge::INFO_MEDIA_CODEC_ERROR: decode_status = DECODE_FAILED; @@ -257,7 +273,7 @@ VideoDecoderJob* VideoDecoderJob::Create( const VideoCodec video_codec, const gfx::Size& size, jobject surface, jobject media_crypto) { scoped_ptr<VideoCodecBridge> codec(VideoCodecBridge::Create(video_codec)); - if (codec->Start(video_codec, size, surface, media_crypto)) + if (codec && codec->Start(video_codec, size, surface, media_crypto)) return new VideoDecoderJob(codec.release()); return NULL; } @@ -275,8 +291,8 @@ AudioDecoderJob* AudioDecoderJob::Create( size_t extra_data_size, jobject media_crypto) { scoped_ptr<AudioCodecBridge> codec(AudioCodecBridge::Create(audio_codec)); - if (codec->Start(audio_codec, sample_rate, channel_count, extra_data, - extra_data_size, true, media_crypto)) { + if (codec && codec->Start(audio_codec, sample_rate, channel_count, extra_data, + extra_data_size, true, media_crypto)) { return new AudioDecoderJob(codec.release()); } return NULL; @@ -287,6 +303,10 @@ AudioDecoderJob::AudioDecoderJob(MediaCodecBridge* media_codec_bridge) media_codec_bridge, true) {} +void AudioDecoderJob::SetVolume(double volume) { + static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->SetVolume(volume); +} + MediaSourcePlayer::MediaSourcePlayer( int player_id, MediaPlayerManager* manager) @@ -304,6 +324,7 @@ MediaSourcePlayer::MediaSourcePlayer( playing_(false), is_audio_encrypted_(false), is_video_encrypted_(false), + volume_(-1.0), clock_(&default_tick_clock_), reconfig_audio_decoder_(false), reconfig_video_decoder_(false), @@ -407,7 +428,9 @@ void MediaSourcePlayer::Release() { ReleaseMediaResourcesFromManager(); } -void MediaSourcePlayer::SetVolume(float leftVolume, float rightVolume) { +void MediaSourcePlayer::SetVolume(double volume) { + volume_ = volume; + SetVolumeInternal(); } bool MediaSourcePlayer::CanPause() { @@ -546,6 +569,7 @@ void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) { } void MediaSourcePlayer::OnSeekRequestAck(unsigned seek_request_id) { + DVLOG(1) << "OnSeekRequestAck(" << seek_request_id << ")"; // Do nothing until the most recent seek request is processed. if (seek_request_id_ != seek_request_id) return; @@ -613,7 +637,9 @@ void MediaSourcePlayer::MediaDecoderCallback( Release(); OnMediaError(MEDIA_ERROR_DECODE); return; - } else if (decode_status == MediaDecoderJob::DECODE_SUCCEEDED) { + } + + if (decode_status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER) { if (is_audio) audio_access_unit_index_++; else @@ -698,6 +724,7 @@ void MediaSourcePlayer::DecodeMoreAudio() { } void MediaSourcePlayer::DecodeMoreVideo() { + DVLOG(1) << "DecodeMoreVideo()"; DCHECK(!video_decoder_job_->is_decoding()); DCHECK(HasVideoData()); @@ -712,6 +739,9 @@ void MediaSourcePlayer::DecodeMoreVideo() { return; } + DVLOG(3) << "VideoDecoderJob::Decode(" << video_access_unit_index_ << ", " + << start_time_ticks_.ToInternalValue() << ", " + << start_presentation_timestamp_.InMilliseconds() << ")"; video_decoder_job_->Decode( received_video_.access_units[video_access_unit_index_], start_time_ticks_, start_presentation_timestamp_, @@ -734,6 +764,7 @@ void MediaSourcePlayer::PlaybackCompleted(bool is_audio) { } void MediaSourcePlayer::ClearDecodingData() { + DVLOG(1) << "ClearDecodingData()"; if (audio_decoder_job_) audio_decoder_job_->Flush(); if (video_decoder_job_) @@ -786,8 +817,10 @@ void MediaSourcePlayer::ConfigureAudioDecoderJob() { audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0], audio_extra_data_.size(), media_codec.obj())); - if (audio_decoder_job_) + if (audio_decoder_job_) { + SetVolumeInternal(); reconfig_audio_decoder_ = false; + } } void MediaSourcePlayer::ConfigureVideoDecoderJob() { @@ -866,6 +899,7 @@ void MediaSourcePlayer::SyncAndStartDecoderJobs() { } void MediaSourcePlayer::RequestAudioData() { + DVLOG(2) << "RequestAudioData()"; DCHECK(HasAudio()); if (waiting_for_audio_data_) @@ -878,6 +912,7 @@ void MediaSourcePlayer::RequestAudioData() { } void MediaSourcePlayer::RequestVideoData() { + DVLOG(2) << "RequestVideoData()"; DCHECK(HasVideo()); if (waiting_for_video_data_) return; @@ -896,4 +931,9 @@ bool MediaSourcePlayer::HasVideoData() const { return video_access_unit_index_ < received_video_.access_units.size(); } +void MediaSourcePlayer::SetVolumeInternal() { + if (audio_decoder_job_ && volume_ >= 0) + audio_decoder_job_.get()->SetVolume(volume_); +} + } // namespace media diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h index efab0a2f6c..7253d565e3 100644 --- a/media/base/android/media_source_player.h +++ b/media/base/android/media_source_player.h @@ -40,7 +40,8 @@ class MediaDecoderJob { public: enum DecodeStatus { DECODE_SUCCEEDED, - DECODE_TRY_AGAIN_LATER, + DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER, + DECODE_TRY_DEQUEUE_OUTPUT_AGAIN_LATER, DECODE_FORMAT_CHANGED, DECODE_END_OF_STREAM, DECODE_FAILED, @@ -54,19 +55,14 @@ class MediaDecoderJob { size_t)> DecoderCallback; // Called by MediaSourcePlayer to decode some data. - void Decode( - const MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit& unit, - const base::TimeTicks& start_time_ticks, - const base::TimeDelta& start_presentation_timestamp, - const MediaDecoderJob::DecoderCallback& callback); + void Decode(const AccessUnit& unit, + const base::TimeTicks& start_time_ticks, + const base::TimeDelta& start_presentation_timestamp, + const MediaDecoderJob::DecoderCallback& callback); // Flush the decoder. void Flush(); - struct Deleter { - inline void operator()(MediaDecoderJob* ptr) const { ptr->Release(); } - }; - // Causes this instance to be deleted on the thread it is bound to. void Release(); @@ -86,18 +82,19 @@ class MediaDecoderJob { const base::TimeDelta& presentation_timestamp, const MediaDecoderJob::DecoderCallback& callback, DecodeStatus status); + DecodeStatus QueueInputBuffer(const AccessUnit& unit); + // Helper function to decoder data on |thread_|. |unit| contains all the data // to be decoded. |start_time_ticks| and |start_presentation_timestamp| // represent the system time and the presentation timestamp when the first // frame is rendered. We use these information to estimate when the current // frame should be rendered. If |needs_flush| is true, codec needs to be // flushed at the beginning of this call. - void DecodeInternal( - const MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit& unit, - const base::TimeTicks& start_time_ticks, - const base::TimeDelta& start_presentation_timestamp, - bool needs_flush, - const MediaDecoderJob::DecoderCallback& callback); + void DecodeInternal(const AccessUnit& unit, + const base::TimeTicks& start_time_ticks, + const base::TimeDelta& start_presentation_timestamp, + bool needs_flush, + const MediaDecoderJob::DecoderCallback& callback); // The UI message loop where callbacks should be dispatched. scoped_refptr<base::MessageLoopProxy> ui_loop_; @@ -122,8 +119,9 @@ class MediaDecoderJob { bool is_decoding_; }; -typedef scoped_ptr<MediaDecoderJob, MediaDecoderJob::Deleter> - ScopedMediaDecoderJob; +struct DecoderJobDeleter { + inline void operator()(MediaDecoderJob* ptr) const { ptr->Release(); } +}; // This class handles media source extensions on Android. It uses Android // MediaCodec to decode audio and video streams in two separate threads. @@ -142,7 +140,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid { virtual void Pause() OVERRIDE; virtual void SeekTo(base::TimeDelta timestamp) OVERRIDE; virtual void Release() OVERRIDE; - virtual void SetVolume(float leftVolume, float rightVolume) OVERRIDE; + virtual void SetVolume(double volume) OVERRIDE; virtual int GetVideoWidth() OVERRIDE; virtual int GetVideoHeight() OVERRIDE; virtual base::TimeDelta GetCurrentTime() OVERRIDE; @@ -219,6 +217,9 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid { bool HasAudioData() const; bool HasVideoData() const; + // Helper function to set the volume. + void SetVolumeInternal(); + enum PendingEventFlags { NO_EVENT_PENDING = 0, SEEK_EVENT_PENDING = 1 << 0, @@ -246,6 +247,7 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid { bool playing_; bool is_audio_encrypted_; bool is_video_encrypted_; + double volume_; // base::TickClock used by |clock_|. base::DefaultTickClock default_tick_clock_; @@ -266,8 +268,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid { gfx::ScopedJavaSurface surface_; // Decoder jobs - ScopedMediaDecoderJob audio_decoder_job_; - ScopedMediaDecoderJob video_decoder_job_; + scoped_ptr<AudioDecoderJob, DecoderJobDeleter> audio_decoder_job_; + scoped_ptr<VideoDecoderJob, DecoderJobDeleter> video_decoder_job_; bool reconfig_audio_decoder_; bool reconfig_video_decoder_; diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc index 31466400c9..cc99cf7cde 100644 --- a/media/base/android/media_source_player_unittest.cc +++ b/media/base/android/media_source_player_unittest.cc @@ -94,9 +94,12 @@ class MediaSourcePlayerTest : public testing::Test { protected: // Get the decoder job from the MediaSourcePlayer. MediaDecoderJob* GetMediaDecoderJob(bool is_audio) { - if (is_audio) - return player_->audio_decoder_job_.get(); - return player_->video_decoder_job_.get(); + if (is_audio) { + return reinterpret_cast<MediaDecoderJob*>( + player_->audio_decoder_job_.get()); + } + return reinterpret_cast<MediaDecoderJob*>( + player_->video_decoder_job_.get()); } // Starts an audio decoder job. @@ -393,7 +396,10 @@ TEST_F(MediaSourcePlayerTest, DecoderJobsCannotStartWithoutAudio) { EXPECT_TRUE(video_decoder_job->is_decoding()); } -TEST_F(MediaSourcePlayerTest, StartTimeTicksResetAfterDecoderUnderruns) { +// Disabled due to http://crbug.com/266041. +// TODO(xhwang/qinmin): Fix this test and reenable it. +TEST_F(MediaSourcePlayerTest, + DISABLED_StartTimeTicksResetAfterDecoderUnderruns) { if (!MediaCodecBridge::IsAvailable()) return; diff --git a/media/base/audio_buffer.cc b/media/base/audio_buffer.cc index 61296dad87..b2cdd8c41a 100644 --- a/media/base/audio_buffer.cc +++ b/media/base/audio_buffer.cc @@ -11,22 +11,23 @@ namespace media { -// Alignment of each channel's data; use 8-byte alignment as that is bigger -// than maximum size of a sample, and the minimum alignment. -enum { kChannelAlignment = 8 }; +// Alignment of each channel's data; this must match what ffmpeg expects +// (which may be 0, 16, or 32, depending on the processor). Selecting 32 in +// order to work on all processors. +enum { kChannelAlignment = 32 }; AudioBuffer::AudioBuffer(SampleFormat sample_format, int channel_count, int frame_count, + bool create_buffer, const uint8* const* data, const base::TimeDelta timestamp, const base::TimeDelta duration) : sample_format_(sample_format), channel_count_(channel_count), - frame_count_(frame_count), adjusted_frame_count_(frame_count), trim_start_(0), - end_of_stream_(data == NULL && frame_count_ == 0), + end_of_stream_(!create_buffer && data == NULL && frame_count == 0), timestamp_(timestamp), duration_(duration) { CHECK_GE(channel_count, 0); @@ -37,7 +38,7 @@ AudioBuffer::AudioBuffer(SampleFormat sample_format, int data_size = frame_count * bytes_per_channel; // Empty buffer? - if (!data) + if (!create_buffer) return; if (sample_format == kSampleFormatPlanarF32 || @@ -56,7 +57,8 @@ AudioBuffer::AudioBuffer(SampleFormat sample_format, // Copy each channel's data into the appropriate spot. for (int i = 0; i < channel_count; ++i) { channel_data_.push_back(data_.get() + i * block_size_per_channel); - memcpy(channel_data_[i], data[i], data_size); + if (data) + memcpy(channel_data_[i], data[i], data_size); } return; } @@ -71,7 +73,8 @@ AudioBuffer::AudioBuffer(SampleFormat sample_format, data_size *= channel_count; data_.reset( static_cast<uint8*>(base::AlignedAlloc(data_size, kChannelAlignment))); - memcpy(data_.get(), data[0], data_size); + if (data) + memcpy(data_.get(), data[0], data_size); } AudioBuffer::~AudioBuffer() {} @@ -85,9 +88,29 @@ scoped_refptr<AudioBuffer> AudioBuffer::CopyFrom( const base::TimeDelta timestamp, const base::TimeDelta duration) { // If you hit this CHECK you likely have a bug in a demuxer. Go fix it. + CHECK_GT(frame_count, 0); // Otherwise looks like an EOF buffer. CHECK(data[0]); - return make_scoped_refptr(new AudioBuffer( - sample_format, channel_count, frame_count, data, timestamp, duration)); + return make_scoped_refptr(new AudioBuffer(sample_format, + channel_count, + frame_count, + true, + data, + timestamp, + duration)); +} + +// static +scoped_refptr<AudioBuffer> AudioBuffer::CreateBuffer(SampleFormat sample_format, + int channel_count, + int frame_count) { + CHECK_GT(frame_count, 0); // Otherwise looks like an EOF buffer. + return make_scoped_refptr(new AudioBuffer(sample_format, + channel_count, + frame_count, + true, + NULL, + kNoTimestamp(), + kNoTimestamp())); } // static @@ -98,14 +121,19 @@ scoped_refptr<AudioBuffer> AudioBuffer::CreateEmptyBuffer( const base::TimeDelta duration) { CHECK_GT(frame_count, 0); // Otherwise looks like an EOF buffer. // Since data == NULL, format doesn't matter. - return make_scoped_refptr(new AudioBuffer( - kSampleFormatF32, channel_count, frame_count, NULL, timestamp, duration)); + return make_scoped_refptr(new AudioBuffer(kSampleFormatF32, + channel_count, + frame_count, + false, + NULL, + timestamp, + duration)); } // static scoped_refptr<AudioBuffer> AudioBuffer::CreateEOSBuffer() { return make_scoped_refptr(new AudioBuffer( - kUnknownSampleFormat, 1, 0, NULL, kNoTimestamp(), kNoTimestamp())); + kUnknownSampleFormat, 1, 0, false, NULL, kNoTimestamp(), kNoTimestamp())); } // Convert int16 values in the range [kint16min, kint16max] to [-1.0, 1.0]. @@ -124,10 +152,12 @@ void AudioBuffer::ReadFrames(int frames_to_copy, // specified must be in range. DCHECK(!end_of_stream()); DCHECK_EQ(dest->channels(), channel_count_); - source_frame_offset += trim_start_; - DCHECK_LE(source_frame_offset + frames_to_copy, frame_count_); + DCHECK_LE(source_frame_offset + frames_to_copy, adjusted_frame_count_); DCHECK_LE(dest_frame_offset + frames_to_copy, dest->frames()); + // Move the start past any frames that have been trimmed. + source_frame_offset += trim_start_; + if (!data_) { // Special case for an empty buffer. dest->ZeroFramesPartial(dest_frame_offset, frames_to_copy); @@ -189,8 +219,8 @@ void AudioBuffer::ReadFrames(int frames_to_copy, } void AudioBuffer::TrimStart(int frames_to_trim) { - CHECK_LT(frames_to_trim, adjusted_frame_count_); - trim_start_ += frames_to_trim; + CHECK_GE(frames_to_trim, 0); + CHECK_LE(frames_to_trim, adjusted_frame_count_); // Adjust timestamp_ and duration_ to reflect the smaller number of frames. double offset = static_cast<double>(duration_.InMicroseconds()) * @@ -200,8 +230,25 @@ void AudioBuffer::TrimStart(int frames_to_trim) { timestamp_ += offset_as_time; duration_ -= offset_as_time; + // Finally adjust the number of frames in this buffer and where the start + // really is. + adjusted_frame_count_ -= frames_to_trim; + trim_start_ += frames_to_trim; +} + +void AudioBuffer::TrimEnd(int frames_to_trim) { + CHECK_GE(frames_to_trim, 0); + CHECK_LE(frames_to_trim, adjusted_frame_count_); + + // Adjust duration_ only to reflect the smaller number of frames. + double offset = static_cast<double>(duration_.InMicroseconds()) * + frames_to_trim / adjusted_frame_count_; + base::TimeDelta offset_as_time = + base::TimeDelta::FromMicroseconds(static_cast<int64>(offset)); + duration_ -= offset_as_time; + // Finally adjust the number of frames in this buffer. - adjusted_frame_count_ = frame_count_ - trim_start_; + adjusted_frame_count_ -= frames_to_trim; } } // namespace media diff --git a/media/base/audio_buffer.h b/media/base/audio_buffer.h index 9200666cb5..e52355ac4c 100644 --- a/media/base/audio_buffer.h +++ b/media/base/audio_buffer.h @@ -37,6 +37,12 @@ class MEDIA_EXPORT AudioBuffer const base::TimeDelta timestamp, const base::TimeDelta duration); + // Create an AudioBuffer with |frame_count| frames. Buffer is allocated, but + // not initialized. Timestamp and duration are set to kNoTimestamp(). + static scoped_refptr<AudioBuffer> CreateBuffer(SampleFormat sample_format, + int channel_count, + int frame_count); + // Create an empty AudioBuffer with |frame_count| frames. static scoped_refptr<AudioBuffer> CreateEmptyBuffer( int channel_count, @@ -60,10 +66,15 @@ class MEDIA_EXPORT AudioBuffer AudioBus* dest); // Trim an AudioBuffer by removing |frames_to_trim| frames from the start. + // Timestamp and duration are adjusted to reflect the fewer frames. // Note that repeated calls to TrimStart() may result in timestamp() and // duration() being off by a few microseconds due to rounding issues. void TrimStart(int frames_to_trim); + // Trim an AudioBuffer by removing |frames_to_trim| frames from the end. + // Duration is adjusted to reflect the fewer frames. + void TrimEnd(int frames_to_trim); + // Return the number of channels. int channel_count() const { return channel_count_; } @@ -83,16 +94,22 @@ class MEDIA_EXPORT AudioBuffer // If there's no data in this buffer, it represents end of stream. bool end_of_stream() const { return end_of_stream_; } + // Access to the raw buffer for ffmpeg to write directly to. Data for planar + // data is grouped by channel. + uint8* writable_data() { return data_.get(); } + private: friend class base::RefCountedThreadSafe<AudioBuffer>; // Allocates aligned contiguous buffer to hold all channel data (1 block for // interleaved data, |channel_count| blocks for planar data), copies - // [data,data+data_size) to the allocated buffer(s). If |data| is null an end - // of stream buffer is created. + // [data,data+data_size) to the allocated buffer(s). If |data| is null, no + // data is copied. If |create_buffer| is false, no data buffer is created (or + // copied to). AudioBuffer(SampleFormat sample_format, int channel_count, int frame_count, + bool create_buffer, const uint8* const* data, const base::TimeDelta timestamp, const base::TimeDelta duration); @@ -101,7 +118,6 @@ class MEDIA_EXPORT AudioBuffer const SampleFormat sample_format_; const int channel_count_; - const int frame_count_; int adjusted_frame_count_; int trim_start_; const bool end_of_stream_; diff --git a/media/base/audio_buffer_unittest.cc b/media/base/audio_buffer_unittest.cc index f6384e880f..473778a6b5 100644 --- a/media/base/audio_buffer_unittest.cc +++ b/media/base/audio_buffer_unittest.cc @@ -256,7 +256,7 @@ TEST(AudioBufferTest, Trim) { buffer->ReadFrames(20, 0, 0, bus.get()); VerifyResult(bus->channel(0), 20, 1.0f, 1.0f); - // Trim off 10 frames. + // Trim off 10 frames from the start. buffer->TrimStart(10); EXPECT_EQ(buffer->frame_count(), frames - 10); EXPECT_EQ(buffer->timestamp(), start_time + base::TimeDelta::FromSeconds(10)); @@ -264,13 +264,27 @@ TEST(AudioBufferTest, Trim) { buffer->ReadFrames(20, 0, 0, bus.get()); VerifyResult(bus->channel(0), 20, 11.0f, 1.0f); - // Trim off 80 more. - buffer->TrimStart(80); - EXPECT_EQ(buffer->frame_count(), frames - 90); - EXPECT_EQ(buffer->timestamp(), start_time + base::TimeDelta::FromSeconds(90)); - EXPECT_EQ(buffer->duration(), base::TimeDelta::FromSeconds(10)); + // Trim off 10 frames from the end. + buffer->TrimEnd(10); + EXPECT_EQ(buffer->frame_count(), frames - 20); + EXPECT_EQ(buffer->timestamp(), start_time + base::TimeDelta::FromSeconds(10)); + EXPECT_EQ(buffer->duration(), base::TimeDelta::FromSeconds(80)); + buffer->ReadFrames(20, 0, 0, bus.get()); + VerifyResult(bus->channel(0), 20, 11.0f, 1.0f); + + // Trim off 50 more from the start. + buffer->TrimStart(50); + EXPECT_EQ(buffer->frame_count(), frames - 70); + EXPECT_EQ(buffer->timestamp(), start_time + base::TimeDelta::FromSeconds(60)); + EXPECT_EQ(buffer->duration(), base::TimeDelta::FromSeconds(30)); buffer->ReadFrames(10, 0, 0, bus.get()); - VerifyResult(bus->channel(0), 10, 91.0f, 1.0f); + VerifyResult(bus->channel(0), 10, 61.0f, 1.0f); + + // Trim off the last 30 frames. + buffer->TrimEnd(30); + EXPECT_EQ(buffer->frame_count(), 0); + EXPECT_EQ(buffer->timestamp(), start_time + base::TimeDelta::FromSeconds(60)); + EXPECT_EQ(buffer->duration(), base::TimeDelta::FromSeconds(0)); } } // namespace media diff --git a/media/base/container_names.cc b/media/base/container_names.cc index 8b68e31e24..f062929d54 100644 --- a/media/base/container_names.cc +++ b/media/base/container_names.cc @@ -975,8 +975,6 @@ static bool CheckMov(const uint8* buffer, int buffer_size) { default: return false; } - if (atomsize <= 0) - break; // Indicates the last atom or length too big. if (atomsize == 1) { // Indicates that the length is the next 64bits. if (offset + 16 > buffer_size) @@ -985,6 +983,8 @@ static bool CheckMov(const uint8* buffer, int buffer_size) { break; // Offset is way past buffer size. atomsize = Read32(buffer + offset + 12); } + if (atomsize <= 0) + break; // Indicates the last atom or length too big. offset += atomsize; } return true; diff --git a/media/base/container_names_unittest.cc b/media/base/container_names_unittest.cc index 4aa20638ba..21f80af6d9 100644 --- a/media/base/container_names_unittest.cc +++ b/media/base/container_names_unittest.cc @@ -74,6 +74,13 @@ const char kRm1Buffer[12] = ".RMF\0\0"; const char kRm2Buffer[12] = ".ra\xfd"; uint8 kWtvBuffer[] = { 0xb7, 0xd8, 0x00, 0x20, 0x37, 0x49, 0xda, 0x11, 0xa6, 0x4e, 0x00, 0x07, 0xe9, 0x5e, 0xad, 0x8d }; +uint8 kBug263073Buffer[] = { + 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x69, 0x73, 0x6f, 0x6d, 0x6d, 0x70, 0x34, 0x32, + 0x00, 0x00, 0x00, 0x01, 0x6d, 0x64, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0x2e, 0x22, 0xcf, 0x00, 0x00, 0x00, 0x37, 0x67, 0x64, 0x00, 0x28, + 0xac, 0x2c, 0xa4, 0x01, 0xe0, 0x08, 0x9f, 0x97, 0x01, 0x52, 0x02, 0x02, + 0x02, 0x80, 0x00, 0x01}; // Test that containers that start with fixed strings are handled correctly. // This is to verify that the TAG matches the first 4 characters of the string. @@ -91,6 +98,7 @@ TEST(ContainerNamesTest, CheckFixedStrings) { VERIFY(kRm1Buffer, CONTAINER_RM); VERIFY(kRm2Buffer, CONTAINER_RM); VERIFY(kWtvBuffer, CONTAINER_WTV); + VERIFY(kBug263073Buffer, CONTAINER_MOV); } // Determine the container type of a specified file. diff --git a/media/base/simd/filter_yuv_mmx.cc b/media/base/simd/filter_yuv_mmx.cc index c69d9de651..3991fe72fe 100644 --- a/media/base/simd/filter_yuv_mmx.cc +++ b/media/base/simd/filter_yuv_mmx.cc @@ -16,6 +16,7 @@ namespace media { #if defined(COMPILER_MSVC) // Warning 4799 is about calling emms before the function exits. // We calls emms in a frame level so suppress this warning. +#pragma warning(push) #pragma warning(disable: 4799) #endif @@ -72,7 +73,7 @@ void FilterYUVRows_MMX(uint8* dest, } #if defined(COMPILER_MSVC) -#pragma warning(default: 4799) +#pragma warning(pop) #endif } // namespace media |