summaryrefslogtreecommitdiff
path: root/media
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2013-08-07 11:04:47 +0100
committerBen Murdoch <benm@google.com>2013-08-07 11:04:47 +0100
commit3240926e260ce088908e02ac07a6cf7b0c0cbf44 (patch)
tree5ff5fa16b2fb9278c6d731b3d19123731f3f8626 /media
parent3b21a50ee4fe6f71bb117cbee9998a4f465eea9d (diff)
downloadchromium_org-3240926e260ce088908e02ac07a6cf7b0c0cbf44.tar.gz
Merge from Chromium at DEPS revision r216133
This commit was generated by merge_to_master.py. Change-Id: I541d5d1d8520b6b3829fbc1fa18552bf9ad4a5c7
Diffstat (limited to 'media')
-rw-r--r--media/DEPS1
-rw-r--r--media/base/android/media_source_player.cc34
-rw-r--r--media/base/android/media_source_player.h6
-rw-r--r--media/base/android/media_source_player_unittest.cc35
-rw-r--r--media/base/media_log.cc30
-rw-r--r--media/base/media_log.h6
-rw-r--r--media/base/media_log_event.h3
-rw-r--r--media/base/sample_format.cc23
-rw-r--r--media/base/sample_format.h3
-rw-r--r--media/base/video_frame.cc28
-rw-r--r--media/base/video_frame.h3
-rw-r--r--media/filters/ffmpeg_demuxer.cc77
12 files changed, 227 insertions, 22 deletions
diff --git a/media/DEPS b/media/DEPS
index 7bb03fcbd8..9c27abac17 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -3,7 +3,6 @@ include_rules = [
"+jni",
"+third_party/ffmpeg",
"+third_party/libvpx",
- "+third_party/openmax",
"+third_party/opus",
"+third_party/skia",
"+ui/gfx",
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index 5d4aa98b66..979d1ccff4 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -67,6 +67,7 @@ MediaDecoderJob::MediaDecoderJob(
media_codec_bridge_(media_codec_bridge),
needs_flush_(false),
is_audio_(is_audio),
+ input_eos_encountered_(false),
weak_this_(this),
is_decoding_(false) {
}
@@ -130,7 +131,9 @@ MediaDecoderJob::DecodeStatus MediaDecoderJob::QueueInputBuffer(
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()) {
+ return DECODE_INPUT_END_OF_STREAM;
+ }
+ if (unit.key_id.empty()) {
media_codec_bridge_->QueueInputBuffer(
input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp);
} else {
@@ -157,14 +160,21 @@ void MediaDecoderJob::DecodeInternal(
const MediaDecoderJob::DecoderCallback& callback) {
if (needs_flush) {
DVLOG(1) << "DecodeInternal needs flush.";
+ input_eos_encountered_ = false;
media_codec_bridge_->Reset();
}
- 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;
+ DecodeStatus decode_status = DECODE_INPUT_END_OF_STREAM;
+ if (!input_eos_encountered_) {
+ decode_status = QueueInputBuffer(unit);
+ if (decode_status == DECODE_INPUT_END_OF_STREAM) {
+ input_eos_encountered_ = true;
+ } else if (decode_status != DECODE_SUCCEEDED) {
+ ui_loop_->PostTask(FROM_HERE,
+ base::Bind(callback, decode_status,
+ start_presentation_timestamp, 0));
+ return;
+ }
}
size_t offset = 0;
@@ -178,12 +188,14 @@ void MediaDecoderJob::DecodeInternal(
timeout, &offset, &size, &presentation_timestamp, &end_of_stream);
if (end_of_stream)
- decode_status = DECODE_END_OF_STREAM;
+ decode_status = DECODE_OUTPUT_END_OF_STREAM;
switch (outputBufferIndex) {
case MediaCodecBridge::INFO_OUTPUT_BUFFERS_CHANGED:
+ DCHECK(decode_status != DECODE_INPUT_END_OF_STREAM);
media_codec_bridge_->GetOutputBuffers();
break;
case MediaCodecBridge::INFO_OUTPUT_FORMAT_CHANGED:
+ DCHECK(decode_status != DECODE_INPUT_END_OF_STREAM);
// TODO(qinmin): figure out what we should do if format changes.
decode_status = DECODE_FORMAT_CHANGED;
break;
@@ -232,7 +244,7 @@ void MediaDecoderJob::ReleaseOutputBuffer(
static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->PlayOutputBuffer(
outputBufferIndex, size);
}
- if (status != DECODE_END_OF_STREAM || size != 0u)
+ if (status != DECODE_OUTPUT_END_OF_STREAM || size != 0u)
media_codec_bridge_->ReleaseOutputBuffer(outputBufferIndex, !is_audio_);
ui_loop_->PostTask(FROM_HERE, base::Bind(
callback, status, presentation_timestamp, is_audio_ ? size : 0));
@@ -639,7 +651,9 @@ void MediaSourcePlayer::MediaDecoderCallback(
return;
}
- if (decode_status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER) {
+ // If the input reaches input EOS, there is no need to request new data.
+ if (decode_status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER &&
+ decode_status != MediaDecoderJob::DECODE_INPUT_END_OF_STREAM) {
if (is_audio)
audio_access_unit_index_++;
else
@@ -656,7 +670,7 @@ void MediaSourcePlayer::MediaDecoderCallback(
UpdateTimestamps(presentation_timestamp, audio_output_bytes);
}
- if (decode_status == MediaDecoderJob::DECODE_END_OF_STREAM) {
+ if (decode_status == MediaDecoderJob::DECODE_OUTPUT_END_OF_STREAM) {
PlaybackCompleted(is_audio);
return;
}
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index 7253d565e3..05fd224e04 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -43,7 +43,8 @@ class MediaDecoderJob {
DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER,
DECODE_TRY_DEQUEUE_OUTPUT_AGAIN_LATER,
DECODE_FORMAT_CHANGED,
- DECODE_END_OF_STREAM,
+ DECODE_INPUT_END_OF_STREAM,
+ DECODE_OUTPUT_END_OF_STREAM,
DECODE_FAILED,
};
@@ -111,6 +112,9 @@ class MediaDecoderJob {
// Whether this is an audio decoder.
bool is_audio_;
+ // Whether input EOS is encountered.
+ bool input_eos_encountered_;
+
// Weak pointer passed to media decoder jobs for callbacks. It is bounded to
// the decoder thread.
base::WeakPtrFactory<MediaDecoderJob> weak_this_;
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index cc99cf7cde..40d28e43f5 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -35,7 +35,10 @@ class MockMediaPlayerManager : public MediaPlayerManager {
virtual void OnMediaMetadataChanged(
int player_id, base::TimeDelta duration, int width, int height,
bool success) OVERRIDE {}
- virtual void OnPlaybackComplete(int player_id) OVERRIDE {}
+ virtual void OnPlaybackComplete(int player_id) OVERRIDE {
+ if (message_loop_.is_running())
+ message_loop_.Quit();
+ }
virtual void OnMediaInterrupted(int player_id) OVERRIDE {}
virtual void OnBufferingUpdate(int player_id, int percentage) OVERRIDE {}
virtual void OnSeekComplete(int player_id,
@@ -426,4 +429,34 @@ TEST_F(MediaSourcePlayerTest,
EXPECT_LE(100.0, (current - previous).InMillisecondsF());
}
+TEST_F(MediaSourcePlayerTest, NoRequestForDataAfterInputEOS) {
+ if (!MediaCodecBridge::IsAvailable())
+ return;
+
+ // Test MediaSourcePlayer will not request for new data after input EOS is
+ // reached.
+ scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
+ new gfx::SurfaceTextureBridge(0));
+ gfx::ScopedJavaSurface surface(surface_texture.get());
+ player_->SetVideoSurface(surface.Pass());
+ StartVideoDecoderJob();
+ player_->OnSeekRequestAck(manager_->last_seek_request_id());
+ EXPECT_EQ(1, manager_->num_requests());
+ // Send the first input chunk.
+ player_->ReadFromDemuxerAck(CreateReadFromDemuxerAckForVideo());
+ manager_->message_loop()->Run();
+ EXPECT_EQ(2, manager_->num_requests());
+
+ // Send EOS.
+ MediaPlayerHostMsg_ReadFromDemuxerAck_Params ack_params;
+ ack_params.type = DemuxerStream::VIDEO;
+ ack_params.access_units.resize(1);
+ ack_params.access_units[0].status = DemuxerStream::kOk;
+ ack_params.access_units[0].end_of_stream = true;
+ player_->ReadFromDemuxerAck(ack_params);
+ manager_->message_loop()->Run();
+ // No more request for data should be made.
+ EXPECT_EQ(2, manager_->num_requests());
+}
+
} // namespace media
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index 0f2e182284..8a07b020c7 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -56,6 +56,8 @@ const char* MediaLog::EventTypeToString(MediaLogEvent::Type type) {
return "BUFFERED_EXTENTS_CHANGED";
case MediaLogEvent::MEDIA_SOURCE_ERROR:
return "MEDIA_SOURCE_ERROR";
+ case MediaLogEvent::PROPERTY_CHANGE:
+ return "PROPERTY_CHANGE";
}
NOTREACHED();
return NULL;
@@ -198,4 +200,32 @@ scoped_ptr<MediaLogEvent> MediaLog::CreateMediaSourceErrorEvent(
return event.Pass();
}
+void MediaLog::SetStringProperty(
+ const char* key, const std::string& value) {
+ scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
+ event->params.SetString(key, value);
+ AddEvent(event.Pass());
+}
+
+void MediaLog::SetIntegerProperty(
+ const char* key, int value) {
+ scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
+ event->params.SetInteger(key, value);
+ AddEvent(event.Pass());
+}
+
+void MediaLog::SetDoubleProperty(
+ const char* key, double value) {
+ scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
+ event->params.SetDouble(key, value);
+ AddEvent(event.Pass());
+}
+
+void MediaLog::SetBooleanProperty(
+ const char* key, bool value) {
+ scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
+ event->params.SetBoolean(key, value);
+ AddEvent(event.Pass());
+}
+
} //namespace media
diff --git a/media/base/media_log.h b/media/base/media_log.h
index 191de33b56..1d25c0973a 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -68,6 +68,12 @@ class MEDIA_EXPORT MediaLog : public base::RefCountedThreadSafe<MediaLog> {
scoped_ptr<MediaLogEvent> CreateMediaSourceErrorEvent(
const std::string& error);
+ // Report a property change without an accompanying event.
+ void SetStringProperty(const char* key, const std::string& value);
+ void SetIntegerProperty(const char* key, int value);
+ void SetDoubleProperty(const char* key, double value);
+ void SetBooleanProperty(const char* key, bool value);
+
protected:
friend class base::RefCountedThreadSafe<MediaLog>;
virtual ~MediaLog();
diff --git a/media/base/media_log_event.h b/media/base/media_log_event.h
index 142626b1d7..811d1131a7 100644
--- a/media/base/media_log_event.h
+++ b/media/base/media_log_event.h
@@ -87,6 +87,9 @@ struct MediaLogEvent {
// Errors reported by Media Source Extensions code.
MEDIA_SOURCE_ERROR,
// params: "error": Error string describing the error detected.
+
+ // A property has changed without any special event occurring.
+ PROPERTY_CHANGE,
};
int32 id;
diff --git a/media/base/sample_format.cc b/media/base/sample_format.cc
index 3fdcf1018e..a4791cd686 100644
--- a/media/base/sample_format.cc
+++ b/media/base/sample_format.cc
@@ -29,4 +29,27 @@ int SampleFormatToBytesPerChannel(SampleFormat sample_format) {
return 0;
}
+const char* SampleFormatToString(SampleFormat sample_format) {
+ switch(sample_format) {
+ case kUnknownSampleFormat:
+ return "Unknown sample format";
+ case kSampleFormatU8:
+ return "Unsigned 8-bit with bias of 128";
+ case kSampleFormatS16:
+ return "Signed 16-bit";
+ case kSampleFormatS32:
+ return "Signed 32-bit";
+ case kSampleFormatF32:
+ return "Float 32-bit";
+ case kSampleFormatPlanarS16:
+ return "Signed 16-bit planar";
+ case kSampleFormatPlanarF32:
+ return "Float 32-bit planar";
+ case kSampleFormatMax:
+ break;
+ }
+ NOTREACHED() << "Invalid sample format provided: " << sample_format;
+ return "";
+}
+
} // namespace media
diff --git a/media/base/sample_format.h b/media/base/sample_format.h
index bcaa5b2785..3d2799fa12 100644
--- a/media/base/sample_format.h
+++ b/media/base/sample_format.h
@@ -29,6 +29,9 @@ enum SampleFormat {
// |sample_format|.
MEDIA_EXPORT int SampleFormatToBytesPerChannel(SampleFormat sample_format);
+// Returns the name of the sample format as a string
+MEDIA_EXPORT const char* SampleFormatToString(SampleFormat sample_format);
+
} // namespace media
#endif // MEDIA_BASE_SAMPLE_FORMAT_H
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index de7440ae20..10ad0511ff 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -43,6 +43,34 @@ scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
}
// static
+std::string VideoFrame::FormatToString(VideoFrame::Format format) {
+ switch (format) {
+ case VideoFrame::INVALID:
+ return "INVALID";
+ case VideoFrame::RGB32:
+ return "RGB32";
+ case VideoFrame::YV12:
+ return "YV12";
+ case VideoFrame::YV16:
+ return "YV16";
+ case VideoFrame::EMPTY:
+ return "EMPTY";
+ case VideoFrame::I420:
+ return "I420";
+ case VideoFrame::NATIVE_TEXTURE:
+ return "NATIVE_TEXTURE";
+#if defined(GOOGLE_TV)
+ case VideoFrame::HOLE:
+ return "HOLE";
+#endif
+ case VideoFrame::YV12A:
+ return "YV12A";
+ }
+ NOTREACHED() << "Invalid videoframe format provided: " << format;
+ return "";
+}
+
+// static
bool VideoFrame::IsValidConfig(VideoFrame::Format format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 128143c4df..8207d9be1d 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -53,6 +53,9 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
YV12A = 14, // 20bpp YUVA planar 1x1 Y, 2x2 VU, 1x1 A samples.
};
+ // Returns the name of a Format as a string.
+ static std::string FormatToString(Format format);
+
// This class calls the TextureNoLongerNeededCallback when the last reference
// on the class is destroyed. The VideoFrame holds a reference to the mailbox
// but anyone else who queries the mailbox should also hold a reference while
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 70c55963f1..14b1fff99c 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -17,6 +17,7 @@
#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "base/time/time.h"
#include "media/base/audio_decoder_config.h"
@@ -491,8 +492,12 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
// partial playback. At least one audio or video stream must be playable.
AVFormatContext* format_context = glue_->format_context();
streams_.resize(format_context->nb_streams);
- bool found_audio_stream = false;
- bool found_video_stream = false;
+
+ AVStream* audio_stream = NULL;
+ AudioDecoderConfig audio_config;
+
+ AVStream* video_stream = NULL;
+ VideoDecoderConfig video_config;
base::TimeDelta max_duration;
for (size_t i = 0; i < format_context->nb_streams; ++i) {
@@ -501,31 +506,32 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
AVMediaType codec_type = codec_context->codec_type;
if (codec_type == AVMEDIA_TYPE_AUDIO) {
- if (found_audio_stream)
+ if (audio_stream)
continue;
+
// Log the codec detected, whether it is supported or not.
UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec",
codec_context->codec_id);
// Ensure the codec is supported. IsValidConfig() also checks that the
// channel layout and sample format are valid.
- AudioDecoderConfig audio_config;
AVStreamToAudioDecoderConfig(stream, &audio_config, false);
if (!audio_config.IsValidConfig())
continue;
- found_audio_stream = true;
+ audio_stream = stream;
} else if (codec_type == AVMEDIA_TYPE_VIDEO) {
- if (found_video_stream)
+ if (video_stream)
continue;
+
// Log the codec detected, whether it is supported or not.
UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec",
codec_context->codec_id);
// Ensure the codec is supported. IsValidConfig() also checks that the
// frame size and visible size are valid.
- VideoDecoderConfig video_config;
AVStreamToVideoDecoderConfig(stream, &video_config, false);
+
if (!video_config.IsValidConfig())
continue;
- found_video_stream = true;
+ video_stream = stream;
} else {
continue;
}
@@ -541,7 +547,7 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
}
}
- if (!found_audio_stream && !found_video_stream) {
+ if (!audio_stream && !video_stream) {
status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
return;
}
@@ -579,6 +585,59 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
if (bitrate_ > 0)
data_source_->SetBitrate(bitrate_);
+ // Audio logging
+ if (audio_stream) {
+ AVCodecContext* audio_codec = audio_stream->codec;
+ media_log_->SetBooleanProperty("found_audio_stream", true);
+
+ SampleFormat sample_format = audio_config.sample_format();
+ std::string sample_name = SampleFormatToString(sample_format);
+
+ media_log_->SetStringProperty("audio_sample_format", sample_name);
+
+ media_log_->SetStringProperty("audio_codec_name",
+ audio_codec->codec_name);
+ media_log_->SetIntegerProperty("audio_sample_rate",
+ audio_codec->sample_rate);
+ media_log_->SetIntegerProperty("audio_channels_count",
+ audio_codec->channels);
+ media_log_->SetIntegerProperty("audio_samples_per_second",
+ audio_config.samples_per_second());
+ } else {
+ media_log_->SetBooleanProperty("found_audio_stream", false);
+ }
+
+ // Video logging
+ if (video_stream) {
+ AVCodecContext* video_codec = video_stream->codec;
+ media_log_->SetBooleanProperty("found_video_stream", true);
+ media_log_->SetStringProperty("video_codec_name", video_codec->codec_name);
+ media_log_->SetIntegerProperty("width", video_codec->width);
+ media_log_->SetIntegerProperty("height", video_codec->height);
+ media_log_->SetIntegerProperty("coded_width",
+ video_codec->coded_width);
+ media_log_->SetIntegerProperty("coded_height",
+ video_codec->coded_height);
+ media_log_->SetStringProperty(
+ "time_base",
+ base::StringPrintf("%d/%d",
+ video_codec->time_base.num,
+ video_codec->time_base.den));
+ media_log_->SetStringProperty(
+ "video_format", VideoFrame::FormatToString(video_config.format()));
+ media_log_->SetBooleanProperty("video_is_encrypted",
+ video_config.is_encrypted());
+ } else {
+ media_log_->SetBooleanProperty("found_video_stream", false);
+ }
+
+
+ media_log_->SetDoubleProperty("max_duration", max_duration.InSecondsF());
+ media_log_->SetDoubleProperty("start_time", start_time_.InSecondsF());
+ media_log_->SetDoubleProperty("filesize_in_bytes",
+ static_cast<double>(filesize_in_bytes));
+ media_log_->SetIntegerProperty("bitrate", bitrate_);
+
status_cb.Run(PIPELINE_OK);
}