summaryrefslogtreecommitdiff
path: root/chromium/media/filters/ffmpeg_demuxer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/filters/ffmpeg_demuxer.cc')
-rw-r--r--chromium/media/filters/ffmpeg_demuxer.cc144
1 files changed, 122 insertions, 22 deletions
diff --git a/chromium/media/filters/ffmpeg_demuxer.cc b/chromium/media/filters/ffmpeg_demuxer.cc
index 723eb5f28d9..6b8027164bd 100644
--- a/chromium/media/filters/ffmpeg_demuxer.cc
+++ b/chromium/media/filters/ffmpeg_demuxer.cc
@@ -11,13 +11,12 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
-#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/sparse_histogram.h"
-#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/sys_byteorder.h"
#include "base/task_runner_util.h"
#include "base/time/time.h"
#include "media/base/audio_decoder_config.h"
@@ -26,11 +25,11 @@
#include "media/base/decrypt_config.h"
#include "media/base/limits.h"
#include "media/base/media_log.h"
-#include "media/base/media_switches.h"
#include "media/base/video_decoder_config.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
#include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h"
+#include "media/filters/webvtt_util.h"
#include "media/webm/webm_crypto_helpers.h"
namespace media {
@@ -64,6 +63,9 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(
AVStreamToVideoDecoderConfig(stream, &video_config_, true);
is_encrypted = video_config_.is_encrypted();
break;
+ case AVMEDIA_TYPE_SUBTITLE:
+ type_ = TEXT;
+ break;
default:
NOTREACHED();
break;
@@ -114,27 +116,67 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
// keep this generic so that other side_data types in the future can be
// handled the same way as well.
av_packet_split_side_data(packet.get());
- int side_data_size = 0;
- uint8* side_data = av_packet_get_side_data(
- packet.get(),
- AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
- &side_data_size);
-
- // If a packet is returned by FFmpeg's av_parser_parse2() the packet will
- // reference inner memory of FFmpeg. As such we should transfer the packet
- // into memory we control.
scoped_refptr<DecoderBuffer> buffer;
- if (side_data_size > 0) {
+
+ if (type() == DemuxerStream::TEXT) {
+ int id_size = 0;
+ uint8* id_data = av_packet_get_side_data(
+ packet.get(),
+ AV_PKT_DATA_WEBVTT_IDENTIFIER,
+ &id_size);
+
+ int settings_size = 0;
+ uint8* settings_data = av_packet_get_side_data(
+ packet.get(),
+ AV_PKT_DATA_WEBVTT_SETTINGS,
+ &settings_size);
+
+ std::vector<uint8> side_data;
+ MakeSideData(id_data, id_data + id_size,
+ settings_data, settings_data + settings_size,
+ &side_data);
+
buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size,
- side_data, side_data_size);
+ side_data.data(), side_data.size());
} else {
- buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size);
+ int side_data_size = 0;
+ uint8* side_data = av_packet_get_side_data(
+ packet.get(),
+ AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
+ &side_data_size);
+
+ // If a packet is returned by FFmpeg's av_parser_parse2() the packet will
+ // reference inner memory of FFmpeg. As such we should transfer the packet
+ // into memory we control.
+ if (side_data_size > 0) {
+ buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size,
+ side_data, side_data_size);
+ } else {
+ buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size);
+ }
+
+ int skip_samples_size = 0;
+ uint8* skip_samples = av_packet_get_side_data(packet.get(),
+ AV_PKT_DATA_SKIP_SAMPLES,
+ &skip_samples_size);
+ const int kSkipSamplesValidSize = 10;
+ const int kSkipSamplesOffset = 4;
+ if (skip_samples_size >= kSkipSamplesValidSize) {
+ int discard_padding_samples = base::ByteSwapToLE32(
+ *(reinterpret_cast<const uint32*>(skip_samples +
+ kSkipSamplesOffset)));
+ // TODO(vigneshv): Change decoder buffer to use number of samples so that
+ // this conversion can be avoided.
+ buffer->set_discard_padding(base::TimeDelta::FromMicroseconds(
+ discard_padding_samples * 1000000.0 /
+ audio_decoder_config().samples_per_second()));
+ }
}
if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
(type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
- packet->data, packet->size,
+ packet->data, packet->size,
reinterpret_cast<const uint8*>(encryption_key_id_.data()),
encryption_key_id_.size()));
if (!config)
@@ -272,6 +314,27 @@ bool FFmpegDemuxerStream::HasAvailableCapacity() {
return buffer_queue_.IsEmpty() || buffer_queue_.Duration() < kCapacity;
}
+TextKind FFmpegDemuxerStream::GetTextKind() const {
+ DCHECK_EQ(type_, DemuxerStream::TEXT);
+
+ if (stream_->disposition & AV_DISPOSITION_CAPTIONS)
+ return kTextCaptions;
+
+ if (stream_->disposition & AV_DISPOSITION_DESCRIPTIONS)
+ return kTextDescriptions;
+
+ if (stream_->disposition & AV_DISPOSITION_METADATA)
+ return kTextMetadata;
+
+ return kTextSubtitles;
+}
+
+std::string FFmpegDemuxerStream::GetMetadata(const char* key) const {
+ const AVDictionaryEntry* entry =
+ av_dict_get(stream_->metadata, key, NULL, 0);
+ return (entry == NULL || entry->value == NULL) ? "" : entry->value;
+}
+
// static
base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp(
const AVRational& time_base, int64 timestamp) {
@@ -300,6 +363,7 @@ FFmpegDemuxer::FFmpegDemuxer(
bitrate_(0),
start_time_(kNoTimestamp()),
audio_disabled_(false),
+ text_enabled_(false),
duration_known_(false),
url_protocol_(data_source, BindToLoop(message_loop_, base::Bind(
&FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))),
@@ -345,11 +409,6 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb));
}
-void FFmpegDemuxer::SetPlaybackRate(float playback_rate) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- data_source_->SetPlaybackRate(playback_rate);
-}
-
void FFmpegDemuxer::OnAudioRendererDisabled() {
DCHECK(message_loop_->BelongsToCurrentThread());
audio_disabled_ = true;
@@ -362,10 +421,12 @@ void FFmpegDemuxer::OnAudioRendererDisabled() {
}
void FFmpegDemuxer::Initialize(DemuxerHost* host,
- const PipelineStatusCB& status_cb) {
+ const PipelineStatusCB& status_cb,
+ bool enable_text_tracks) {
DCHECK(message_loop_->BelongsToCurrentThread());
host_ = host;
weak_this_ = weak_factory_.GetWeakPtr();
+ text_enabled_ = enable_text_tracks;
// TODO(scherkus): DataSource should have a host by this point,
// see http://crbug.com/122071
@@ -409,6 +470,25 @@ base::TimeDelta FFmpegDemuxer::GetStartTime() const {
return start_time_;
}
+void FFmpegDemuxer::AddTextStreams() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ for (StreamVector::size_type idx = 0; idx < streams_.size(); ++idx) {
+ FFmpegDemuxerStream* stream = streams_[idx];
+ if (stream == NULL || stream->type() != DemuxerStream::TEXT)
+ continue;
+
+ TextKind kind = stream->GetTextKind();
+ std::string title = stream->GetMetadata("title");
+ std::string language = stream->GetMetadata("language");
+
+ // TODO: Implement "id" metadata in FFMPEG.
+ // See: http://crbug.com/323183
+ host_->AddTextStream(stream, TextTrackConfig(kind, title, language,
+ std::string()));
+ }
+}
+
// Helper for calculating the bitrate of the media based on information stored
// in |format_context| or failing that the size and duration of the media.
//
@@ -527,6 +607,10 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
if (!video_config.IsValidConfig())
continue;
video_stream = stream;
+ } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) {
+ continue;
+ }
} else {
continue;
}
@@ -547,6 +631,9 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
return;
}
+ if (text_enabled_)
+ AddTextStreams();
+
if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) {
// If there is a duration value in the container use that to find the
// maximum between it and the duration from A/V streams.
@@ -758,6 +845,19 @@ void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) {
packet.swap(new_packet);
}
+ // Special case for opus in ogg. FFmpeg is pre-trimming the codec delay
+ // from the packet timestamp. Chrome expects to handle this itself inside
+ // the decoder, so shift timestamps by the delay in this case.
+ // TODO(dalecurtis): Try to get fixed upstream. See http://crbug.com/328207
+ if (strcmp(glue_->format_context()->iformat->name, "ogg") == 0) {
+ const AVCodecContext* codec_context =
+ glue_->format_context()->streams[packet->stream_index]->codec;
+ if (codec_context->codec_id == AV_CODEC_ID_OPUS &&
+ codec_context->delay > 0) {
+ packet->pts += codec_context->delay;
+ }
+ }
+
FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
demuxer_stream->EnqueuePacket(packet.Pass());
}