summaryrefslogtreecommitdiff
path: root/media
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2014-11-06 12:50:09 +0000
committerBen Murdoch <benm@google.com>2014-11-06 12:50:09 +0000
commit5b892326406927b709cdaf6c384d4ababf456332 (patch)
treec2b36e6a80232b23731b9b3be8f5b7701382dfcb /media
parent17d46637767dc9c540d984794ad6b5162e8c8c4f (diff)
downloadchromium_org-5b892326406927b709cdaf6c384d4ababf456332.tar.gz
Merge from Chromium at DEPS revision 39.0.2171.53
This commit was generated by merge_to_master.py. Change-Id: Id583a0dc312e50e455eb8bd04f75c74304738c9d
Diffstat (limited to 'media')
-rw-r--r--media/base/android/audio_decoder_job.cc22
-rw-r--r--media/base/android/audio_decoder_job.h2
-rw-r--r--media/base/android/media_decoder_job.cc67
-rw-r--r--media/base/android/media_decoder_job.h12
-rw-r--r--media/base/android/media_source_player.cc4
-rw-r--r--media/base/android/media_source_player_unittest.cc151
-rw-r--r--media/base/android/video_decoder_job.cc40
-rw-r--r--media/base/android/video_decoder_job.h15
-rw-r--r--media/base/container_names.cc4
9 files changed, 208 insertions, 109 deletions
diff --git a/media/base/android/audio_decoder_job.cc b/media/base/android/audio_decoder_job.cc
index cb882cee42..e3769200ab 100644
--- a/media/base/android/audio_decoder_job.cc
+++ b/media/base/android/audio_decoder_job.cc
@@ -51,6 +51,17 @@ bool AudioDecoderJob::HasStream() const {
return audio_codec_ != kUnknownAudioCodec;
}
+void AudioDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
+ // TODO(qinmin): split DemuxerConfig for audio and video separately so we
+ // can simply store the stucture here.
+ audio_codec_ = configs.audio_codec;
+ num_channels_ = configs.audio_channels;
+ sampling_rate_ = configs.audio_sampling_rate;
+ set_is_content_encrypted(configs.is_audio_encrypted);
+ audio_extra_data_ = configs.audio_extra_data;
+ bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_;
+}
+
void AudioDecoderJob::SetVolume(double volume) {
volume_ = volume;
SetVolumeInternal();
@@ -94,17 +105,6 @@ bool AudioDecoderJob::ComputeTimeToRender() const {
return false;
}
-void AudioDecoderJob::UpdateDemuxerConfigs(const DemuxerConfigs& configs) {
- // TODO(qinmin): split DemuxerConfig for audio and video separately so we
- // can simply store the stucture here.
- audio_codec_ = configs.audio_codec;
- num_channels_ = configs.audio_channels;
- sampling_rate_ = configs.audio_sampling_rate;
- set_is_content_encrypted(configs.is_audio_encrypted);
- audio_extra_data_ = configs.audio_extra_data;
- bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_;
-}
-
bool AudioDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const {
return audio_codec_ != configs.audio_codec ||
diff --git a/media/base/android/audio_decoder_job.h b/media/base/android/audio_decoder_job.h
index f3bb091a7d..ecd3f213a7 100644
--- a/media/base/android/audio_decoder_job.h
+++ b/media/base/android/audio_decoder_job.h
@@ -29,6 +29,7 @@ class AudioDecoderJob : public MediaDecoderJob {
// MediaDecoderJob implementation.
virtual bool HasStream() const OVERRIDE;
+ virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) OVERRIDE;
// Sets the volume of the audio output.
void SetVolume(double volume);
@@ -47,7 +48,6 @@ class AudioDecoderJob : public MediaDecoderJob {
virtual bool ComputeTimeToRender() const OVERRIDE;
virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const OVERRIDE;
- virtual void UpdateDemuxerConfigs(const DemuxerConfigs& configs) OVERRIDE;
virtual bool CreateMediaCodecBridgeInternal() OVERRIDE;
// Helper method to set the audio output volume.
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index bceba43bc1..13206ec89e 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -24,7 +24,8 @@ MediaDecoderJob::MediaDecoderJob(
const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner,
const base::Closure& request_data_cb,
const base::Closure& config_changed_cb)
- : ui_task_runner_(base::MessageLoopProxy::current()),
+ : need_to_reconfig_decoder_job_(false),
+ ui_task_runner_(base::MessageLoopProxy::current()),
decoder_task_runner_(decoder_task_runner),
needs_flush_(false),
input_eos_encountered_(false),
@@ -203,13 +204,6 @@ void MediaDecoderJob::ReleaseDecoderResources() {
release_resources_pending_ = true;
}
-bool MediaDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
- bool config_changed = AreDemuxerConfigsChanged(configs);
- if (config_changed)
- UpdateDemuxerConfigs(configs);
- return config_changed;
-}
-
base::android::ScopedJavaLocalRef<jobject> MediaDecoderJob::GetMediaCrypto() {
base::android::ScopedJavaLocalRef<jobject> media_crypto;
if (drm_bridge_)
@@ -335,19 +329,18 @@ void MediaDecoderJob::DecodeCurrentAccessUnit(
int index = CurrentReceivedDataChunkIndex();
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();
+ SetDemuxerConfigs(configs);
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_ || !reconfigure_needed) {
need_to_reconfig_decoder_job_ =
need_to_reconfig_decoder_job_ || reconfigure_needed;
+ // Report MEDIA_CODEC_OK status so decoder will continue decoding and
+ // MEDIA_CODEC_OUTPUT_FORMAT_CHANGED status will come later.
ui_task_runner_->PostTask(FROM_HERE, base::Bind(
&MediaDecoderJob::OnDecodeCompleted, base::Unretained(this),
- MEDIA_CODEC_OUTPUT_FORMAT_CHANGED, kNoTimestamp(), kNoTimestamp()));
+ MEDIA_CODEC_OK, kNoTimestamp(), kNoTimestamp()));
return;
}
// Start draining the decoder so that all the remaining frames are
@@ -430,20 +423,31 @@ void MediaDecoderJob::DecodeInternal(
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
kMediaCodecTimeoutInMilliseconds);
- MediaCodecStatus status =
- media_codec_bridge_->DequeueOutputBuffer(timeout,
- &buffer_index,
- &offset,
- &size,
- &presentation_timestamp,
- &output_eos_encountered_,
- NULL);
-
- if (status != MEDIA_CODEC_OK) {
+ MediaCodecStatus status = MEDIA_CODEC_OK;
+ bool has_format_change = false;
+ // Dequeue the output buffer until a MEDIA_CODEC_OK, MEDIA_CODEC_ERROR or
+ // MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER is received.
+ do {
+ status = media_codec_bridge_->DequeueOutputBuffer(
+ timeout,
+ &buffer_index,
+ &offset,
+ &size,
+ &presentation_timestamp,
+ &output_eos_encountered_,
+ NULL);
if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
!media_codec_bridge_->GetOutputBuffers()) {
status = MEDIA_CODEC_ERROR;
+ } else if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) {
+ // TODO(qinmin): instead of waiting for the next output buffer to be
+ // dequeued, post a task on the UI thread to signal the format change.
+ has_format_change = true;
}
+ } while (status != MEDIA_CODEC_OK && status != MEDIA_CODEC_ERROR &&
+ status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
+
+ if (status != MEDIA_CODEC_OK) {
callback.Run(status, kNoTimestamp(), kNoTimestamp());
return;
}
@@ -451,6 +455,8 @@ void MediaDecoderJob::DecodeInternal(
// TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
if (output_eos_encountered_)
status = MEDIA_CODEC_OUTPUT_END_OF_STREAM;
+ else if (has_format_change)
+ status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED;
bool render_output = presentation_timestamp >= preroll_timestamp_ &&
(status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
@@ -517,7 +523,6 @@ void MediaDecoderJob::OnDecodeCompleted(
switch (status) {
case MEDIA_CODEC_OK:
case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
- case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
if (!input_eos_encountered_) {
@@ -534,6 +539,10 @@ void MediaDecoderJob::OnDecodeCompleted(
case MEDIA_CODEC_ERROR:
// Do nothing.
break;
+
+ case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
+ DCHECK(false) << "Invalid output status";
+ break;
};
if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM && drain_decoder_) {
@@ -541,6 +550,12 @@ void MediaDecoderJob::OnDecodeCompleted(
status = MEDIA_CODEC_OK;
}
+ if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) {
+ if (UpdateOutputFormat())
+ config_changed_cb_.Run();
+ status = MEDIA_CODEC_OK;
+ }
+
if (release_resources_pending_) {
ReleaseMediaCodecBridge();
release_resources_pending_ = false;
@@ -642,6 +657,10 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded(
return true;
}
+bool MediaDecoderJob::UpdateOutputFormat() {
+ return false;
+}
+
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 3b5da1450c..15d963530b 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -84,9 +84,8 @@ class MediaDecoderJob {
// Releases all the decoder resources as the current tab is going background.
virtual void ReleaseDecoderResources();
- // Sets the demuxer configs. Returns true if configs has changed, or false
- // otherwise.
- bool SetDemuxerConfigs(const DemuxerConfigs& configs);
+ // Sets the demuxer configs.
+ virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) = 0;
// Returns whether the decoder has finished decoding all the data.
bool OutputEOSReached() const;
@@ -228,13 +227,14 @@ class MediaDecoderJob {
virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const = 0;
- // 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;
+ // Update the output format from the decoder, returns true if the output
+ // format changes, or false otherwise.
+ virtual bool UpdateOutputFormat();
+
// 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_source_player.cc b/media/base/android/media_source_player.cc
index ac2df3bbd7..4018451b71 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -150,11 +150,11 @@ bool MediaSourcePlayer::IsPlaying() {
}
int MediaSourcePlayer::GetVideoWidth() {
- return video_decoder_job_->width();
+ return video_decoder_job_->output_width();
}
int MediaSourcePlayer::GetVideoHeight() {
- return video_decoder_job_->height();
+ return video_decoder_job_->output_height();
}
void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 9cb2b411b6..84821c4baf 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -45,6 +45,7 @@ class MockMediaPlayerManager : public MediaPlayerManager {
: message_loop_(message_loop),
playback_completed_(false),
num_resources_requested_(0),
+ num_metadata_changes_(0),
timestamp_updated_(false) {}
virtual ~MockMediaPlayerManager() {}
@@ -62,7 +63,9 @@ class MockMediaPlayerManager : public MediaPlayerManager {
}
virtual void OnMediaMetadataChanged(
int player_id, base::TimeDelta duration, int width, int height,
- bool success) OVERRIDE {}
+ bool success) OVERRIDE {
+ num_metadata_changes_++;
+ }
virtual void OnPlaybackComplete(int player_id) OVERRIDE {
playback_completed_ = true;
if (message_loop_->is_running())
@@ -92,6 +95,10 @@ class MockMediaPlayerManager : public MediaPlayerManager {
return num_resources_requested_;
}
+ int num_metadata_changes() const {
+ return num_metadata_changes_;
+ }
+
void OnMediaResourcesRequested(int player_id) {
num_resources_requested_++;
}
@@ -109,6 +116,8 @@ class MockMediaPlayerManager : public MediaPlayerManager {
bool playback_completed_;
// The number of resource requests this object has seen.
int num_resources_requested_;
+ // The number of metadata changes reported by the player.
+ int num_metadata_changes_;
// Playback timestamp was updated.
bool timestamp_updated_;
@@ -272,7 +281,7 @@ class MediaSourcePlayerTest : public testing::Test {
DemuxerConfigs configs;
configs.video_codec = kCodecVP8;
configs.video_size =
- use_larger_size ? gfx::Size(640, 480) : gfx::Size(320, 240);
+ use_larger_size ? gfx::Size(640, 240) : gfx::Size(320, 240);
configs.is_video_encrypted = false;
configs.duration = kDefaultDuration;
return configs;
@@ -363,13 +372,19 @@ class MediaSourcePlayerTest : public testing::Test {
EXPECT_TRUE(false);
}
- AccessUnit CreateAccessUnitWithData(bool is_audio, int audio_packet_id) {
+ AccessUnit CreateAccessUnitWithData(bool is_audio, int audio_packet_id,
+ bool use_large_size_video) {
AccessUnit unit;
unit.status = DemuxerStream::kOk;
- scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(
- is_audio ? base::StringPrintf("vorbis-packet-%d", audio_packet_id)
- : "vp8-I-frame-320x240");
+ scoped_refptr<DecoderBuffer> buffer;
+ if (is_audio) {
+ buffer = ReadTestDataFile(
+ base::StringPrintf("vorbis-packet-%d", audio_packet_id));
+ } else {
+ buffer = ReadTestDataFile(
+ use_large_size_video ? "vp8-I-frame-640x240" : "vp8-I-frame-320x240");
+ }
unit.data = std::vector<uint8>(
buffer->data(), buffer->data() + buffer->data_size());
@@ -387,15 +402,16 @@ class MediaSourcePlayerTest : public testing::Test {
DemuxerData data;
data.type = DemuxerStream::AUDIO;
data.access_units.resize(1);
- data.access_units[0] = CreateAccessUnitWithData(true, packet_id);
+ data.access_units[0] = CreateAccessUnitWithData(true, packet_id, false);
+
return data;
}
- DemuxerData CreateReadFromDemuxerAckForVideo() {
+ DemuxerData CreateReadFromDemuxerAckForVideo(bool use_large_size) {
DemuxerData data;
data.type = DemuxerStream::VIDEO;
data.access_units.resize(1);
- data.access_units[0] = CreateAccessUnitWithData(false, 0);
+ data.access_units[0] = CreateAccessUnitWithData(false, 0, use_large_size);
return data;
}
@@ -495,7 +511,7 @@ class MediaSourcePlayerTest : public testing::Test {
// |start_timestamp| must be smaller than |target_timestamp|.
EXPECT_LE(start_timestamp, target_timestamp);
DemuxerData data = is_audio ? CreateReadFromDemuxerAckForAudio(1) :
- CreateReadFromDemuxerAckForVideo();
+ CreateReadFromDemuxerAckForVideo(false);
int current_timestamp = start_timestamp.InMilliseconds();
// Send some data with access unit timestamps before the |target_timestamp|,
@@ -526,7 +542,7 @@ class MediaSourcePlayerTest : public testing::Test {
data.access_units.resize(config_unit_index + 1);
for (int i = 0; i < config_unit_index; ++i)
- data.access_units[i] = CreateAccessUnitWithData(is_audio, i);
+ data.access_units[i] = CreateAccessUnitWithData(is_audio, i, false);
data.access_units[config_unit_index].status = DemuxerStream::kConfigChanged;
data.demuxer_configs.resize(1);
@@ -552,13 +568,13 @@ class MediaSourcePlayerTest : public testing::Test {
if (trigger_with_release_start) {
// Consume the first frame, so that the next VideoDecoderJob will not
// inherit the I-frame from the previous decoder.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
ReleasePlayer();
WaitForVideoDecodeDone();
// Simulate demuxer's response to the video data request. The data will be
// passed to the next MediaCodecBridge.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_FALSE(GetMediaCodecBridge(false));
EXPECT_FALSE(player_.IsPlaying());
EXPECT_EQ(expected_num_seek_requests, demuxer_->num_seek_requests());
@@ -572,7 +588,7 @@ class MediaSourcePlayerTest : public testing::Test {
message_loop_.RunUntilIdle();
} else {
// Simulate demuxer's response to the video data request.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// While the decoder is decoding, trigger a browser seek by changing
// surface. Demuxer does not know of browser seek in advance, so no
@@ -588,7 +604,7 @@ class MediaSourcePlayerTest : public testing::Test {
// Wait for the media codec bridge to finish decoding and be reset pending
// the browser seek.
WaitForVideoDecodeDone();
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
}
// Only one browser seek should have been initiated, and no further data
@@ -628,7 +644,7 @@ class MediaSourcePlayerTest : public testing::Test {
if (is_audio) {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
} else {
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EnableAdaptiveVideoPlayback(enable_adaptive_playback);
}
@@ -640,7 +656,7 @@ class MediaSourcePlayerTest : public testing::Test {
}
DemuxerConfigs configs = is_audio ?
- CreateAudioDemuxerConfigs(kCodecAAC, false) :
+ CreateAudioDemuxerConfigs(kCodecVorbis, true) :
CreateVideoDemuxerConfigs(true);
// Feed and decode access units with data for any units prior to
// |config_unit_index|, and a |kConfigChanged| unit at that index.
@@ -654,7 +670,7 @@ class MediaSourcePlayerTest : public testing::Test {
if (is_audio)
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
else
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(true));
// If the adaptive playback setting was not passed to the MediaCodecBridge
// earlier, do it here.
@@ -775,7 +791,7 @@ class MediaSourcePlayerTest : public testing::Test {
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
if (have_video)
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// Run until more data is requested a number of times equal to the number of
// media types configured. Since prefetching may be in progress, we cannot
@@ -796,7 +812,7 @@ class MediaSourcePlayerTest : public testing::Test {
if (eos_video)
player_.OnDemuxerDataAvailable(CreateEOSAck(false));
else
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
}
player_.SeekTo(base::TimeDelta());
@@ -889,7 +905,7 @@ TEST_F(MediaSourcePlayerTest, StartVideoCodecWithValidSurface) {
EXPECT_EQ(0, demuxer_->num_seek_requests());
// Send the first input chunk and verify that decoder will be created.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
WaitForVideoDecodeDone();
}
@@ -911,7 +927,7 @@ TEST_F(MediaSourcePlayerTest, StartVideoCodecWithInvalidSurface) {
EXPECT_EQ(0, demuxer_->num_seek_requests());
EXPECT_EQ(1, demuxer_->num_data_requests());
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_FALSE(GetMediaCodecBridge(false));
}
@@ -944,7 +960,7 @@ TEST_F(MediaSourcePlayerTest, SetSurfaceWhileSeeking) {
player_.OnDemuxerSeekDone(kNoTimestamp());
EXPECT_FALSE(GetMediaCodecBridge(false));
EXPECT_EQ(1, demuxer_->num_data_requests());
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
// Reconfirm exactly 1 seek request has been made of demuxer, and that it
@@ -963,7 +979,7 @@ TEST_F(MediaSourcePlayerTest, ChangeMultipleSurfaceWhileDecoding) {
EXPECT_EQ(0, demuxer_->num_seek_requests());
// Send the first input chunk.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// While the decoder is decoding, change multiple surfaces. Pass an empty
// surface first.
@@ -975,7 +991,7 @@ TEST_F(MediaSourcePlayerTest, ChangeMultipleSurfaceWhileDecoding) {
// Wait for the media codec bridge to finish decoding and be reset pending a
// browser seek.
WaitForVideoDecodeDone();
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// Only one browser seek should have been initiated. No further data request
// should have been processed on |message_loop_| before surface change event
@@ -990,7 +1006,7 @@ TEST_F(MediaSourcePlayerTest, ChangeMultipleSurfaceWhileDecoding) {
EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(1, demuxer_->num_seek_requests());
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
WaitForVideoDecodeDone();
}
@@ -1004,7 +1020,7 @@ TEST_F(MediaSourcePlayerTest, SetEmptySurfaceAndStarveWhileDecoding) {
EXPECT_EQ(1, demuxer_->num_data_requests());
// Send the first input chunk.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// While the decoder is decoding, pass an empty surface.
gfx::ScopedJavaSurface empty_surface;
@@ -1020,7 +1036,7 @@ TEST_F(MediaSourcePlayerTest, SetEmptySurfaceAndStarveWhileDecoding) {
// surface is empty.
EXPECT_EQ(0, demuxer_->num_browser_seek_requests());
EXPECT_EQ(2, demuxer_->num_data_requests());
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// Playback resumes once a non-empty surface is passed.
CreateNextTextureAndSetVideoSurface();
@@ -1038,7 +1054,7 @@ TEST_F(MediaSourcePlayerTest, ReleaseVideoDecoderResourcesWhileDecoding) {
// No resource is requested since there is no data to decode.
EXPECT_EQ(0, manager_.num_resources_requested());
ReleasePlayer();
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
// Recreate the video decoder.
CreateNextTextureAndSetVideoSurface();
@@ -1106,7 +1122,7 @@ TEST_F(MediaSourcePlayerTest, VideoOnlyStartAfterSeekFinish) {
EXPECT_EQ(1, demuxer_->num_seek_requests());
// Decoder is created after data is received.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
WaitForVideoDecodeDone();
}
@@ -1157,7 +1173,7 @@ TEST_F(MediaSourcePlayerTest, DecoderJobsCannotStartWithoutAudio) {
EXPECT_FALSE(video_decoder_job->is_decoding());
// Sending video data to player, video decoder should not start.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_FALSE(video_decoder_job->is_decoding());
// Sending audio data to player, both decoders should start now.
@@ -1204,7 +1220,7 @@ TEST_F(MediaSourcePlayerTest, V_SecondAccessUnitIsEOSAndResumePlayAfterSeek) {
StartVideoDecoderJob();
// Send the first input chunk.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
WaitForVideoDecodeDone();
VerifyPlaybackCompletesOnEOSDecode(true, false);
@@ -1340,7 +1356,7 @@ TEST_F(MediaSourcePlayerTest, V_StarvationDuringEOSDecode) {
// starvation occurs during EOS decode.
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob();
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
WaitForVideoDecodeDone();
// Simulate decoder underrun to trigger prefetch while decoding EOS.
@@ -1472,7 +1488,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_RegularSeekPendsBrowserSeekDone) {
EXPECT_FALSE(GetMediaCodecBridge(false));
EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(2, demuxer_->num_seek_requests());
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
WaitForVideoDecodeDone();
}
@@ -1496,7 +1512,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_InitialReleaseAndStart) {
EXPECT_FALSE(GetMediaCodecBridge(false));
// No browser seek is needed.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_EQ(0, demuxer_->num_browser_seek_requests());
EXPECT_EQ(2, demuxer_->num_data_requests());
WaitForVideoDecodeDone();
@@ -1689,7 +1705,7 @@ TEST_F(MediaSourcePlayerTest, AudioPrerollFinishesBeforeVideo) {
base::TimeDelta seek_ack_position = base::TimeDelta::FromMilliseconds(70);
DemuxerData audio_data = CreateReadFromDemuxerAckForAudio(0);
audio_data.access_units[0].timestamp = seek_ack_position;
- DemuxerData video_data = CreateReadFromDemuxerAckForVideo();
+ DemuxerData video_data = CreateReadFromDemuxerAckForVideo(false);
video_data.access_units[0].timestamp = seek_ack_position;
player_.OnDemuxerDataAvailable(audio_data);
player_.OnDemuxerDataAvailable(video_data);
@@ -1724,12 +1740,19 @@ TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) {
CreateNextTextureAndSetVideoSurface();
Start(CreateAudioVideoDemuxerConfigs());
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(true));
EXPECT_TRUE(GetMediaCodecBridge(false));
EnableAdaptiveVideoPlayback(false);
WaitForAudioVideoDecodeDone();
+ // If audio or video hasn't finished prerolling, let them finish it.
+ if (IsPrerolling(true))
+ PrerollDecoderToTime(true, base::TimeDelta(), base::TimeDelta(), true);
+ if (IsPrerolling(false))
+ PrerollDecoderToTime(false, base::TimeDelta(), base::TimeDelta(), false);
+ int expected_num_data_requests = demuxer_->num_data_requests();
+
// Simulate audio |kConfigChanged| prefetched as standalone access unit.
DemuxerConfigs audio_configs = CreateAudioDemuxerConfigs(kCodecVorbis, true);
player_.OnDemuxerDataAvailable(
@@ -1739,7 +1762,7 @@ TEST_F(MediaSourcePlayerTest, SimultaneousAudioVideoConfigChange) {
player_.OnDemuxerDataAvailable(
CreateReadFromDemuxerAckWithConfigChanged(
false, 0, CreateVideoDemuxerConfigs(true)));
- EXPECT_EQ(6, demuxer_->num_data_requests());
+ EXPECT_EQ(expected_num_data_requests + 2, demuxer_->num_data_requests());
EXPECT_TRUE(IsDrainingDecoder(true));
EXPECT_TRUE(IsDrainingDecoder(false));
@@ -1757,12 +1780,20 @@ TEST_F(MediaSourcePlayerTest,
CreateNextTextureAndSetVideoSurface();
Start(CreateAudioVideoDemuxerConfigs());
player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForAudio(0));
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
+ EXPECT_EQ(4, demuxer_->num_data_requests());
EXPECT_TRUE(GetMediaCodecBridge(true));
EXPECT_TRUE(GetMediaCodecBridge(false));
EnableAdaptiveVideoPlayback(true);
WaitForAudioVideoDecodeDone();
+ // If audio or video hasn't finished prerolling, let them finish it.
+ if (IsPrerolling(true))
+ PrerollDecoderToTime(true, base::TimeDelta(), base::TimeDelta(), true);
+ if (IsPrerolling(false))
+ PrerollDecoderToTime(false, base::TimeDelta(), base::TimeDelta(), false);
+ int expected_num_data_requests = demuxer_->num_data_requests();
+
// Simulate audio |kConfigChanged| prefetched as standalone access unit.
DemuxerConfigs audio_configs = CreateAudioDemuxerConfigs(kCodecVorbis, true);
player_.OnDemuxerDataAvailable(
@@ -1772,7 +1803,7 @@ TEST_F(MediaSourcePlayerTest,
player_.OnDemuxerDataAvailable(
CreateReadFromDemuxerAckWithConfigChanged(
false, 0, CreateVideoDemuxerConfigs(true)));
- EXPECT_EQ(6, demuxer_->num_data_requests());
+ EXPECT_EQ(expected_num_data_requests + 2, demuxer_->num_data_requests());
EXPECT_TRUE(IsDrainingDecoder(true));
EXPECT_FALSE(IsDrainingDecoder(false));
@@ -1789,6 +1820,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit0) {
// received in OnDemuxerDataAvailable() ostensibly while
// |PREFETCH_DONE_EVENT_PENDING|.
StartConfigChange(true, true, 0, false);
+ WaitForAudioDecodeDone();
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit1) {
@@ -1799,6 +1831,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInPrefetchUnit1) {
// received in OnDemuxerDataAvailable() ostensibly while
// |PREFETCH_DONE_EVENT_PENDING|.
StartConfigChange(true, true, 1, false);
+ WaitForAudioDecodeDone();
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit0AfterPrefetch) {
@@ -1809,6 +1842,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit0AfterPrefetch) {
// received in OnDemuxerDataAvailable() from data requested ostensibly while
// not prefetching.
StartConfigChange(true, false, 0, false);
+ WaitForAudioDecodeDone();
}
TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit1AfterPrefetch) {
@@ -1819,6 +1853,7 @@ TEST_F(MediaSourcePlayerTest, DemuxerConfigRequestedIfInUnit1AfterPrefetch) {
// received in OnDemuxerDataAvailable() from data requested ostensibly while
// not prefetching.
StartConfigChange(true, false, 1, false);
+ WaitForAudioDecodeDone();
}
TEST_F(MediaSourcePlayerTest, BrowserSeek_PrerollAfterBrowserSeek) {
@@ -1857,6 +1892,7 @@ TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChange) {
// 2 codecs should have been created, one before the config change, and one
// after it.
EXPECT_EQ(2, manager_.num_resources_requested());
+ WaitForVideoDecodeDone();
}
TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChangeWithAdaptivePlayback) {
@@ -1874,6 +1910,7 @@ TEST_F(MediaSourcePlayerTest, VideoDemuxerConfigChangeWithAdaptivePlayback) {
// Only 1 codec should have been created so far.
EXPECT_EQ(1, manager_.num_resources_requested());
+ WaitForVideoDecodeDone();
}
TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedBySeek) {
@@ -1926,13 +1963,13 @@ TEST_F(MediaSourcePlayerTest, DecoderDrainInterruptedBySurfaceChange) {
WaitForVideoDecodeDone();
EXPECT_FALSE(IsDrainingDecoder(false));
- EXPECT_FALSE(GetMediaCodecBridge(false));
EXPECT_TRUE(player_.IsPlaying());
- EXPECT_EQ(3, demuxer_->num_data_requests());
- // Finish the browser seek introduced by surface change.
- player_.OnDemuxerSeekDone(base::TimeDelta());
+ // The frame after the config change should always be an iframe, so no browser
+ // seek is needed when recreating the video decoder due to surface change.
+ EXPECT_TRUE(GetMediaCodecBridge(false));
EXPECT_EQ(4, demuxer_->num_data_requests());
+ EXPECT_EQ(0, demuxer_->num_seek_requests());
}
TEST_F(MediaSourcePlayerTest,
@@ -1943,7 +1980,7 @@ TEST_F(MediaSourcePlayerTest,
// should not cause any crashes.
CreateNextTextureAndSetVideoSurface();
StartVideoDecoderJob();
- DemuxerData data = CreateReadFromDemuxerAckForVideo();
+ DemuxerData data = CreateReadFromDemuxerAckForVideo(false);
player_.OnDemuxerDataAvailable(data);
// Trigger a surface change and decoder starvation.
@@ -2142,6 +2179,7 @@ TEST_F(MediaSourcePlayerTest, ConfigChangedThenReleaseThenStart) {
EXPECT_TRUE(player_.IsPlaying());
EXPECT_EQ(3, demuxer_->num_data_requests());
EXPECT_EQ(0, demuxer_->num_seek_requests());
+ WaitForAudioDecodeDone();
}
TEST_F(MediaSourcePlayerTest, BrowserSeek_ThenReleaseThenDemuxerSeekDone) {
@@ -2200,7 +2238,7 @@ TEST_F(MediaSourcePlayerTest, BrowserSeek_ThenReleaseThenStart) {
EXPECT_EQ(1, demuxer_->num_seek_requests());
// Decoder will be created once data is received.
- player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo());
+ player_.OnDemuxerDataAvailable(CreateReadFromDemuxerAckForVideo(false));
EXPECT_TRUE(GetMediaCodecBridge(false));
WaitForVideoDecodeDone();
}
@@ -2258,4 +2296,25 @@ TEST_F(MediaSourcePlayerTest, CurrentTimeKeepsIncreasingAfterConfigChange) {
DecodeAudioDataUntilOutputBecomesAvailable();
}
+TEST_F(MediaSourcePlayerTest, VideoMetadataChangeAfterConfigChange) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ // Test that after a config change, metadata change will be happen
+ // after decoder is drained.
+ StartConfigChange(false, true, 2, false);
+ EXPECT_EQ(1, manager_.num_metadata_changes());
+ EXPECT_FALSE(IsDrainingDecoder(false));
+
+ // Create video data with new resolutions.
+ DemuxerData data = CreateReadFromDemuxerAckForVideo(true);
+
+ // Wait for the metadata change.
+ while(manager_.num_metadata_changes() == 1) {
+ player_.OnDemuxerDataAvailable(data);
+ WaitForVideoDecodeDone();
+ }
+ EXPECT_EQ(2, manager_.num_metadata_changes());
+ WaitForVideoDecodeDone();
+}
+
} // namespace media
diff --git a/media/base/android/video_decoder_job.cc b/media/base/android/video_decoder_job.cc
index 1eb3723c5b..bfa0dc32a6 100644
--- a/media/base/android/video_decoder_job.cc
+++ b/media/base/android/video_decoder_job.cc
@@ -33,8 +33,10 @@ VideoDecoderJob::VideoDecoderJob(
request_data_cb,
on_demuxer_config_changed_cb),
video_codec_(kUnknownVideoCodec),
- width_(0),
- height_(0),
+ config_width_(0),
+ config_height_(0),
+ output_width_(0),
+ output_height_(0),
request_resources_cb_(request_resources_cb),
next_video_data_is_iframe_(true) {
}
@@ -69,6 +71,17 @@ void VideoDecoderJob::ReleaseDecoderResources() {
surface_ = gfx::ScopedJavaSurface();
}
+void VideoDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
+ video_codec_ = configs.video_codec;
+ config_width_ = configs.video_size.width();
+ config_height_ = configs.video_size.height();
+ set_is_content_encrypted(configs.is_video_encrypted);
+ if (!media_codec_bridge_) {
+ output_width_ = config_width_;
+ output_height_ = config_height_;
+ }
+}
+
void VideoDecoderJob::ReleaseOutputBuffer(
int output_buffer_index,
size_t size,
@@ -83,13 +96,6 @@ bool VideoDecoderJob::ComputeTimeToRender() const {
return true;
}
-void VideoDecoderJob::UpdateDemuxerConfigs(const DemuxerConfigs& configs) {
- video_codec_ = configs.video_codec;
- width_ = configs.video_size.width();
- height_ = configs.video_size.height();
- set_is_content_encrypted(configs.is_video_encrypted);
-}
-
bool VideoDecoderJob::IsCodecReconfigureNeeded(
const DemuxerConfigs& configs) const {
if (!media_codec_bridge_)
@@ -114,8 +120,8 @@ bool VideoDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const {
return video_codec_ != configs.video_codec ||
is_content_encrypted() != configs.is_video_encrypted ||
- width_ != configs.video_size.width() ||
- height_ != configs.video_size.height();
+ config_width_ != configs.video_size.width() ||
+ config_height_ != configs.video_size.height();
}
bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
@@ -133,7 +139,7 @@ bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
drm_bridge()->IsProtectedSurfaceRequired();
media_codec_bridge_.reset(VideoCodecBridge::CreateDecoder(
- video_codec_, is_secure, gfx::Size(width_, height_),
+ video_codec_, is_secure, gfx::Size(config_width_, config_height_),
surface_.j_surface().obj(), GetMediaCrypto().obj()));
if (!media_codec_bridge_)
@@ -147,6 +153,16 @@ void VideoDecoderJob::CurrentDataConsumed(bool is_config_change) {
next_video_data_is_iframe_ = is_config_change;
}
+bool VideoDecoderJob::UpdateOutputFormat() {
+ if (!media_codec_bridge_)
+ return false;
+ int prev_output_width = output_width_;
+ int prev_output_height = output_height_;
+ media_codec_bridge_->GetOutputFormat(&output_width_, &output_height_);
+ return (output_width_ != prev_output_width) ||
+ (output_height_ != prev_output_height);
+}
+
bool VideoDecoderJob::IsProtectedSurfaceRequired() {
return is_content_encrypted() && drm_bridge() &&
drm_bridge()->IsProtectedSurfaceRequired();
diff --git a/media/base/android/video_decoder_job.h b/media/base/android/video_decoder_job.h
index ac3dc27b10..c6005fb144 100644
--- a/media/base/android/video_decoder_job.h
+++ b/media/base/android/video_decoder_job.h
@@ -35,13 +35,14 @@ class VideoDecoderJob : public MediaDecoderJob {
virtual bool HasStream() const OVERRIDE;
virtual void Flush() OVERRIDE;
virtual void ReleaseDecoderResources() OVERRIDE;
+ virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) OVERRIDE;
bool next_video_data_is_iframe() {
return next_video_data_is_iframe_;
}
- int width() const { return width_; }
- int height() const { return height_; }
+ int output_width() const { return output_width_; }
+ int output_height() const { return output_height_; }
private:
// MediaDecoderJob implementation.
@@ -52,21 +53,25 @@ class VideoDecoderJob : public MediaDecoderJob {
base::TimeDelta current_presentation_timestamp,
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;
virtual void CurrentDataConsumed(bool is_config_change) OVERRIDE;
+ virtual bool UpdateOutputFormat() OVERRIDE;
// Returns true if a protected surface is required for video playback.
bool IsProtectedSurfaceRequired();
// Video configs from the demuxer.
VideoCodec video_codec_;
- int width_;
- int height_;
+ int config_width_;
+ int config_height_;
+
+ // Video output format.
+ int output_width_;
+ int output_height_;
// The surface object currently owned by the player.
gfx::ScopedJavaSurface surface_;
diff --git a/media/base/container_names.cc b/media/base/container_names.cc
index 0f629f8a64..7b188b6b04 100644
--- a/media/base/container_names.cc
+++ b/media/base/container_names.cc
@@ -954,7 +954,7 @@ static bool CheckMov(const uint8* buffer, int buffer_size) {
int offset = 0;
while (offset + 8 < buffer_size) {
- int atomsize = Read32(buffer + offset);
+ uint32 atomsize = Read32(buffer + offset);
uint32 atomtype = Read32(buffer + offset + 4);
// Only need to check for ones that are valid at the top level.
switch (atomtype) {
@@ -985,7 +985,7 @@ static bool CheckMov(const uint8* buffer, int buffer_size) {
break; // Offset is way past buffer size.
atomsize = Read32(buffer + offset + 12);
}
- if (atomsize <= 0)
+ if (atomsize == 0 || atomsize > static_cast<size_t>(buffer_size))
break; // Indicates the last atom or length too big.
offset += atomsize;
}