Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1039)

Unified Diff: media/filters/ffmpeg_demuxer.cc

Issue 23702007: Render inband text tracks in the media pipeline (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/filters/ffmpeg_demuxer.cc
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 30cb0c0029857c8f19cf0b0d33dc75c143635163..7ff86ecb7bb80b71aa6dea81af0cd81ce3196930 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -64,6 +64,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;
@@ -110,36 +113,65 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
LOG(ERROR) << "Format conversion failed.";
}
- // Get side data if any. For now, the only type of side_data is VP8 Alpha. We
- // 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) {
+
+ // Get side data if any. For now, the only types of side_data are VP8 Alpha,
+ // and WebVTT id and settings. We 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());
+ 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);
+
+ // The DecoderBuffer only supports a single side data item. In the case of
+ // a WebVTT cue, we can have potentially two side data items. In order to
+ // avoid disrupting DecoderBuffer any more than we need to, we copy both
+ // side data items onto a single one, and separate them with a marker
+ // (the byte value 0xFF is not part of the representation of any UTF8
+ // character).
+ std::basic_string<uint8> side_data;
+ side_data.append(id_data, id_size);
+ side_data.append(1, 0xFF);
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 nit: Consider just using the null terminator and s
Matthew Heaney (Chromium) 2013/09/13 19:51:54 I appended a NUL to each sub-string, instead of us
+ side_data.append(settings_data, settings_size);
+
buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size,
- side_data, side_data_size);
+ side_data.data(), side_data.length());
} else {
- buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size);
- }
+ int side_data_size = 0;
+ uint8* side_data = av_packet_get_side_data(
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 Why is this change needed for non-texttrack data?
Matthew Heaney (Chromium) 2013/09/13 19:51:54 I'm not quite following here. What change are you
acolwell GONE FROM CHROMIUM 2013/09/13 20:57:30 IIUC This is the non-text track code path. The ori
Matthew Heaney (Chromium) 2013/09/20 23:53:54 I checked master again -- this bit of code was alr
+ 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);
+ }
- if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
- (type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
- scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
- packet->data, packet->size,
- reinterpret_cast<const uint8*>(encryption_key_id_.data()),
- encryption_key_id_.size()));
- if (!config)
- LOG(ERROR) << "Creation of DecryptConfig failed.";
- buffer->set_decrypt_config(config.Pass());
+ if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 nit: I don't think you need to move this into the
Matthew Heaney (Chromium) 2013/09/13 19:51:54 Done.
+ (type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
+ scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
+ packet->data, packet->size,
+ reinterpret_cast<const uint8*>(encryption_key_id_.data()),
+ encryption_key_id_.size()));
+ if (!config)
+ LOG(ERROR) << "Creation of DecryptConfig failed.";
+ buffer->set_decrypt_config(config.Pass());
+ }
}
buffer->set_timestamp(ConvertStreamTimestamp(
@@ -288,6 +320,7 @@ FFmpegDemuxer::FFmpegDemuxer(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
DataSource* data_source,
const NeedKeyCB& need_key_cb,
+ const FFmpegAddTextTrackCB& add_text_track_cb,
const scoped_refptr<MediaLog>& media_log)
: host_(NULL),
message_loop_(message_loop),
@@ -303,7 +336,8 @@ FFmpegDemuxer::FFmpegDemuxer(
duration_known_(false),
url_protocol_(data_source, BindToLoop(message_loop_, base::Bind(
&FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))),
- need_key_cb_(need_key_cb) {
+ need_key_cb_(need_key_cb),
+ add_text_track_cb_(add_text_track_cb) {
DCHECK(message_loop_.get());
DCHECK(data_source_);
}
@@ -393,6 +427,18 @@ DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) {
return GetFFmpegStream(type);
}
+int FFmpegDemuxer::GetStreamCount() const {
+ return streams_.size();
+}
+
+DemuxerStream* FFmpegDemuxer::GetStreamByIndex(int idx) {
+ if (idx < 0 || StreamVector::size_type(idx) >= streams_.size()) {
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 nit: These should be DCHECKs.
Matthew Heaney (Chromium) 2013/09/13 19:51:54 Done.
+ return NULL;
+ } else {
+ return streams_[idx];
+ }
+}
+
FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream(
DemuxerStream::Type type) const {
StreamVector::const_iterator iter;
@@ -494,6 +540,8 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
AVStream* video_stream = NULL;
VideoDecoderConfig video_config;
+ std::vector<AVStream*> text_streams(format_context->nb_streams);
+
base::TimeDelta max_duration;
for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVStream* stream = format_context->streams[i];
@@ -527,6 +575,13 @@ 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 ||
+ add_text_track_cb_.is_null()) {
+ continue;
+ }
+
+ text_streams[i] = stream;
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 nit: Any harm in doing the work below, right here?
Matthew Heaney (Chromium) 2013/09/13 19:51:54 We iterate through all the streams first, in order
acolwell GONE FROM CHROMIUM 2013/09/13 20:57:30 I don't think this should be a problem. Reads on t
Matthew Heaney (Chromium) 2013/09/20 23:53:54 I ended up moving that loop into its own subprogra
} else {
continue;
}
@@ -547,6 +602,44 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
return;
}
+ for (size_t idx = 0; idx < text_streams.size(); ++idx) {
+ AVStream* text_stream = text_streams[idx];
+ if (text_stream == NULL)
+ continue;
+
+ TextKind kind;
+
+ if (text_stream->disposition & AV_DISPOSITION_CAPTIONS) {
+ kind = kTextCaptions;
+ } else if (text_stream->disposition & AV_DISPOSITION_DESCRIPTIONS) {
+ kind = kTextDescriptions;
+ } else if (text_stream->disposition & AV_DISPOSITION_METADATA) {
+ kind = kTextMetadata;
+ } else {
+ kind = kTextSubtitles;
+ }
+
+ AVDictionaryEntry* text_title =
+ av_dict_get(text_stream->metadata, "title", NULL, 0);
+
+ std::string title;
+
+ if (text_title != NULL && text_title->value != NULL) {
acolwell GONE FROM CHROMIUM 2013/09/12 00:15:15 nit: move av_dict_get() & NULL checks to a helper
Matthew Heaney (Chromium) 2013/09/13 19:51:54 Done.
+ title = text_title->value;
+ }
+
+ AVDictionaryEntry* text_language =
+ av_dict_get(text_stream->metadata, "language", NULL, 0);
+
+ std::string language;
+
+ if (text_language != NULL && text_language->value != NULL) {
+ language = text_language->value;
+ }
+
+ add_text_track_cb_.Run(kind, title, language, idx);
+ }
+
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.

Powered by Google App Engine
This is Rietveld 408576698