[Cronet] Delay StartNetLog and StopNetLog until native request context is initialized
[chromium-blink-merge.git] / media / filters / ffmpeg_demuxer.cc
blobbe31709b05017603df7c655e6e06e417eaef997e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/ffmpeg_demuxer.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/sys_byteorder.h"
21 #include "base/task_runner_util.h"
22 #include "base/time/time.h"
23 #include "media/base/audio_decoder_config.h"
24 #include "media/base/bind_to_current_loop.h"
25 #include "media/base/decoder_buffer.h"
26 #include "media/base/decrypt_config.h"
27 #include "media/base/limits.h"
28 #include "media/base/media_log.h"
29 #include "media/base/video_decoder_config.h"
30 #include "media/ffmpeg/ffmpeg_common.h"
31 #include "media/filters/ffmpeg_aac_bitstream_converter.h"
32 #include "media/filters/ffmpeg_bitstream_converter.h"
33 #include "media/filters/ffmpeg_glue.h"
34 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h"
35 #include "media/filters/webvtt_util.h"
36 #include "media/formats/webm/webm_crypto_helpers.h"
38 namespace media {
40 static base::Time ExtractTimelineOffset(AVFormatContext* format_context) {
41 if (strstr(format_context->iformat->name, "webm") ||
42 strstr(format_context->iformat->name, "matroska")) {
43 const AVDictionaryEntry* entry =
44 av_dict_get(format_context->metadata, "creation_time", NULL, 0);
46 base::Time timeline_offset;
47 if (entry != NULL && entry->value != NULL &&
48 FFmpegUTCDateToTime(entry->value, &timeline_offset)) {
49 return timeline_offset;
53 return base::Time();
56 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) {
57 return base::TimeDelta::FromMicroseconds(
58 frames * base::Time::kMicrosecondsPerSecond / sample_rate);
61 static base::TimeDelta ExtractStartTime(AVStream* stream,
62 base::TimeDelta start_time_estimate) {
63 DCHECK(start_time_estimate != kNoTimestamp());
64 if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) {
65 return start_time_estimate == kInfiniteDuration() ? kNoTimestamp()
66 : start_time_estimate;
69 // First try the lower of the estimate and the |start_time| value.
70 base::TimeDelta start_time =
71 std::min(ConvertFromTimeBase(stream->time_base, stream->start_time),
72 start_time_estimate);
74 // Next see if the first buffered pts value is usable.
75 if (stream->pts_buffer[0] != static_cast<int64_t>(AV_NOPTS_VALUE)) {
76 const base::TimeDelta buffered_pts =
77 ConvertFromTimeBase(stream->time_base, stream->pts_buffer[0]);
78 if (buffered_pts < start_time)
79 start_time = buffered_pts;
82 // NOTE: Do not use AVStream->first_dts since |start_time| should be a
83 // presentation timestamp.
84 return start_time;
88 // FFmpegDemuxerStream
90 FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer,
91 AVStream* stream)
92 : demuxer_(demuxer),
93 task_runner_(base::MessageLoopProxy::current()),
94 stream_(stream),
95 type_(UNKNOWN),
96 liveness_(LIVENESS_UNKNOWN),
97 end_of_stream_(false),
98 last_packet_timestamp_(kNoTimestamp()),
99 last_packet_duration_(kNoTimestamp()),
100 video_rotation_(VIDEO_ROTATION_0),
101 fixup_negative_ogg_timestamps_(false) {
102 DCHECK(demuxer_);
104 bool is_encrypted = false;
105 int rotation = 0;
106 AVDictionaryEntry* rotation_entry = NULL;
108 // Determine our media format.
109 switch (stream->codec->codec_type) {
110 case AVMEDIA_TYPE_AUDIO:
111 type_ = AUDIO;
112 AVStreamToAudioDecoderConfig(stream, &audio_config_, true);
113 is_encrypted = audio_config_.is_encrypted();
114 break;
115 case AVMEDIA_TYPE_VIDEO:
116 type_ = VIDEO;
117 AVStreamToVideoDecoderConfig(stream, &video_config_, true);
118 is_encrypted = video_config_.is_encrypted();
120 rotation_entry = av_dict_get(stream->metadata, "rotate", NULL, 0);
121 if (rotation_entry && rotation_entry->value && rotation_entry->value[0])
122 base::StringToInt(rotation_entry->value, &rotation);
124 switch (rotation) {
125 case 0:
126 break;
127 case 90:
128 video_rotation_ = VIDEO_ROTATION_90;
129 break;
130 case 180:
131 video_rotation_ = VIDEO_ROTATION_180;
132 break;
133 case 270:
134 video_rotation_ = VIDEO_ROTATION_270;
135 break;
136 default:
137 LOG(ERROR) << "Unsupported video rotation metadata: " << rotation;
138 break;
141 break;
142 case AVMEDIA_TYPE_SUBTITLE:
143 type_ = TEXT;
144 break;
145 default:
146 NOTREACHED();
147 break;
150 // Calculate the duration.
151 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration);
153 if (is_encrypted) {
154 AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL,
156 DCHECK(key);
157 DCHECK(key->value);
158 if (!key || !key->value)
159 return;
160 base::StringPiece base64_key_id(key->value);
161 std::string enc_key_id;
162 base::Base64Decode(base64_key_id, &enc_key_id);
163 DCHECK(!enc_key_id.empty());
164 if (enc_key_id.empty())
165 return;
167 encryption_key_id_.assign(enc_key_id);
168 demuxer_->OnEncryptedMediaInitData(kWebMInitDataType, enc_key_id);
172 FFmpegDemuxerStream::~FFmpegDemuxerStream() {
173 DCHECK(!demuxer_);
174 DCHECK(read_cb_.is_null());
175 DCHECK(buffer_queue_.IsEmpty());
178 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
179 DCHECK(task_runner_->BelongsToCurrentThread());
181 if (!demuxer_ || end_of_stream_) {
182 NOTREACHED() << "Attempted to enqueue packet on a stopped stream";
183 return;
186 #if defined(USE_PROPRIETARY_CODECS)
187 // Convert the packet if there is a bitstream filter.
188 if (packet->data && bitstream_converter_ &&
189 !bitstream_converter_->ConvertPacket(packet.get())) {
190 LOG(ERROR) << "Format conversion failed.";
192 #endif
194 // Get side data if any. For now, the only type of side_data is VP8 Alpha. We
195 // keep this generic so that other side_data types in the future can be
196 // handled the same way as well.
197 av_packet_split_side_data(packet.get());
199 scoped_refptr<DecoderBuffer> buffer;
201 if (type() == DemuxerStream::TEXT) {
202 int id_size = 0;
203 uint8* id_data = av_packet_get_side_data(
204 packet.get(),
205 AV_PKT_DATA_WEBVTT_IDENTIFIER,
206 &id_size);
208 int settings_size = 0;
209 uint8* settings_data = av_packet_get_side_data(
210 packet.get(),
211 AV_PKT_DATA_WEBVTT_SETTINGS,
212 &settings_size);
214 std::vector<uint8> side_data;
215 MakeSideData(id_data, id_data + id_size,
216 settings_data, settings_data + settings_size,
217 &side_data);
219 buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size,
220 side_data.data(), side_data.size());
221 } else {
222 int side_data_size = 0;
223 uint8* side_data = av_packet_get_side_data(
224 packet.get(),
225 AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
226 &side_data_size);
228 scoped_ptr<DecryptConfig> decrypt_config;
229 int data_offset = 0;
230 if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
231 (type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
232 if (!WebMCreateDecryptConfig(
233 packet->data, packet->size,
234 reinterpret_cast<const uint8*>(encryption_key_id_.data()),
235 encryption_key_id_.size(),
236 &decrypt_config,
237 &data_offset)) {
238 LOG(ERROR) << "Creation of DecryptConfig failed.";
242 // If a packet is returned by FFmpeg's av_parser_parse2() the packet will
243 // reference inner memory of FFmpeg. As such we should transfer the packet
244 // into memory we control.
245 if (side_data_size > 0) {
246 buffer = DecoderBuffer::CopyFrom(packet.get()->data + data_offset,
247 packet.get()->size - data_offset,
248 side_data, side_data_size);
249 } else {
250 buffer = DecoderBuffer::CopyFrom(packet.get()->data + data_offset,
251 packet.get()->size - data_offset);
254 int skip_samples_size = 0;
255 const uint32* skip_samples_ptr =
256 reinterpret_cast<const uint32*>(av_packet_get_side_data(
257 packet.get(), AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size));
258 const int kSkipSamplesValidSize = 10;
259 const int kSkipEndSamplesOffset = 1;
260 if (skip_samples_size >= kSkipSamplesValidSize) {
261 // Because FFmpeg rolls codec delay and skip samples into one we can only
262 // allow front discard padding on the first buffer. Otherwise the discard
263 // helper can't figure out which data to discard. See AudioDiscardHelper.
264 int discard_front_samples = base::ByteSwapToLE32(*skip_samples_ptr);
265 if (last_packet_timestamp_ != kNoTimestamp() && discard_front_samples) {
266 DLOG(ERROR) << "Skip samples are only allowed for the first packet.";
267 discard_front_samples = 0;
270 const int discard_end_samples =
271 base::ByteSwapToLE32(*(skip_samples_ptr + kSkipEndSamplesOffset));
272 const int samples_per_second =
273 audio_decoder_config().samples_per_second();
274 buffer->set_discard_padding(std::make_pair(
275 FramesToTimeDelta(discard_front_samples, samples_per_second),
276 FramesToTimeDelta(discard_end_samples, samples_per_second)));
279 if (decrypt_config)
280 buffer->set_decrypt_config(decrypt_config.Pass());
283 if (packet->duration >= 0) {
284 buffer->set_duration(
285 ConvertStreamTimestamp(stream_->time_base, packet->duration));
286 } else {
287 // TODO(wolenetz): Remove when FFmpeg stops returning negative durations.
288 // https://crbug.com/394418
289 DVLOG(1) << "FFmpeg returned a buffer with a negative duration! "
290 << packet->duration;
291 buffer->set_duration(kNoTimestamp());
294 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp().
295 const base::TimeDelta stream_timestamp =
296 ConvertStreamTimestamp(stream_->time_base, packet->pts);
298 if (stream_timestamp != kNoTimestamp()) {
299 const bool is_audio = type() == AUDIO;
301 // If this is an OGG file with negative timestamps don't rebase any other
302 // stream types against the negative starting time.
303 base::TimeDelta start_time = demuxer_->start_time();
304 if (fixup_negative_ogg_timestamps_ && !is_audio &&
305 start_time < base::TimeDelta()) {
306 start_time = base::TimeDelta();
309 // Don't rebase timestamps for positive start times, the HTML Media Spec
310 // details this in section "4.8.10.6 Offsets into the media resource." We
311 // will still need to rebase timestamps before seeking with FFmpeg though.
312 if (start_time > base::TimeDelta())
313 start_time = base::TimeDelta();
315 buffer->set_timestamp(stream_timestamp - start_time);
317 // If enabled, mark audio packets with negative timestamps for post-decode
318 // discard.
319 if (fixup_negative_ogg_timestamps_ && is_audio &&
320 stream_timestamp < base::TimeDelta() &&
321 buffer->duration() != kNoTimestamp()) {
322 if (stream_timestamp + buffer->duration() < base::TimeDelta()) {
323 // Discard the entire packet if it's entirely before zero.
324 buffer->set_discard_padding(
325 std::make_pair(kInfiniteDuration(), base::TimeDelta()));
326 } else {
327 // Only discard part of the frame if it overlaps zero.
328 buffer->set_discard_padding(
329 std::make_pair(-stream_timestamp, base::TimeDelta()));
332 } else {
333 // If this happens on the first packet, decoders will throw an error.
334 buffer->set_timestamp(kNoTimestamp());
337 if (last_packet_timestamp_ != kNoTimestamp()) {
338 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing
339 // continuity across links in the chain it uses the timestamp information
340 // from each link directly. Doing so can lead to timestamps which appear to
341 // go backwards in time.
343 // If the new link starts with a negative timestamp or a timestamp less than
344 // the original (positive) |start_time|, we will get a negative timestamp
345 // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not
346 // able to work out a timestamp using the previous link and the next.
348 // Fixing chained ogg is non-trivial, so for now just reuse the last good
349 // timestamp. The decoder will rewrite the timestamps to be sample accurate
350 // later. See http://crbug.com/396864.
351 if (fixup_negative_ogg_timestamps_ &&
352 (buffer->timestamp() == kNoTimestamp() ||
353 buffer->timestamp() < last_packet_timestamp_)) {
354 buffer->set_timestamp(last_packet_timestamp_ +
355 (last_packet_duration_ != kNoTimestamp()
356 ? last_packet_duration_
357 : base::TimeDelta::FromMicroseconds(1)));
360 // The demuxer should always output positive timestamps.
361 DCHECK(buffer->timestamp() >= base::TimeDelta());
362 DCHECK(buffer->timestamp() != kNoTimestamp());
364 if (last_packet_timestamp_ < buffer->timestamp()) {
365 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp());
366 demuxer_->NotifyBufferingChanged();
370 if (packet.get()->flags & AV_PKT_FLAG_KEY)
371 buffer->set_is_key_frame(true);
373 last_packet_timestamp_ = buffer->timestamp();
374 last_packet_duration_ = buffer->duration();
376 buffer_queue_.Push(buffer);
377 SatisfyPendingRead();
380 void FFmpegDemuxerStream::SetEndOfStream() {
381 DCHECK(task_runner_->BelongsToCurrentThread());
382 end_of_stream_ = true;
383 SatisfyPendingRead();
386 void FFmpegDemuxerStream::FlushBuffers() {
387 DCHECK(task_runner_->BelongsToCurrentThread());
388 DCHECK(read_cb_.is_null()) << "There should be no pending read";
390 // H264 and AAC require that we resend the header after flush.
391 // Reset bitstream for converter to do so.
392 // This is related to chromium issue 140371 (http://crbug.com/140371).
393 ResetBitstreamConverter();
395 buffer_queue_.Clear();
396 end_of_stream_ = false;
397 last_packet_timestamp_ = kNoTimestamp();
398 last_packet_duration_ = kNoTimestamp();
401 void FFmpegDemuxerStream::Stop() {
402 DCHECK(task_runner_->BelongsToCurrentThread());
403 buffer_queue_.Clear();
404 if (!read_cb_.is_null()) {
405 base::ResetAndReturn(&read_cb_).Run(
406 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
408 demuxer_ = NULL;
409 stream_ = NULL;
410 end_of_stream_ = true;
413 DemuxerStream::Type FFmpegDemuxerStream::type() const {
414 DCHECK(task_runner_->BelongsToCurrentThread());
415 return type_;
418 DemuxerStream::Liveness FFmpegDemuxerStream::liveness() const {
419 DCHECK(task_runner_->BelongsToCurrentThread());
420 return liveness_;
423 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) {
424 DCHECK(task_runner_->BelongsToCurrentThread());
425 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported";
426 read_cb_ = BindToCurrentLoop(read_cb);
428 // Don't accept any additional reads if we've been told to stop.
429 // The |demuxer_| may have been destroyed in the pipeline thread.
431 // TODO(scherkus): it would be cleaner to reply with an error message.
432 if (!demuxer_) {
433 base::ResetAndReturn(&read_cb_).Run(
434 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
435 return;
438 SatisfyPendingRead();
441 void FFmpegDemuxerStream::EnableBitstreamConverter() {
442 DCHECK(task_runner_->BelongsToCurrentThread());
444 #if defined(USE_PROPRIETARY_CODECS)
445 InitBitstreamConverter();
446 #else
447 NOTREACHED() << "Proprietary codecs not enabled.";
448 #endif
451 void FFmpegDemuxerStream::ResetBitstreamConverter() {
452 #if defined(USE_PROPRIETARY_CODECS)
453 if (bitstream_converter_)
454 InitBitstreamConverter();
455 #endif // defined(USE_PROPRIETARY_CODECS)
458 void FFmpegDemuxerStream::InitBitstreamConverter() {
459 #if defined(USE_PROPRIETARY_CODECS)
460 if (stream_->codec->codec_id == AV_CODEC_ID_H264) {
461 bitstream_converter_.reset(
462 new FFmpegH264ToAnnexBBitstreamConverter(stream_->codec));
463 } else if (stream_->codec->codec_id == AV_CODEC_ID_AAC) {
464 bitstream_converter_.reset(
465 new FFmpegAACBitstreamConverter(stream_->codec));
467 #endif // defined(USE_PROPRIETARY_CODECS)
470 bool FFmpegDemuxerStream::SupportsConfigChanges() { return false; }
472 AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() {
473 DCHECK(task_runner_->BelongsToCurrentThread());
474 CHECK_EQ(type_, AUDIO);
475 return audio_config_;
478 VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() {
479 DCHECK(task_runner_->BelongsToCurrentThread());
480 CHECK_EQ(type_, VIDEO);
481 return video_config_;
484 VideoRotation FFmpegDemuxerStream::video_rotation() {
485 return video_rotation_;
488 void FFmpegDemuxerStream::SetLiveness(Liveness liveness) {
489 DCHECK(task_runner_->BelongsToCurrentThread());
490 DCHECK_EQ(liveness_, LIVENESS_UNKNOWN);
491 liveness_ = liveness;
494 base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const {
495 return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts);
498 Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
499 return buffered_ranges_;
502 void FFmpegDemuxerStream::SatisfyPendingRead() {
503 DCHECK(task_runner_->BelongsToCurrentThread());
504 if (!read_cb_.is_null()) {
505 if (!buffer_queue_.IsEmpty()) {
506 base::ResetAndReturn(&read_cb_).Run(
507 DemuxerStream::kOk, buffer_queue_.Pop());
508 } else if (end_of_stream_) {
509 base::ResetAndReturn(&read_cb_).Run(
510 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
514 // Have capacity? Ask for more!
515 if (HasAvailableCapacity() && !end_of_stream_) {
516 demuxer_->NotifyCapacityAvailable();
520 bool FFmpegDemuxerStream::HasAvailableCapacity() {
521 // TODO(scherkus): Remove this return and reenable time-based capacity
522 // after our data sources support canceling/concurrent reads, see
523 // http://crbug.com/165762 for details.
524 #if 1
525 return !read_cb_.is_null();
526 #else
527 // Try to have one second's worth of encoded data per stream.
528 const base::TimeDelta kCapacity = base::TimeDelta::FromSeconds(1);
529 return buffer_queue_.IsEmpty() || buffer_queue_.Duration() < kCapacity;
530 #endif
533 size_t FFmpegDemuxerStream::MemoryUsage() const {
534 return buffer_queue_.data_size();
537 TextKind FFmpegDemuxerStream::GetTextKind() const {
538 DCHECK_EQ(type_, DemuxerStream::TEXT);
540 if (stream_->disposition & AV_DISPOSITION_CAPTIONS)
541 return kTextCaptions;
543 if (stream_->disposition & AV_DISPOSITION_DESCRIPTIONS)
544 return kTextDescriptions;
546 if (stream_->disposition & AV_DISPOSITION_METADATA)
547 return kTextMetadata;
549 return kTextSubtitles;
552 std::string FFmpegDemuxerStream::GetMetadata(const char* key) const {
553 const AVDictionaryEntry* entry =
554 av_dict_get(stream_->metadata, key, NULL, 0);
555 return (entry == NULL || entry->value == NULL) ? "" : entry->value;
558 // static
559 base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp(
560 const AVRational& time_base, int64 timestamp) {
561 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE))
562 return kNoTimestamp();
564 return ConvertFromTimeBase(time_base, timestamp);
568 // FFmpegDemuxer
570 FFmpegDemuxer::FFmpegDemuxer(
571 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
572 DataSource* data_source,
573 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
574 const scoped_refptr<MediaLog>& media_log)
575 : host_(NULL),
576 task_runner_(task_runner),
577 blocking_thread_("FFmpegDemuxer"),
578 pending_read_(false),
579 pending_seek_(false),
580 data_source_(data_source),
581 media_log_(media_log),
582 bitrate_(0),
583 start_time_(kNoTimestamp()),
584 preferred_stream_for_seeking_(-1, kNoTimestamp()),
585 fallback_stream_for_seeking_(-1, kNoTimestamp()),
586 text_enabled_(false),
587 duration_known_(false),
588 encrypted_media_init_data_cb_(encrypted_media_init_data_cb),
589 weak_factory_(this) {
590 DCHECK(task_runner_.get());
591 DCHECK(data_source_);
594 FFmpegDemuxer::~FFmpegDemuxer() {}
596 void FFmpegDemuxer::Stop() {
597 DCHECK(task_runner_->BelongsToCurrentThread());
599 // The order of Stop() and Abort() is important here. If Abort() is called
600 // first, control may pass into FFmpeg where it can destruct buffers that are
601 // in the process of being fulfilled by the DataSource.
602 data_source_->Stop();
603 url_protocol_->Abort();
605 // This will block until all tasks complete. Note that after this returns it's
606 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this
607 // thread. Each of the reply task methods must check whether we've stopped the
608 // thread and drop their results on the floor.
609 blocking_thread_.Stop();
611 StreamVector::iterator iter;
612 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
613 if (*iter)
614 (*iter)->Stop();
617 data_source_ = NULL;
620 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
621 DCHECK(task_runner_->BelongsToCurrentThread());
622 CHECK(!pending_seek_);
624 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
625 // otherwise we can end up waiting for a pre-seek read to complete even though
626 // we know we're going to drop it on the floor.
628 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
629 // Since EnqueuePacket() rebased negative timestamps by the start time, we
630 // must correct the shift here.
632 // Additionally, to workaround limitations in how we expose seekable ranges to
633 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
634 // start time to the start time.
635 const base::TimeDelta seek_time =
636 start_time_ < base::TimeDelta() ? time + start_time_
637 : time < start_time_ ? start_time_ : time;
639 // Choose the seeking stream based on whether it contains the seek time, if no
640 // match can be found prefer the preferred stream.
642 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a
643 // given container will demux all packets after the seek point. Instead it
644 // only guarantees that all packets after the file position of the seek will
645 // be demuxed. It's an open question whether FFmpeg should fix this:
646 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
647 // Tracked by http://crbug.com/387996.
648 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp());
649 const int stream_index =
650 seek_time < preferred_stream_for_seeking_.second &&
651 seek_time >= fallback_stream_for_seeking_.second
652 ? fallback_stream_for_seeking_.first
653 : preferred_stream_for_seeking_.first;
654 DCHECK_NE(stream_index, -1);
656 const AVStream* seeking_stream =
657 glue_->format_context()->streams[stream_index];
659 pending_seek_ = true;
660 base::PostTaskAndReplyWithResult(
661 blocking_thread_.message_loop_proxy().get(),
662 FROM_HERE,
663 base::Bind(&av_seek_frame,
664 glue_->format_context(),
665 seeking_stream->index,
666 ConvertToTimeBase(seeking_stream->time_base, seek_time),
667 // Always seek to a timestamp <= to the desired timestamp.
668 AVSEEK_FLAG_BACKWARD),
669 base::Bind(
670 &FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb));
673 void FFmpegDemuxer::Initialize(DemuxerHost* host,
674 const PipelineStatusCB& status_cb,
675 bool enable_text_tracks) {
676 DCHECK(task_runner_->BelongsToCurrentThread());
677 host_ = host;
678 text_enabled_ = enable_text_tracks;
680 url_protocol_.reset(new BlockingUrlProtocol(data_source_, BindToCurrentLoop(
681 base::Bind(&FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))));
682 glue_.reset(new FFmpegGlue(url_protocol_.get()));
683 AVFormatContext* format_context = glue_->format_context();
685 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
686 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
687 // available, so add a metadata entry to ensure some is always present.
688 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0);
690 // Ensure ffmpeg doesn't give up too early while looking for stream params;
691 // this does not increase the amount of data downloaded. The default value
692 // is 5 AV_TIME_BASE units (1 second each), which prevents some oddly muxed
693 // streams from being detected properly; this value was chosen arbitrarily.
694 format_context->max_analyze_duration2 = 60 * AV_TIME_BASE;
696 // Open the AVFormatContext using our glue layer.
697 CHECK(blocking_thread_.Start());
698 base::PostTaskAndReplyWithResult(
699 blocking_thread_.message_loop_proxy().get(),
700 FROM_HERE,
701 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())),
702 base::Bind(&FFmpegDemuxer::OnOpenContextDone,
703 weak_factory_.GetWeakPtr(),
704 status_cb));
707 base::Time FFmpegDemuxer::GetTimelineOffset() const {
708 return timeline_offset_;
711 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) {
712 DCHECK(task_runner_->BelongsToCurrentThread());
713 return GetFFmpegStream(type);
716 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream(
717 DemuxerStream::Type type) const {
718 StreamVector::const_iterator iter;
719 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
720 if (*iter && (*iter)->type() == type) {
721 return *iter;
724 return NULL;
727 base::TimeDelta FFmpegDemuxer::GetStartTime() const {
728 return std::max(start_time_, base::TimeDelta());
731 void FFmpegDemuxer::AddTextStreams() {
732 DCHECK(task_runner_->BelongsToCurrentThread());
734 for (StreamVector::size_type idx = 0; idx < streams_.size(); ++idx) {
735 FFmpegDemuxerStream* stream = streams_[idx];
736 if (stream == NULL || stream->type() != DemuxerStream::TEXT)
737 continue;
739 TextKind kind = stream->GetTextKind();
740 std::string title = stream->GetMetadata("title");
741 std::string language = stream->GetMetadata("language");
743 // TODO: Implement "id" metadata in FFMPEG.
744 // See: http://crbug.com/323183
745 host_->AddTextStream(stream, TextTrackConfig(kind, title, language,
746 std::string()));
750 // Helper for calculating the bitrate of the media based on information stored
751 // in |format_context| or failing that the size and duration of the media.
753 // Returns 0 if a bitrate could not be determined.
754 static int CalculateBitrate(
755 AVFormatContext* format_context,
756 const base::TimeDelta& duration,
757 int64 filesize_in_bytes) {
758 // If there is a bitrate set on the container, use it.
759 if (format_context->bit_rate > 0)
760 return format_context->bit_rate;
762 // Then try to sum the bitrates individually per stream.
763 int bitrate = 0;
764 for (size_t i = 0; i < format_context->nb_streams; ++i) {
765 AVCodecContext* codec_context = format_context->streams[i]->codec;
766 bitrate += codec_context->bit_rate;
768 if (bitrate > 0)
769 return bitrate;
771 // See if we can approximate the bitrate as long as we have a filesize and
772 // valid duration.
773 if (duration.InMicroseconds() <= 0 ||
774 duration == kInfiniteDuration() ||
775 filesize_in_bytes == 0) {
776 return 0;
779 // Do math in floating point as we'd overflow an int64 if the filesize was
780 // larger than ~1073GB.
781 double bytes = filesize_in_bytes;
782 double duration_us = duration.InMicroseconds();
783 return bytes * 8000000.0 / duration_us;
786 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb,
787 bool result) {
788 DCHECK(task_runner_->BelongsToCurrentThread());
789 if (!blocking_thread_.IsRunning()) {
790 status_cb.Run(PIPELINE_ERROR_ABORT);
791 return;
794 if (!result) {
795 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN);
796 return;
799 // Fully initialize AVFormatContext by parsing the stream a little.
800 base::PostTaskAndReplyWithResult(
801 blocking_thread_.message_loop_proxy().get(),
802 FROM_HERE,
803 base::Bind(&avformat_find_stream_info,
804 glue_->format_context(),
805 static_cast<AVDictionary**>(NULL)),
806 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone,
807 weak_factory_.GetWeakPtr(),
808 status_cb));
811 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
812 int result) {
813 DCHECK(task_runner_->BelongsToCurrentThread());
814 if (!blocking_thread_.IsRunning() || !data_source_) {
815 status_cb.Run(PIPELINE_ERROR_ABORT);
816 return;
819 if (result < 0) {
820 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE);
821 return;
824 // Create demuxer stream entries for each possible AVStream. Each stream
825 // is examined to determine if it is supported or not (is the codec enabled
826 // for it in this release?). Unsupported streams are skipped, allowing for
827 // partial playback. At least one audio or video stream must be playable.
828 AVFormatContext* format_context = glue_->format_context();
829 streams_.resize(format_context->nb_streams);
831 // Estimate the start time for each stream by looking through the packets
832 // buffered during avformat_find_stream_info(). These values will be
833 // considered later when determining the actual stream start time.
835 // These packets haven't been completely processed yet, so only look through
836 // these values if the AVFormatContext has a valid start time.
838 // If no estimate is found, the stream entry will be kInfiniteDuration().
839 std::vector<base::TimeDelta> start_time_estimates(format_context->nb_streams,
840 kInfiniteDuration());
841 if (format_context->packet_buffer &&
842 format_context->start_time != static_cast<int64>(AV_NOPTS_VALUE)) {
843 struct AVPacketList* packet_buffer = format_context->packet_buffer;
844 while (packet_buffer != format_context->packet_buffer_end) {
845 DCHECK_LT(static_cast<size_t>(packet_buffer->pkt.stream_index),
846 start_time_estimates.size());
847 const AVStream* stream =
848 format_context->streams[packet_buffer->pkt.stream_index];
849 if (packet_buffer->pkt.pts != static_cast<int64>(AV_NOPTS_VALUE)) {
850 const base::TimeDelta packet_pts =
851 ConvertFromTimeBase(stream->time_base, packet_buffer->pkt.pts);
852 if (packet_pts < start_time_estimates[stream->index])
853 start_time_estimates[stream->index] = packet_pts;
855 packet_buffer = packet_buffer->next;
859 AVStream* audio_stream = NULL;
860 AudioDecoderConfig audio_config;
862 AVStream* video_stream = NULL;
863 VideoDecoderConfig video_config;
865 // If available, |start_time_| will be set to the lowest stream start time.
866 start_time_ = kInfiniteDuration();
868 base::TimeDelta max_duration;
869 for (size_t i = 0; i < format_context->nb_streams; ++i) {
870 AVStream* stream = format_context->streams[i];
871 const AVCodecContext* codec_context = stream->codec;
872 const AVMediaType codec_type = codec_context->codec_type;
874 if (codec_type == AVMEDIA_TYPE_AUDIO) {
875 if (audio_stream)
876 continue;
878 // Log the codec detected, whether it is supported or not.
879 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec",
880 codec_context->codec_id);
881 // Ensure the codec is supported. IsValidConfig() also checks that the
882 // channel layout and sample format are valid.
883 AVStreamToAudioDecoderConfig(stream, &audio_config, false);
884 if (!audio_config.IsValidConfig())
885 continue;
886 audio_stream = stream;
887 } else if (codec_type == AVMEDIA_TYPE_VIDEO) {
888 if (video_stream)
889 continue;
891 // Log the codec detected, whether it is supported or not.
892 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec",
893 codec_context->codec_id);
894 // Ensure the codec is supported. IsValidConfig() also checks that the
895 // frame size and visible size are valid.
896 AVStreamToVideoDecoderConfig(stream, &video_config, false);
898 if (!video_config.IsValidConfig())
899 continue;
900 video_stream = stream;
901 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) {
902 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) {
903 continue;
905 } else {
906 continue;
909 streams_[i] = new FFmpegDemuxerStream(this, stream);
910 max_duration = std::max(max_duration, streams_[i]->duration());
912 const base::TimeDelta start_time =
913 ExtractStartTime(stream, start_time_estimates[i]);
914 const bool has_start_time = start_time != kNoTimestamp();
916 // Always prefer the video stream for seeking. If none exists, we'll swap
917 // the fallback stream with the preferred stream below.
918 if (codec_type == AVMEDIA_TYPE_VIDEO) {
919 preferred_stream_for_seeking_ =
920 StreamSeekInfo(i, has_start_time ? start_time : base::TimeDelta());
923 if (!has_start_time)
924 continue;
926 if (start_time < start_time_) {
927 start_time_ = start_time;
929 // Choose the stream with the lowest starting time as the fallback stream
930 // for seeking. Video should always be preferred.
931 fallback_stream_for_seeking_ = StreamSeekInfo(i, start_time);
935 if (!audio_stream && !video_stream) {
936 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
937 return;
940 if (text_enabled_)
941 AddTextStreams();
943 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) {
944 // If there is a duration value in the container use that to find the
945 // maximum between it and the duration from A/V streams.
946 const AVRational av_time_base = {1, AV_TIME_BASE};
947 max_duration =
948 std::max(max_duration,
949 ConvertFromTimeBase(av_time_base, format_context->duration));
950 } else {
951 // The duration is unknown, in which case this is likely a live stream.
952 max_duration = kInfiniteDuration();
955 // Ogg has some peculiarities around negative timestamps, so use this flag to
956 // setup the FFmpegDemuxerStreams appropriately.
958 // Post-decode frame dropping for packets with negative timestamps is outlined
959 // in section A.2 in the Ogg Vorbis spec:
960 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
961 if (strcmp(format_context->iformat->name, "ogg") == 0 && audio_stream &&
962 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS) {
963 for (size_t i = 0; i < streams_.size(); ++i) {
964 if (streams_[i])
965 streams_[i]->enable_negative_timestamp_fixups_for_ogg();
968 // Fixup the seeking information to avoid selecting the audio stream simply
969 // because it has a lower starting time.
970 if (fallback_stream_for_seeking_.first == audio_stream->index &&
971 fallback_stream_for_seeking_.second < base::TimeDelta()) {
972 fallback_stream_for_seeking_.second = base::TimeDelta();
976 // If no start time could be determined, default to zero and prefer the video
977 // stream over the audio stream for seeking. E.g., The WAV demuxer does not
978 // put timestamps on its frames.
979 if (start_time_ == kInfiniteDuration()) {
980 start_time_ = base::TimeDelta();
981 preferred_stream_for_seeking_ = StreamSeekInfo(
982 video_stream ? video_stream->index : audio_stream->index, start_time_);
983 } else if (!video_stream) {
984 // If no video stream exists, use the audio or text stream found above.
985 preferred_stream_for_seeking_ = fallback_stream_for_seeking_;
988 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS
989 // generation so we always get timestamps, see http://crbug.com/169570
990 if (strcmp(format_context->iformat->name, "avi") == 0)
991 format_context->flags |= AVFMT_FLAG_GENPTS;
993 // For testing purposes, don't overwrite the timeline offset if set already.
994 if (timeline_offset_.is_null())
995 timeline_offset_ = ExtractTimelineOffset(format_context);
997 // Since we're shifting the externally visible start time to zero, we need to
998 // adjust the timeline offset to compensate.
999 if (!timeline_offset_.is_null() && start_time_ < base::TimeDelta())
1000 timeline_offset_ += start_time_;
1002 if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) {
1003 SetLiveness(DemuxerStream::LIVENESS_LIVE);
1004 } else if (max_duration != kInfiniteDuration()) {
1005 SetLiveness(DemuxerStream::LIVENESS_RECORDED);
1006 } else {
1007 SetLiveness(DemuxerStream::LIVENESS_UNKNOWN);
1010 // Good to go: set the duration and bitrate and notify we're done
1011 // initializing.
1012 host_->SetDuration(max_duration);
1013 duration_known_ = (max_duration != kInfiniteDuration());
1015 int64 filesize_in_bytes = 0;
1016 url_protocol_->GetSize(&filesize_in_bytes);
1017 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes);
1018 if (bitrate_ > 0)
1019 data_source_->SetBitrate(bitrate_);
1021 // Audio logging
1022 if (audio_stream) {
1023 AVCodecContext* audio_codec = audio_stream->codec;
1024 media_log_->SetBooleanProperty("found_audio_stream", true);
1026 SampleFormat sample_format = audio_config.sample_format();
1027 std::string sample_name = SampleFormatToString(sample_format);
1029 media_log_->SetStringProperty("audio_sample_format", sample_name);
1031 AVCodec* codec = avcodec_find_decoder(audio_codec->codec_id);
1032 if (codec) {
1033 media_log_->SetStringProperty("audio_codec_name", codec->name);
1036 media_log_->SetIntegerProperty("audio_channels_count",
1037 audio_codec->channels);
1038 media_log_->SetIntegerProperty("audio_samples_per_second",
1039 audio_config.samples_per_second());
1040 } else {
1041 media_log_->SetBooleanProperty("found_audio_stream", false);
1044 // Video logging
1045 if (video_stream) {
1046 AVCodecContext* video_codec = video_stream->codec;
1047 media_log_->SetBooleanProperty("found_video_stream", true);
1049 AVCodec* codec = avcodec_find_decoder(video_codec->codec_id);
1050 if (codec) {
1051 media_log_->SetStringProperty("video_codec_name", codec->name);
1052 } else if (video_codec->codec_id == AV_CODEC_ID_VP9) {
1053 // ffmpeg doesn't know about VP9 decoder. So we need to log it explicitly.
1054 media_log_->SetStringProperty("video_codec_name", "vp9");
1057 media_log_->SetIntegerProperty("width", video_codec->width);
1058 media_log_->SetIntegerProperty("height", video_codec->height);
1059 media_log_->SetIntegerProperty("coded_width",
1060 video_codec->coded_width);
1061 media_log_->SetIntegerProperty("coded_height",
1062 video_codec->coded_height);
1063 media_log_->SetStringProperty(
1064 "time_base",
1065 base::StringPrintf("%d/%d",
1066 video_codec->time_base.num,
1067 video_codec->time_base.den));
1068 media_log_->SetStringProperty(
1069 "video_format", VideoFrame::FormatToString(video_config.format()));
1070 media_log_->SetBooleanProperty("video_is_encrypted",
1071 video_config.is_encrypted());
1072 } else {
1073 media_log_->SetBooleanProperty("found_video_stream", false);
1076 media_log_->SetTimeProperty("max_duration", max_duration);
1077 media_log_->SetTimeProperty("start_time", start_time_);
1078 media_log_->SetIntegerProperty("bitrate", bitrate_);
1080 status_cb.Run(PIPELINE_OK);
1083 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) {
1084 DCHECK(task_runner_->BelongsToCurrentThread());
1085 CHECK(pending_seek_);
1086 pending_seek_ = false;
1088 if (!blocking_thread_.IsRunning()) {
1089 cb.Run(PIPELINE_ERROR_ABORT);
1090 return;
1093 if (result < 0) {
1094 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being
1095 // captured from stdout and contaminates testing.
1096 // TODO(scherkus): Implement this properly and signal error (BUG=23447).
1097 VLOG(1) << "Not implemented";
1100 // Tell streams to flush buffers due to seeking.
1101 StreamVector::iterator iter;
1102 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
1103 if (*iter)
1104 (*iter)->FlushBuffers();
1107 // Resume reading until capacity.
1108 ReadFrameIfNeeded();
1110 // Notify we're finished seeking.
1111 cb.Run(PIPELINE_OK);
1114 void FFmpegDemuxer::ReadFrameIfNeeded() {
1115 DCHECK(task_runner_->BelongsToCurrentThread());
1117 // Make sure we have work to do before reading.
1118 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() ||
1119 pending_read_ || pending_seek_) {
1120 return;
1123 // Allocate and read an AVPacket from the media. Save |packet_ptr| since
1124 // evaluation order of packet.get() and base::Passed(&packet) is
1125 // undefined.
1126 ScopedAVPacket packet(new AVPacket());
1127 AVPacket* packet_ptr = packet.get();
1129 pending_read_ = true;
1130 base::PostTaskAndReplyWithResult(
1131 blocking_thread_.message_loop_proxy().get(),
1132 FROM_HERE,
1133 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr),
1134 base::Bind(&FFmpegDemuxer::OnReadFrameDone,
1135 weak_factory_.GetWeakPtr(),
1136 base::Passed(&packet)));
1139 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) {
1140 DCHECK(task_runner_->BelongsToCurrentThread());
1141 DCHECK(pending_read_);
1142 pending_read_ = false;
1144 if (!blocking_thread_.IsRunning() || pending_seek_) {
1145 return;
1148 // Consider the stream as ended if:
1149 // - either underlying ffmpeg returned an error
1150 // - or FFMpegDemuxer reached the maximum allowed memory usage.
1151 if (result < 0 || IsMaxMemoryUsageReached()) {
1152 // Update the duration based on the highest elapsed time across all streams
1153 // if it was previously unknown.
1154 if (!duration_known_) {
1155 base::TimeDelta max_duration;
1157 for (StreamVector::iterator iter = streams_.begin();
1158 iter != streams_.end();
1159 ++iter) {
1160 if (!*iter)
1161 continue;
1163 base::TimeDelta duration = (*iter)->GetElapsedTime();
1164 if (duration != kNoTimestamp() && duration > max_duration)
1165 max_duration = duration;
1168 if (max_duration > base::TimeDelta()) {
1169 host_->SetDuration(max_duration);
1170 duration_known_ = true;
1173 // If we have reached the end of stream, tell the downstream filters about
1174 // the event.
1175 StreamHasEnded();
1176 return;
1179 // Queue the packet with the appropriate stream.
1180 DCHECK_GE(packet->stream_index, 0);
1181 DCHECK_LT(packet->stream_index, static_cast<int>(streams_.size()));
1183 // Defend against ffmpeg giving us a bad stream index.
1184 if (packet->stream_index >= 0 &&
1185 packet->stream_index < static_cast<int>(streams_.size()) &&
1186 streams_[packet->stream_index]) {
1187 // TODO(scherkus): Fix demuxing upstream to never return packets w/o data
1188 // when av_read_frame() returns success code. See bug comment for ideas:
1190 // https://code.google.com/p/chromium/issues/detail?id=169133#c10
1191 if (!packet->data) {
1192 ScopedAVPacket new_packet(new AVPacket());
1193 av_new_packet(new_packet.get(), 0);
1194 av_packet_copy_props(new_packet.get(), packet.get());
1195 packet.swap(new_packet);
1198 // Special case for opus in ogg. FFmpeg is pre-trimming the codec delay
1199 // from the packet timestamp. Chrome expects to handle this itself inside
1200 // the decoder, so shift timestamps by the delay in this case.
1201 // TODO(dalecurtis): Try to get fixed upstream. See http://crbug.com/328207
1202 if (strcmp(glue_->format_context()->iformat->name, "ogg") == 0) {
1203 const AVCodecContext* codec_context =
1204 glue_->format_context()->streams[packet->stream_index]->codec;
1205 if (codec_context->codec_id == AV_CODEC_ID_OPUS &&
1206 codec_context->delay > 0) {
1207 packet->pts += codec_context->delay;
1211 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
1212 demuxer_stream->EnqueuePacket(packet.Pass());
1215 // Keep reading until we've reached capacity.
1216 ReadFrameIfNeeded();
1219 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
1220 DCHECK(task_runner_->BelongsToCurrentThread());
1221 StreamVector::iterator iter;
1222 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
1223 if (*iter && (*iter)->HasAvailableCapacity()) {
1224 return true;
1227 return false;
1230 bool FFmpegDemuxer::IsMaxMemoryUsageReached() const {
1231 DCHECK(task_runner_->BelongsToCurrentThread());
1233 // Max allowed memory usage, all streams combined.
1234 const size_t kDemuxerMemoryLimit = 150 * 1024 * 1024;
1236 size_t memory_left = kDemuxerMemoryLimit;
1237 for (StreamVector::const_iterator iter = streams_.begin();
1238 iter != streams_.end(); ++iter) {
1239 if (!(*iter))
1240 continue;
1242 size_t stream_memory_usage = (*iter)->MemoryUsage();
1243 if (stream_memory_usage > memory_left)
1244 return true;
1245 memory_left -= stream_memory_usage;
1247 return false;
1250 void FFmpegDemuxer::StreamHasEnded() {
1251 DCHECK(task_runner_->BelongsToCurrentThread());
1252 StreamVector::iterator iter;
1253 for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
1254 if (!*iter)
1255 continue;
1256 (*iter)->SetEndOfStream();
1260 void FFmpegDemuxer::OnEncryptedMediaInitData(
1261 const std::string& init_data_type,
1262 const std::string& encryption_key_id) {
1263 std::vector<uint8> key_id_local(encryption_key_id.begin(),
1264 encryption_key_id.end());
1265 encrypted_media_init_data_cb_.Run(init_data_type, key_id_local);
1268 void FFmpegDemuxer::NotifyCapacityAvailable() {
1269 DCHECK(task_runner_->BelongsToCurrentThread());
1270 ReadFrameIfNeeded();
1273 void FFmpegDemuxer::NotifyBufferingChanged() {
1274 DCHECK(task_runner_->BelongsToCurrentThread());
1275 Ranges<base::TimeDelta> buffered;
1276 FFmpegDemuxerStream* audio = GetFFmpegStream(DemuxerStream::AUDIO);
1277 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO);
1278 if (audio && video) {
1279 buffered = audio->GetBufferedRanges().IntersectionWith(
1280 video->GetBufferedRanges());
1281 } else if (audio) {
1282 buffered = audio->GetBufferedRanges();
1283 } else if (video) {
1284 buffered = video->GetBufferedRanges();
1286 for (size_t i = 0; i < buffered.size(); ++i)
1287 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i));
1290 void FFmpegDemuxer::OnDataSourceError() {
1291 host_->OnDemuxerError(PIPELINE_ERROR_READ);
1294 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) {
1295 DCHECK(task_runner_->BelongsToCurrentThread());
1296 for (const auto& stream : streams_) { // |stream| is a ref to a pointer.
1297 if (stream)
1298 stream->SetLiveness(liveness);
1302 } // namespace media