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. |