Commit 07cdc5cc authored by qinmin's avatar qinmin Committed by Commit bot

Send metadata change to renderer after decoder is drained

MediaSourcePlayer sends a metadata change to renderer whenever it receives a config change.
However, the decoder might still be in the draining process when this happens.
To fix this, we should wait for a OUTPUT_FORMAT_CHANGED status before sending out the IPC.

BUG=381975

Review URL: https://codereview.chromium.org/623363002

Cr-Commit-Position: refs/heads/master@{#299338}
parent bca18a07
...@@ -51,6 +51,17 @@ bool AudioDecoderJob::HasStream() const { ...@@ -51,6 +51,17 @@ bool AudioDecoderJob::HasStream() const {
return audio_codec_ != kUnknownAudioCodec; 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) { void AudioDecoderJob::SetVolume(double volume) {
volume_ = volume; volume_ = volume;
SetVolumeInternal(); SetVolumeInternal();
...@@ -94,17 +105,6 @@ bool AudioDecoderJob::ComputeTimeToRender() const { ...@@ -94,17 +105,6 @@ bool AudioDecoderJob::ComputeTimeToRender() const {
return false; 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( bool AudioDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const { const DemuxerConfigs& configs) const {
return audio_codec_ != configs.audio_codec || return audio_codec_ != configs.audio_codec ||
......
...@@ -29,6 +29,7 @@ class AudioDecoderJob : public MediaDecoderJob { ...@@ -29,6 +29,7 @@ class AudioDecoderJob : public MediaDecoderJob {
// MediaDecoderJob implementation. // MediaDecoderJob implementation.
virtual bool HasStream() const override; virtual bool HasStream() const override;
virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) override;
// Sets the volume of the audio output. // Sets the volume of the audio output.
void SetVolume(double volume); void SetVolume(double volume);
...@@ -47,7 +48,6 @@ class AudioDecoderJob : public MediaDecoderJob { ...@@ -47,7 +48,6 @@ class AudioDecoderJob : public MediaDecoderJob {
virtual bool ComputeTimeToRender() const override; virtual bool ComputeTimeToRender() const override;
virtual bool AreDemuxerConfigsChanged( virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const override; const DemuxerConfigs& configs) const override;
virtual void UpdateDemuxerConfigs(const DemuxerConfigs& configs) override;
virtual bool CreateMediaCodecBridgeInternal() override; virtual bool CreateMediaCodecBridgeInternal() override;
// Helper method to set the audio output volume. // Helper method to set the audio output volume.
......
...@@ -202,13 +202,6 @@ void MediaDecoderJob::ReleaseDecoderResources() { ...@@ -202,13 +202,6 @@ void MediaDecoderJob::ReleaseDecoderResources() {
release_resources_pending_ = true; 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> MediaDecoderJob::GetMediaCrypto() {
base::android::ScopedJavaLocalRef<jobject> media_crypto; base::android::ScopedJavaLocalRef<jobject> media_crypto;
if (drm_bridge_) if (drm_bridge_)
...@@ -334,19 +327,18 @@ void MediaDecoderJob::DecodeCurrentAccessUnit( ...@@ -334,19 +327,18 @@ void MediaDecoderJob::DecodeCurrentAccessUnit(
int index = CurrentReceivedDataChunkIndex(); int index = CurrentReceivedDataChunkIndex();
const DemuxerConfigs& configs = received_data_[index].demuxer_configs[0]; const DemuxerConfigs& configs = received_data_[index].demuxer_configs[0];
bool reconfigure_needed = IsCodecReconfigureNeeded(configs); bool reconfigure_needed = IsCodecReconfigureNeeded(configs);
// TODO(qinmin): |config_changed_cb_| should be run after draining finishes. SetDemuxerConfigs(configs);
// http://crbug.com/381975.
if (SetDemuxerConfigs(configs))
config_changed_cb_.Run();
if (!drain_decoder_) { if (!drain_decoder_) {
// If we haven't decoded any data yet, just skip the current access unit // If we haven't decoded any data yet, just skip the current access unit
// and request the MediaCodec to be recreated on next Decode(). // and request the MediaCodec to be recreated on next Decode().
if (skip_eos_enqueue_ || !reconfigure_needed) { if (skip_eos_enqueue_ || !reconfigure_needed) {
need_to_reconfig_decoder_job_ = need_to_reconfig_decoder_job_ =
need_to_reconfig_decoder_job_ || reconfigure_needed; 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( ui_task_runner_->PostTask(FROM_HERE, base::Bind(
&MediaDecoderJob::OnDecodeCompleted, base::Unretained(this), &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this),
MEDIA_CODEC_OUTPUT_FORMAT_CHANGED, kNoTimestamp(), kNoTimestamp())); MEDIA_CODEC_OK, kNoTimestamp(), kNoTimestamp()));
return; return;
} }
// Start draining the decoder so that all the remaining frames are // Start draining the decoder so that all the remaining frames are
...@@ -542,6 +534,9 @@ void MediaDecoderJob::OnDecodeCompleted( ...@@ -542,6 +534,9 @@ void MediaDecoderJob::OnDecodeCompleted(
status = MEDIA_CODEC_OK; status = MEDIA_CODEC_OK;
} }
if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED && UpdateOutputFormat())
config_changed_cb_.Run();
if (release_resources_pending_) { if (release_resources_pending_) {
ReleaseMediaCodecBridge(); ReleaseMediaCodecBridge();
release_resources_pending_ = false; release_resources_pending_ = false;
...@@ -643,6 +638,10 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded( ...@@ -643,6 +638,10 @@ bool MediaDecoderJob::IsCodecReconfigureNeeded(
return true; return true;
} }
bool MediaDecoderJob::UpdateOutputFormat() {
return false;
}
void MediaDecoderJob::ReleaseMediaCodecBridge() { void MediaDecoderJob::ReleaseMediaCodecBridge() {
if (!media_codec_bridge_) if (!media_codec_bridge_)
return; return;
......
...@@ -84,9 +84,8 @@ class MediaDecoderJob { ...@@ -84,9 +84,8 @@ class MediaDecoderJob {
// Releases all the decoder resources as the current tab is going background. // Releases all the decoder resources as the current tab is going background.
virtual void ReleaseDecoderResources(); virtual void ReleaseDecoderResources();
// Sets the demuxer configs. Returns true if configs has changed, or false // Sets the demuxer configs.
// otherwise. virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) = 0;
bool SetDemuxerConfigs(const DemuxerConfigs& configs);
// Returns whether the decoder has finished decoding all the data. // Returns whether the decoder has finished decoding all the data.
bool OutputEOSReached() const; bool OutputEOSReached() const;
...@@ -228,13 +227,14 @@ class MediaDecoderJob { ...@@ -228,13 +227,14 @@ class MediaDecoderJob {
virtual bool AreDemuxerConfigsChanged( virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const = 0; 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 // Returns true if |media_codec_bridge_| needs to be reconfigured for the
// new DemuxerConfigs, or false otherwise. // new DemuxerConfigs, or false otherwise.
virtual bool IsCodecReconfigureNeeded(const DemuxerConfigs& configs) const; 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. // Return the index to |received_data_| that is not currently being decoded.
size_t inactive_demuxer_data_index() const { size_t inactive_demuxer_data_index() const {
return 1 - current_demuxer_data_index_; return 1 - current_demuxer_data_index_;
......
...@@ -150,11 +150,11 @@ bool MediaSourcePlayer::IsPlaying() { ...@@ -150,11 +150,11 @@ bool MediaSourcePlayer::IsPlaying() {
} }
int MediaSourcePlayer::GetVideoWidth() { int MediaSourcePlayer::GetVideoWidth() {
return video_decoder_job_->width(); return video_decoder_job_->output_width();
} }
int MediaSourcePlayer::GetVideoHeight() { int MediaSourcePlayer::GetVideoHeight() {
return video_decoder_job_->height(); return video_decoder_job_->output_height();
} }
void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) { void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
......
...@@ -45,6 +45,7 @@ class MockMediaPlayerManager : public MediaPlayerManager { ...@@ -45,6 +45,7 @@ class MockMediaPlayerManager : public MediaPlayerManager {
: message_loop_(message_loop), : message_loop_(message_loop),
playback_completed_(false), playback_completed_(false),
num_resources_requested_(0), num_resources_requested_(0),
num_metadata_changes_(0),
timestamp_updated_(false) {} timestamp_updated_(false) {}
virtual ~MockMediaPlayerManager() {} virtual ~MockMediaPlayerManager() {}
...@@ -62,7 +63,9 @@ class MockMediaPlayerManager : public MediaPlayerManager { ...@@ -62,7 +63,9 @@ class MockMediaPlayerManager : public MediaPlayerManager {
} }
virtual void OnMediaMetadataChanged( virtual void OnMediaMetadataChanged(
int player_id, base::TimeDelta duration, int width, int height, 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 { virtual void OnPlaybackComplete(int player_id) override {
playback_completed_ = true; playback_completed_ = true;
if (message_loop_->is_running()) if (message_loop_->is_running())
...@@ -92,6 +95,10 @@ class MockMediaPlayerManager : public MediaPlayerManager { ...@@ -92,6 +95,10 @@ class MockMediaPlayerManager : public MediaPlayerManager {
return num_resources_requested_; return num_resources_requested_;
} }
int num_metadata_changes() const {
return num_metadata_changes_;
}
void OnMediaResourcesRequested(int player_id) { void OnMediaResourcesRequested(int player_id) {
num_resources_requested_++; num_resources_requested_++;
} }
...@@ -109,6 +116,8 @@ class MockMediaPlayerManager : public MediaPlayerManager { ...@@ -109,6 +116,8 @@ class MockMediaPlayerManager : public MediaPlayerManager {
bool playback_completed_; bool playback_completed_;
// The number of resource requests this object has seen. // The number of resource requests this object has seen.
int num_resources_requested_; int num_resources_requested_;
// The number of metadata changes reported by the player.
int num_metadata_changes_;
// Playback timestamp was updated. // Playback timestamp was updated.
bool timestamp_updated_; bool timestamp_updated_;
...@@ -272,7 +281,7 @@ class MediaSourcePlayerTest : public testing::Test { ...@@ -272,7 +281,7 @@ class MediaSourcePlayerTest : public testing::Test {
DemuxerConfigs configs; DemuxerConfigs configs;
configs.video_codec = kCodecVP8; configs.video_codec = kCodecVP8;
configs.video_size = 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.is_video_encrypted = false;
configs.duration = kDefaultDuration; configs.duration = kDefaultDuration;
return configs; return configs;
...@@ -2275,4 +2284,30 @@ TEST_F(MediaSourcePlayerTest, CurrentTimeKeepsIncreasingAfterConfigChange) { ...@@ -2275,4 +2284,30 @@ TEST_F(MediaSourcePlayerTest, CurrentTimeKeepsIncreasingAfterConfigChange) {
DecodeAudioDataUntilOutputBecomesAvailable(); 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();
AccessUnit unit;
unit.status = DemuxerStream::kOk;
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vp8-I-frame-640x240");
unit.data = std::vector<uint8>(
buffer->data(), buffer->data() + buffer->data_size());
data.access_units[0] = unit;
// Wait for the metadata change.
while(manager_.num_metadata_changes() == 1) {
player_.OnDemuxerDataAvailable(data);
WaitForVideoDecodeDone();
}
EXPECT_EQ(2, manager_.num_metadata_changes());
}
} // namespace media } // namespace media
...@@ -33,8 +33,10 @@ VideoDecoderJob::VideoDecoderJob( ...@@ -33,8 +33,10 @@ VideoDecoderJob::VideoDecoderJob(
request_data_cb, request_data_cb,
on_demuxer_config_changed_cb), on_demuxer_config_changed_cb),
video_codec_(kUnknownVideoCodec), video_codec_(kUnknownVideoCodec),
width_(0), config_width_(0),
height_(0), config_height_(0),
output_width_(0),
output_height_(0),
request_resources_cb_(request_resources_cb), request_resources_cb_(request_resources_cb),
next_video_data_is_iframe_(true) { next_video_data_is_iframe_(true) {
} }
...@@ -69,6 +71,17 @@ void VideoDecoderJob::ReleaseDecoderResources() { ...@@ -69,6 +71,17 @@ void VideoDecoderJob::ReleaseDecoderResources() {
surface_ = gfx::ScopedJavaSurface(); 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( void VideoDecoderJob::ReleaseOutputBuffer(
int output_buffer_index, int output_buffer_index,
size_t size, size_t size,
...@@ -83,13 +96,6 @@ bool VideoDecoderJob::ComputeTimeToRender() const { ...@@ -83,13 +96,6 @@ bool VideoDecoderJob::ComputeTimeToRender() const {
return true; 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( bool VideoDecoderJob::IsCodecReconfigureNeeded(
const DemuxerConfigs& configs) const { const DemuxerConfigs& configs) const {
if (!media_codec_bridge_) if (!media_codec_bridge_)
...@@ -114,8 +120,8 @@ bool VideoDecoderJob::AreDemuxerConfigsChanged( ...@@ -114,8 +120,8 @@ bool VideoDecoderJob::AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const { const DemuxerConfigs& configs) const {
return video_codec_ != configs.video_codec || return video_codec_ != configs.video_codec ||
is_content_encrypted() != configs.is_video_encrypted || is_content_encrypted() != configs.is_video_encrypted ||
width_ != configs.video_size.width() || config_width_ != configs.video_size.width() ||
height_ != configs.video_size.height(); config_height_ != configs.video_size.height();
} }
bool VideoDecoderJob::CreateMediaCodecBridgeInternal() { bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
...@@ -133,7 +139,7 @@ bool VideoDecoderJob::CreateMediaCodecBridgeInternal() { ...@@ -133,7 +139,7 @@ bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
drm_bridge()->IsProtectedSurfaceRequired(); drm_bridge()->IsProtectedSurfaceRequired();
media_codec_bridge_.reset(VideoCodecBridge::CreateDecoder( 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())); surface_.j_surface().obj(), GetMediaCrypto().obj()));
if (!media_codec_bridge_) if (!media_codec_bridge_)
...@@ -147,6 +153,16 @@ void VideoDecoderJob::CurrentDataConsumed(bool is_config_change) { ...@@ -147,6 +153,16 @@ void VideoDecoderJob::CurrentDataConsumed(bool is_config_change) {
next_video_data_is_iframe_ = 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() { bool VideoDecoderJob::IsProtectedSurfaceRequired() {
return is_content_encrypted() && drm_bridge() && return is_content_encrypted() && drm_bridge() &&
drm_bridge()->IsProtectedSurfaceRequired(); drm_bridge()->IsProtectedSurfaceRequired();
......
...@@ -35,13 +35,14 @@ class VideoDecoderJob : public MediaDecoderJob { ...@@ -35,13 +35,14 @@ class VideoDecoderJob : public MediaDecoderJob {
virtual bool HasStream() const override; virtual bool HasStream() const override;
virtual void Flush() override; virtual void Flush() override;
virtual void ReleaseDecoderResources() override; virtual void ReleaseDecoderResources() override;
virtual void SetDemuxerConfigs(const DemuxerConfigs& configs) override;
bool next_video_data_is_iframe() { bool next_video_data_is_iframe() {
return next_video_data_is_iframe_; return next_video_data_is_iframe_;
} }
int width() const { return width_; } int output_width() const { return output_width_; }
int height() const { return height_; } int output_height() const { return output_height_; }
private: private:
// MediaDecoderJob implementation. // MediaDecoderJob implementation.
...@@ -52,21 +53,25 @@ class VideoDecoderJob : public MediaDecoderJob { ...@@ -52,21 +53,25 @@ class VideoDecoderJob : public MediaDecoderJob {
base::TimeDelta current_presentation_timestamp, base::TimeDelta current_presentation_timestamp,
const ReleaseOutputCompletionCallback& callback) override; const ReleaseOutputCompletionCallback& callback) override;
virtual bool ComputeTimeToRender() const override; virtual bool ComputeTimeToRender() const override;
virtual void UpdateDemuxerConfigs(const DemuxerConfigs& configs) override;
virtual bool IsCodecReconfigureNeeded( virtual bool IsCodecReconfigureNeeded(
const DemuxerConfigs& configs) const override; const DemuxerConfigs& configs) const override;
virtual bool AreDemuxerConfigsChanged( virtual bool AreDemuxerConfigsChanged(
const DemuxerConfigs& configs) const override; const DemuxerConfigs& configs) const override;
virtual bool CreateMediaCodecBridgeInternal() override; virtual bool CreateMediaCodecBridgeInternal() override;
virtual void CurrentDataConsumed(bool is_config_change) override; virtual void CurrentDataConsumed(bool is_config_change) override;
virtual bool UpdateOutputFormat() override;
// Returns true if a protected surface is required for video playback. // Returns true if a protected surface is required for video playback.
bool IsProtectedSurfaceRequired(); bool IsProtectedSurfaceRequired();
// Video configs from the demuxer. // Video configs from the demuxer.
VideoCodec video_codec_; VideoCodec video_codec_;
int width_; int config_width_;
int height_; int config_height_;
// Video output format.
int output_width_;
int output_height_;
// The surface object currently owned by the player. // The surface object currently owned by the player.
gfx::ScopedJavaSurface surface_; gfx::ScopedJavaSurface surface_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment