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"
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/sys_byteorder.h"
19 #include "base/task_runner_util.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/time/time.h"
22 #include "media/base/bind_to_current_loop.h"
23 #include "media/base/decrypt_config.h"
24 #include "media/base/limits.h"
25 #include "media/base/media_log.h"
26 #include "media/ffmpeg/ffmpeg_common.h"
27 #include "media/filters/ffmpeg_aac_bitstream_converter.h"
28 #include "media/filters/ffmpeg_bitstream_converter.h"
29 #include "media/filters/ffmpeg_glue.h"
30 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h"
31 #include "media/filters/webvtt_util.h"
32 #include "media/formats/webm/webm_crypto_helpers.h"
36 static base::Time
ExtractTimelineOffset(AVFormatContext
* format_context
) {
37 if (strstr(format_context
->iformat
->name
, "webm") ||
38 strstr(format_context
->iformat
->name
, "matroska")) {
39 const AVDictionaryEntry
* entry
=
40 av_dict_get(format_context
->metadata
, "creation_time", NULL
, 0);
42 base::Time timeline_offset
;
43 if (entry
!= NULL
&& entry
->value
!= NULL
&&
44 FFmpegUTCDateToTime(entry
->value
, &timeline_offset
)) {
45 return timeline_offset
;
52 static base::TimeDelta
FramesToTimeDelta(int frames
, double sample_rate
) {
53 return base::TimeDelta::FromMicroseconds(
54 frames
* base::Time::kMicrosecondsPerSecond
/ sample_rate
);
57 static base::TimeDelta
ExtractStartTime(AVStream
* stream
,
58 base::TimeDelta start_time_estimate
) {
59 DCHECK(start_time_estimate
!= kNoTimestamp());
60 if (stream
->start_time
== static_cast<int64_t>(AV_NOPTS_VALUE
)) {
61 return start_time_estimate
== kInfiniteDuration() ? kNoTimestamp()
62 : start_time_estimate
;
65 // First try the lower of the estimate and the |start_time| value.
66 base::TimeDelta start_time
=
67 std::min(ConvertFromTimeBase(stream
->time_base
, stream
->start_time
),
70 // Next see if the first buffered pts value is usable.
71 if (stream
->pts_buffer
[0] != static_cast<int64_t>(AV_NOPTS_VALUE
)) {
72 const base::TimeDelta buffered_pts
=
73 ConvertFromTimeBase(stream
->time_base
, stream
->pts_buffer
[0]);
74 if (buffered_pts
< start_time
)
75 start_time
= buffered_pts
;
78 // NOTE: Do not use AVStream->first_dts since |start_time| should be a
79 // presentation timestamp.
84 // FFmpegDemuxerStream
86 FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer
* demuxer
,
89 task_runner_(base::ThreadTaskRunnerHandle::Get()),
92 liveness_(LIVENESS_UNKNOWN
),
93 end_of_stream_(false),
94 last_packet_timestamp_(kNoTimestamp()),
95 last_packet_duration_(kNoTimestamp()),
96 video_rotation_(VIDEO_ROTATION_0
),
97 fixup_negative_ogg_timestamps_(false) {
100 bool is_encrypted
= false;
102 AVDictionaryEntry
* rotation_entry
= NULL
;
104 // Determine our media format.
105 switch (stream
->codec
->codec_type
) {
106 case AVMEDIA_TYPE_AUDIO
:
108 AVStreamToAudioDecoderConfig(stream
, &audio_config_
, true);
109 is_encrypted
= audio_config_
.is_encrypted();
111 case AVMEDIA_TYPE_VIDEO
:
113 AVStreamToVideoDecoderConfig(stream
, &video_config_
, true);
114 is_encrypted
= video_config_
.is_encrypted();
116 rotation_entry
= av_dict_get(stream
->metadata
, "rotate", NULL
, 0);
117 if (rotation_entry
&& rotation_entry
->value
&& rotation_entry
->value
[0])
118 base::StringToInt(rotation_entry
->value
, &rotation
);
124 video_rotation_
= VIDEO_ROTATION_90
;
127 video_rotation_
= VIDEO_ROTATION_180
;
130 video_rotation_
= VIDEO_ROTATION_270
;
133 LOG(ERROR
) << "Unsupported video rotation metadata: " << rotation
;
138 case AVMEDIA_TYPE_SUBTITLE
:
146 // Calculate the duration.
147 duration_
= ConvertStreamTimestamp(stream
->time_base
, stream
->duration
);
150 AVDictionaryEntry
* key
= av_dict_get(stream
->metadata
, "enc_key_id", NULL
,
154 if (!key
|| !key
->value
)
156 base::StringPiece
base64_key_id(key
->value
);
157 std::string enc_key_id
;
158 base::Base64Decode(base64_key_id
, &enc_key_id
);
159 DCHECK(!enc_key_id
.empty());
160 if (enc_key_id
.empty())
163 encryption_key_id_
.assign(enc_key_id
);
164 demuxer_
->OnEncryptedMediaInitData(EmeInitDataType::WEBM
, enc_key_id
);
168 FFmpegDemuxerStream::~FFmpegDemuxerStream() {
170 DCHECK(read_cb_
.is_null());
171 DCHECK(buffer_queue_
.IsEmpty());
174 void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet
) {
175 DCHECK(task_runner_
->BelongsToCurrentThread());
177 if (!demuxer_
|| end_of_stream_
) {
178 NOTREACHED() << "Attempted to enqueue packet on a stopped stream";
182 #if defined(USE_PROPRIETARY_CODECS)
183 // Convert the packet if there is a bitstream filter.
184 if (packet
->data
&& bitstream_converter_
&&
185 !bitstream_converter_
->ConvertPacket(packet
.get())) {
186 LOG(ERROR
) << "Format conversion failed.";
190 // Get side data if any. For now, the only type of side_data is VP8 Alpha. We
191 // keep this generic so that other side_data types in the future can be
192 // handled the same way as well.
193 av_packet_split_side_data(packet
.get());
195 scoped_refptr
<DecoderBuffer
> buffer
;
197 if (type() == DemuxerStream::TEXT
) {
199 uint8
* id_data
= av_packet_get_side_data(
201 AV_PKT_DATA_WEBVTT_IDENTIFIER
,
204 int settings_size
= 0;
205 uint8
* settings_data
= av_packet_get_side_data(
207 AV_PKT_DATA_WEBVTT_SETTINGS
,
210 std::vector
<uint8
> side_data
;
211 MakeSideData(id_data
, id_data
+ id_size
,
212 settings_data
, settings_data
+ settings_size
,
215 buffer
= DecoderBuffer::CopyFrom(packet
.get()->data
, packet
.get()->size
,
216 side_data
.data(), side_data
.size());
218 int side_data_size
= 0;
219 uint8
* side_data
= av_packet_get_side_data(
221 AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL
,
224 scoped_ptr
<DecryptConfig
> decrypt_config
;
226 if ((type() == DemuxerStream::AUDIO
&& audio_config_
.is_encrypted()) ||
227 (type() == DemuxerStream::VIDEO
&& video_config_
.is_encrypted())) {
228 if (!WebMCreateDecryptConfig(
229 packet
->data
, packet
->size
,
230 reinterpret_cast<const uint8
*>(encryption_key_id_
.data()),
231 encryption_key_id_
.size(),
234 LOG(ERROR
) << "Creation of DecryptConfig failed.";
238 // If a packet is returned by FFmpeg's av_parser_parse2() the packet will
239 // reference inner memory of FFmpeg. As such we should transfer the packet
240 // into memory we control.
241 if (side_data_size
> 0) {
242 buffer
= DecoderBuffer::CopyFrom(packet
.get()->data
+ data_offset
,
243 packet
.get()->size
- data_offset
,
244 side_data
, side_data_size
);
246 buffer
= DecoderBuffer::CopyFrom(packet
.get()->data
+ data_offset
,
247 packet
.get()->size
- data_offset
);
250 int skip_samples_size
= 0;
251 const uint32
* skip_samples_ptr
=
252 reinterpret_cast<const uint32
*>(av_packet_get_side_data(
253 packet
.get(), AV_PKT_DATA_SKIP_SAMPLES
, &skip_samples_size
));
254 const int kSkipSamplesValidSize
= 10;
255 const int kSkipEndSamplesOffset
= 1;
256 if (skip_samples_size
>= kSkipSamplesValidSize
) {
257 // Because FFmpeg rolls codec delay and skip samples into one we can only
258 // allow front discard padding on the first buffer. Otherwise the discard
259 // helper can't figure out which data to discard. See AudioDiscardHelper.
260 int discard_front_samples
= base::ByteSwapToLE32(*skip_samples_ptr
);
261 if (last_packet_timestamp_
!= kNoTimestamp() && discard_front_samples
) {
262 DLOG(ERROR
) << "Skip samples are only allowed for the first packet.";
263 discard_front_samples
= 0;
266 const int discard_end_samples
=
267 base::ByteSwapToLE32(*(skip_samples_ptr
+ kSkipEndSamplesOffset
));
268 const int samples_per_second
=
269 audio_decoder_config().samples_per_second();
270 buffer
->set_discard_padding(std::make_pair(
271 FramesToTimeDelta(discard_front_samples
, samples_per_second
),
272 FramesToTimeDelta(discard_end_samples
, samples_per_second
)));
276 buffer
->set_decrypt_config(decrypt_config
.Pass());
279 if (packet
->duration
>= 0) {
280 buffer
->set_duration(
281 ConvertStreamTimestamp(stream_
->time_base
, packet
->duration
));
283 // TODO(wolenetz): Remove when FFmpeg stops returning negative durations.
284 // https://crbug.com/394418
285 DVLOG(1) << "FFmpeg returned a buffer with a negative duration! "
287 buffer
->set_duration(kNoTimestamp());
290 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp().
291 const base::TimeDelta stream_timestamp
=
292 ConvertStreamTimestamp(stream_
->time_base
, packet
->pts
);
294 if (stream_timestamp
!= kNoTimestamp()) {
295 const bool is_audio
= type() == AUDIO
;
297 // If this is an OGG file with negative timestamps don't rebase any other
298 // stream types against the negative starting time.
299 base::TimeDelta start_time
= demuxer_
->start_time();
300 if (fixup_negative_ogg_timestamps_
&& !is_audio
&&
301 start_time
< base::TimeDelta()) {
302 start_time
= base::TimeDelta();
305 // Don't rebase timestamps for positive start times, the HTML Media Spec
306 // details this in section "4.8.10.6 Offsets into the media resource." We
307 // will still need to rebase timestamps before seeking with FFmpeg though.
308 if (start_time
> base::TimeDelta())
309 start_time
= base::TimeDelta();
311 buffer
->set_timestamp(stream_timestamp
- start_time
);
313 // If enabled, mark audio packets with negative timestamps for post-decode
315 if (fixup_negative_ogg_timestamps_
&& is_audio
&&
316 stream_timestamp
< base::TimeDelta() &&
317 buffer
->duration() != kNoTimestamp()) {
318 if (stream_timestamp
+ buffer
->duration() < base::TimeDelta()) {
319 // Discard the entire packet if it's entirely before zero.
320 buffer
->set_discard_padding(
321 std::make_pair(kInfiniteDuration(), base::TimeDelta()));
323 // Only discard part of the frame if it overlaps zero.
324 buffer
->set_discard_padding(
325 std::make_pair(-stream_timestamp
, base::TimeDelta()));
329 // If this happens on the first packet, decoders will throw an error.
330 buffer
->set_timestamp(kNoTimestamp());
333 if (last_packet_timestamp_
!= kNoTimestamp()) {
334 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing
335 // continuity across links in the chain it uses the timestamp information
336 // from each link directly. Doing so can lead to timestamps which appear to
337 // go backwards in time.
339 // If the new link starts with a negative timestamp or a timestamp less than
340 // the original (positive) |start_time|, we will get a negative timestamp
341 // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not
342 // able to work out a timestamp using the previous link and the next.
344 // Fixing chained ogg is non-trivial, so for now just reuse the last good
345 // timestamp. The decoder will rewrite the timestamps to be sample accurate
346 // later. See http://crbug.com/396864.
347 if (fixup_negative_ogg_timestamps_
&&
348 (buffer
->timestamp() == kNoTimestamp() ||
349 buffer
->timestamp() < last_packet_timestamp_
)) {
350 buffer
->set_timestamp(last_packet_timestamp_
+
351 (last_packet_duration_
!= kNoTimestamp()
352 ? last_packet_duration_
353 : base::TimeDelta::FromMicroseconds(1)));
356 // The demuxer should always output positive timestamps.
357 DCHECK(buffer
->timestamp() >= base::TimeDelta());
358 DCHECK(buffer
->timestamp() != kNoTimestamp());
360 if (last_packet_timestamp_
< buffer
->timestamp()) {
361 buffered_ranges_
.Add(last_packet_timestamp_
, buffer
->timestamp());
362 demuxer_
->NotifyBufferingChanged();
366 if (packet
.get()->flags
& AV_PKT_FLAG_KEY
)
367 buffer
->set_is_key_frame(true);
369 last_packet_timestamp_
= buffer
->timestamp();
370 last_packet_duration_
= buffer
->duration();
372 buffer_queue_
.Push(buffer
);
373 SatisfyPendingRead();
376 void FFmpegDemuxerStream::SetEndOfStream() {
377 DCHECK(task_runner_
->BelongsToCurrentThread());
378 end_of_stream_
= true;
379 SatisfyPendingRead();
382 void FFmpegDemuxerStream::FlushBuffers() {
383 DCHECK(task_runner_
->BelongsToCurrentThread());
384 DCHECK(read_cb_
.is_null()) << "There should be no pending read";
386 // H264 and AAC require that we resend the header after flush.
387 // Reset bitstream for converter to do so.
388 // This is related to chromium issue 140371 (http://crbug.com/140371).
389 ResetBitstreamConverter();
391 buffer_queue_
.Clear();
392 end_of_stream_
= false;
393 last_packet_timestamp_
= kNoTimestamp();
394 last_packet_duration_
= kNoTimestamp();
397 void FFmpegDemuxerStream::Stop() {
398 DCHECK(task_runner_
->BelongsToCurrentThread());
399 buffer_queue_
.Clear();
400 if (!read_cb_
.is_null()) {
401 base::ResetAndReturn(&read_cb_
).Run(
402 DemuxerStream::kOk
, DecoderBuffer::CreateEOSBuffer());
406 end_of_stream_
= true;
409 DemuxerStream::Type
FFmpegDemuxerStream::type() const {
410 DCHECK(task_runner_
->BelongsToCurrentThread());
414 DemuxerStream::Liveness
FFmpegDemuxerStream::liveness() const {
415 DCHECK(task_runner_
->BelongsToCurrentThread());
419 void FFmpegDemuxerStream::Read(const ReadCB
& read_cb
) {
420 DCHECK(task_runner_
->BelongsToCurrentThread());
421 CHECK(read_cb_
.is_null()) << "Overlapping reads are not supported";
422 read_cb_
= BindToCurrentLoop(read_cb
);
424 // Don't accept any additional reads if we've been told to stop.
425 // The |demuxer_| may have been destroyed in the pipeline thread.
427 // TODO(scherkus): it would be cleaner to reply with an error message.
429 base::ResetAndReturn(&read_cb_
).Run(
430 DemuxerStream::kOk
, DecoderBuffer::CreateEOSBuffer());
434 SatisfyPendingRead();
437 void FFmpegDemuxerStream::EnableBitstreamConverter() {
438 DCHECK(task_runner_
->BelongsToCurrentThread());
440 #if defined(USE_PROPRIETARY_CODECS)
441 InitBitstreamConverter();
443 NOTREACHED() << "Proprietary codecs not enabled.";
447 void FFmpegDemuxerStream::ResetBitstreamConverter() {
448 #if defined(USE_PROPRIETARY_CODECS)
449 if (bitstream_converter_
)
450 InitBitstreamConverter();
451 #endif // defined(USE_PROPRIETARY_CODECS)
454 void FFmpegDemuxerStream::InitBitstreamConverter() {
455 #if defined(USE_PROPRIETARY_CODECS)
456 if (stream_
->codec
->codec_id
== AV_CODEC_ID_H264
) {
457 bitstream_converter_
.reset(
458 new FFmpegH264ToAnnexBBitstreamConverter(stream_
->codec
));
459 } else if (stream_
->codec
->codec_id
== AV_CODEC_ID_AAC
) {
460 bitstream_converter_
.reset(
461 new FFmpegAACBitstreamConverter(stream_
->codec
));
463 #endif // defined(USE_PROPRIETARY_CODECS)
466 bool FFmpegDemuxerStream::SupportsConfigChanges() { return false; }
468 AudioDecoderConfig
FFmpegDemuxerStream::audio_decoder_config() {
469 DCHECK(task_runner_
->BelongsToCurrentThread());
470 CHECK_EQ(type_
, AUDIO
);
471 return audio_config_
;
474 VideoDecoderConfig
FFmpegDemuxerStream::video_decoder_config() {
475 DCHECK(task_runner_
->BelongsToCurrentThread());
476 CHECK_EQ(type_
, VIDEO
);
477 return video_config_
;
480 VideoRotation
FFmpegDemuxerStream::video_rotation() {
481 return video_rotation_
;
484 void FFmpegDemuxerStream::SetLiveness(Liveness liveness
) {
485 DCHECK(task_runner_
->BelongsToCurrentThread());
486 DCHECK_EQ(liveness_
, LIVENESS_UNKNOWN
);
487 liveness_
= liveness
;
490 base::TimeDelta
FFmpegDemuxerStream::GetElapsedTime() const {
491 return ConvertStreamTimestamp(stream_
->time_base
, stream_
->cur_dts
);
494 Ranges
<base::TimeDelta
> FFmpegDemuxerStream::GetBufferedRanges() const {
495 return buffered_ranges_
;
498 void FFmpegDemuxerStream::SatisfyPendingRead() {
499 DCHECK(task_runner_
->BelongsToCurrentThread());
500 if (!read_cb_
.is_null()) {
501 if (!buffer_queue_
.IsEmpty()) {
502 base::ResetAndReturn(&read_cb_
).Run(
503 DemuxerStream::kOk
, buffer_queue_
.Pop());
504 } else if (end_of_stream_
) {
505 base::ResetAndReturn(&read_cb_
).Run(
506 DemuxerStream::kOk
, DecoderBuffer::CreateEOSBuffer());
510 // Have capacity? Ask for more!
511 if (HasAvailableCapacity() && !end_of_stream_
) {
512 demuxer_
->NotifyCapacityAvailable();
516 bool FFmpegDemuxerStream::HasAvailableCapacity() {
517 // TODO(scherkus): Remove this return and reenable time-based capacity
518 // after our data sources support canceling/concurrent reads, see
519 // http://crbug.com/165762 for details.
521 return !read_cb_
.is_null();
523 // Try to have one second's worth of encoded data per stream.
524 const base::TimeDelta kCapacity
= base::TimeDelta::FromSeconds(1);
525 return buffer_queue_
.IsEmpty() || buffer_queue_
.Duration() < kCapacity
;
529 size_t FFmpegDemuxerStream::MemoryUsage() const {
530 return buffer_queue_
.data_size();
533 TextKind
FFmpegDemuxerStream::GetTextKind() const {
534 DCHECK_EQ(type_
, DemuxerStream::TEXT
);
536 if (stream_
->disposition
& AV_DISPOSITION_CAPTIONS
)
537 return kTextCaptions
;
539 if (stream_
->disposition
& AV_DISPOSITION_DESCRIPTIONS
)
540 return kTextDescriptions
;
542 if (stream_
->disposition
& AV_DISPOSITION_METADATA
)
543 return kTextMetadata
;
545 return kTextSubtitles
;
548 std::string
FFmpegDemuxerStream::GetMetadata(const char* key
) const {
549 const AVDictionaryEntry
* entry
=
550 av_dict_get(stream_
->metadata
, key
, NULL
, 0);
551 return (entry
== NULL
|| entry
->value
== NULL
) ? "" : entry
->value
;
555 base::TimeDelta
FFmpegDemuxerStream::ConvertStreamTimestamp(
556 const AVRational
& time_base
, int64 timestamp
) {
557 if (timestamp
== static_cast<int64
>(AV_NOPTS_VALUE
))
558 return kNoTimestamp();
560 return ConvertFromTimeBase(time_base
, timestamp
);
566 FFmpegDemuxer::FFmpegDemuxer(
567 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
568 DataSource
* data_source
,
569 const EncryptedMediaInitDataCB
& encrypted_media_init_data_cb
,
570 const scoped_refptr
<MediaLog
>& media_log
)
572 task_runner_(task_runner
),
573 blocking_thread_("FFmpegDemuxer"),
574 pending_read_(false),
575 pending_seek_(false),
576 data_source_(data_source
),
577 media_log_(media_log
),
579 start_time_(kNoTimestamp()),
580 preferred_stream_for_seeking_(-1, kNoTimestamp()),
581 fallback_stream_for_seeking_(-1, kNoTimestamp()),
582 text_enabled_(false),
583 duration_known_(false),
584 encrypted_media_init_data_cb_(encrypted_media_init_data_cb
),
585 weak_factory_(this) {
586 DCHECK(task_runner_
.get());
587 DCHECK(data_source_
);
590 FFmpegDemuxer::~FFmpegDemuxer() {}
592 void FFmpegDemuxer::Stop() {
593 DCHECK(task_runner_
->BelongsToCurrentThread());
595 // The order of Stop() and Abort() is important here. If Abort() is called
596 // first, control may pass into FFmpeg where it can destruct buffers that are
597 // in the process of being fulfilled by the DataSource.
598 data_source_
->Stop();
599 url_protocol_
->Abort();
601 // This will block until all tasks complete. Note that after this returns it's
602 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this
603 // thread. Each of the reply task methods must check whether we've stopped the
604 // thread and drop their results on the floor.
605 blocking_thread_
.Stop();
607 StreamVector::iterator iter
;
608 for (iter
= streams_
.begin(); iter
!= streams_
.end(); ++iter
) {
616 void FFmpegDemuxer::Seek(base::TimeDelta time
, const PipelineStatusCB
& cb
) {
617 DCHECK(task_runner_
->BelongsToCurrentThread());
618 CHECK(!pending_seek_
);
620 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|,
621 // otherwise we can end up waiting for a pre-seek read to complete even though
622 // we know we're going to drop it on the floor.
624 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
625 // Since EnqueuePacket() rebased negative timestamps by the start time, we
626 // must correct the shift here.
628 // Additionally, to workaround limitations in how we expose seekable ranges to
629 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
630 // start time to the start time.
631 const base::TimeDelta seek_time
=
632 start_time_
< base::TimeDelta() ? time
+ start_time_
633 : time
< start_time_
? start_time_
: time
;
635 // Choose the seeking stream based on whether it contains the seek time, if no
636 // match can be found prefer the preferred stream.
638 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a
639 // given container will demux all packets after the seek point. Instead it
640 // only guarantees that all packets after the file position of the seek will
641 // be demuxed. It's an open question whether FFmpeg should fix this:
642 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
643 // Tracked by http://crbug.com/387996.
644 DCHECK(preferred_stream_for_seeking_
.second
!= kNoTimestamp());
645 const int stream_index
=
646 seek_time
< preferred_stream_for_seeking_
.second
&&
647 seek_time
>= fallback_stream_for_seeking_
.second
648 ? fallback_stream_for_seeking_
.first
649 : preferred_stream_for_seeking_
.first
;
650 DCHECK_NE(stream_index
, -1);
652 const AVStream
* seeking_stream
=
653 glue_
->format_context()->streams
[stream_index
];
655 pending_seek_
= true;
656 base::PostTaskAndReplyWithResult(
657 blocking_thread_
.message_loop_proxy().get(),
659 base::Bind(&av_seek_frame
,
660 glue_
->format_context(),
661 seeking_stream
->index
,
662 ConvertToTimeBase(seeking_stream
->time_base
, seek_time
),
663 // Always seek to a timestamp <= to the desired timestamp.
664 AVSEEK_FLAG_BACKWARD
),
666 &FFmpegDemuxer::OnSeekFrameDone
, weak_factory_
.GetWeakPtr(), cb
));
669 void FFmpegDemuxer::Initialize(DemuxerHost
* host
,
670 const PipelineStatusCB
& status_cb
,
671 bool enable_text_tracks
) {
672 DCHECK(task_runner_
->BelongsToCurrentThread());
674 text_enabled_
= enable_text_tracks
;
676 url_protocol_
.reset(new BlockingUrlProtocol(data_source_
, BindToCurrentLoop(
677 base::Bind(&FFmpegDemuxer::OnDataSourceError
, base::Unretained(this)))));
678 glue_
.reset(new FFmpegGlue(url_protocol_
.get()));
679 AVFormatContext
* format_context
= glue_
->format_context();
681 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we
682 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is
683 // available, so add a metadata entry to ensure some is always present.
684 av_dict_set(&format_context
->metadata
, "skip_id3v1_tags", "", 0);
686 // Ensure ffmpeg doesn't give up too early while looking for stream params;
687 // this does not increase the amount of data downloaded. The default value
688 // is 5 AV_TIME_BASE units (1 second each), which prevents some oddly muxed
689 // streams from being detected properly; this value was chosen arbitrarily.
690 format_context
->max_analyze_duration2
= 60 * AV_TIME_BASE
;
692 // Open the AVFormatContext using our glue layer.
693 CHECK(blocking_thread_
.Start());
694 base::PostTaskAndReplyWithResult(
695 blocking_thread_
.message_loop_proxy().get(),
697 base::Bind(&FFmpegGlue::OpenContext
, base::Unretained(glue_
.get())),
698 base::Bind(&FFmpegDemuxer::OnOpenContextDone
,
699 weak_factory_
.GetWeakPtr(),
703 base::Time
FFmpegDemuxer::GetTimelineOffset() const {
704 return timeline_offset_
;
707 DemuxerStream
* FFmpegDemuxer::GetStream(DemuxerStream::Type type
) {
708 DCHECK(task_runner_
->BelongsToCurrentThread());
709 return GetFFmpegStream(type
);
712 FFmpegDemuxerStream
* FFmpegDemuxer::GetFFmpegStream(
713 DemuxerStream::Type type
) const {
714 StreamVector::const_iterator iter
;
715 for (iter
= streams_
.begin(); iter
!= streams_
.end(); ++iter
) {
716 if (*iter
&& (*iter
)->type() == type
) {
723 base::TimeDelta
FFmpegDemuxer::GetStartTime() const {
724 return std::max(start_time_
, base::TimeDelta());
727 void FFmpegDemuxer::AddTextStreams() {
728 DCHECK(task_runner_
->BelongsToCurrentThread());
730 for (StreamVector::size_type idx
= 0; idx
< streams_
.size(); ++idx
) {
731 FFmpegDemuxerStream
* stream
= streams_
[idx
];
732 if (stream
== NULL
|| stream
->type() != DemuxerStream::TEXT
)
735 TextKind kind
= stream
->GetTextKind();
736 std::string title
= stream
->GetMetadata("title");
737 std::string language
= stream
->GetMetadata("language");
739 // TODO: Implement "id" metadata in FFMPEG.
740 // See: http://crbug.com/323183
741 host_
->AddTextStream(stream
, TextTrackConfig(kind
, title
, language
,
746 // Helper for calculating the bitrate of the media based on information stored
747 // in |format_context| or failing that the size and duration of the media.
749 // Returns 0 if a bitrate could not be determined.
750 static int CalculateBitrate(
751 AVFormatContext
* format_context
,
752 const base::TimeDelta
& duration
,
753 int64 filesize_in_bytes
) {
754 // If there is a bitrate set on the container, use it.
755 if (format_context
->bit_rate
> 0)
756 return format_context
->bit_rate
;
758 // Then try to sum the bitrates individually per stream.
760 for (size_t i
= 0; i
< format_context
->nb_streams
; ++i
) {
761 AVCodecContext
* codec_context
= format_context
->streams
[i
]->codec
;
762 bitrate
+= codec_context
->bit_rate
;
767 // See if we can approximate the bitrate as long as we have a filesize and
769 if (duration
.InMicroseconds() <= 0 ||
770 duration
== kInfiniteDuration() ||
771 filesize_in_bytes
== 0) {
775 // Do math in floating point as we'd overflow an int64 if the filesize was
776 // larger than ~1073GB.
777 double bytes
= filesize_in_bytes
;
778 double duration_us
= duration
.InMicroseconds();
779 return bytes
* 8000000.0 / duration_us
;
782 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB
& status_cb
,
784 DCHECK(task_runner_
->BelongsToCurrentThread());
785 if (!blocking_thread_
.IsRunning()) {
786 status_cb
.Run(PIPELINE_ERROR_ABORT
);
791 status_cb
.Run(DEMUXER_ERROR_COULD_NOT_OPEN
);
795 // Fully initialize AVFormatContext by parsing the stream a little.
796 base::PostTaskAndReplyWithResult(
797 blocking_thread_
.message_loop_proxy().get(),
799 base::Bind(&avformat_find_stream_info
,
800 glue_
->format_context(),
801 static_cast<AVDictionary
**>(NULL
)),
802 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone
,
803 weak_factory_
.GetWeakPtr(),
807 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB
& status_cb
,
809 DCHECK(task_runner_
->BelongsToCurrentThread());
810 if (!blocking_thread_
.IsRunning() || !data_source_
) {
811 status_cb
.Run(PIPELINE_ERROR_ABORT
);
816 status_cb
.Run(DEMUXER_ERROR_COULD_NOT_PARSE
);
820 // Create demuxer stream entries for each possible AVStream. Each stream
821 // is examined to determine if it is supported or not (is the codec enabled
822 // for it in this release?). Unsupported streams are skipped, allowing for
823 // partial playback. At least one audio or video stream must be playable.
824 AVFormatContext
* format_context
= glue_
->format_context();
825 streams_
.resize(format_context
->nb_streams
);
827 // Estimate the start time for each stream by looking through the packets
828 // buffered during avformat_find_stream_info(). These values will be
829 // considered later when determining the actual stream start time.
831 // These packets haven't been completely processed yet, so only look through
832 // these values if the AVFormatContext has a valid start time.
834 // If no estimate is found, the stream entry will be kInfiniteDuration().
835 std::vector
<base::TimeDelta
> start_time_estimates(format_context
->nb_streams
,
836 kInfiniteDuration());
837 const AVFormatInternal
* internal
= format_context
->internal
;
838 if (internal
&& internal
->packet_buffer
&&
839 format_context
->start_time
!= static_cast<int64
>(AV_NOPTS_VALUE
)) {
840 struct AVPacketList
* packet_buffer
= internal
->packet_buffer
;
841 while (packet_buffer
!= internal
->packet_buffer_end
) {
842 DCHECK_LT(static_cast<size_t>(packet_buffer
->pkt
.stream_index
),
843 start_time_estimates
.size());
844 const AVStream
* stream
=
845 format_context
->streams
[packet_buffer
->pkt
.stream_index
];
846 if (packet_buffer
->pkt
.pts
!= static_cast<int64
>(AV_NOPTS_VALUE
)) {
847 const base::TimeDelta packet_pts
=
848 ConvertFromTimeBase(stream
->time_base
, packet_buffer
->pkt
.pts
);
849 if (packet_pts
< start_time_estimates
[stream
->index
])
850 start_time_estimates
[stream
->index
] = packet_pts
;
852 packet_buffer
= packet_buffer
->next
;
856 AVStream
* audio_stream
= NULL
;
857 AudioDecoderConfig audio_config
;
859 AVStream
* video_stream
= NULL
;
860 VideoDecoderConfig video_config
;
862 // If available, |start_time_| will be set to the lowest stream start time.
863 start_time_
= kInfiniteDuration();
865 base::TimeDelta max_duration
;
866 for (size_t i
= 0; i
< format_context
->nb_streams
; ++i
) {
867 AVStream
* stream
= format_context
->streams
[i
];
868 const AVCodecContext
* codec_context
= stream
->codec
;
869 const AVMediaType codec_type
= codec_context
->codec_type
;
871 if (codec_type
== AVMEDIA_TYPE_AUDIO
) {
875 // Log the codec detected, whether it is supported or not.
876 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec",
877 codec_context
->codec_id
);
878 // Ensure the codec is supported. IsValidConfig() also checks that the
879 // channel layout and sample format are valid.
880 AVStreamToAudioDecoderConfig(stream
, &audio_config
, false);
881 if (!audio_config
.IsValidConfig())
883 audio_stream
= stream
;
884 } else if (codec_type
== AVMEDIA_TYPE_VIDEO
) {
888 // Log the codec detected, whether it is supported or not.
889 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec",
890 codec_context
->codec_id
);
891 // Ensure the codec is supported. IsValidConfig() also checks that the
892 // frame size and visible size are valid.
893 AVStreamToVideoDecoderConfig(stream
, &video_config
, false);
895 if (!video_config
.IsValidConfig())
897 video_stream
= stream
;
898 } else if (codec_type
== AVMEDIA_TYPE_SUBTITLE
) {
899 if (codec_context
->codec_id
!= AV_CODEC_ID_WEBVTT
|| !text_enabled_
) {
906 streams_
[i
] = new FFmpegDemuxerStream(this, stream
);
907 max_duration
= std::max(max_duration
, streams_
[i
]->duration());
909 const base::TimeDelta start_time
=
910 ExtractStartTime(stream
, start_time_estimates
[i
]);
911 const bool has_start_time
= start_time
!= kNoTimestamp();
913 // Always prefer the video stream for seeking. If none exists, we'll swap
914 // the fallback stream with the preferred stream below.
915 if (codec_type
== AVMEDIA_TYPE_VIDEO
) {
916 preferred_stream_for_seeking_
=
917 StreamSeekInfo(i
, has_start_time
? start_time
: base::TimeDelta());
923 if (start_time
< start_time_
) {
924 start_time_
= start_time
;
926 // Choose the stream with the lowest starting time as the fallback stream
927 // for seeking. Video should always be preferred.
928 fallback_stream_for_seeking_
= StreamSeekInfo(i
, start_time
);
932 if (!audio_stream
&& !video_stream
) {
933 status_cb
.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS
);
940 if (format_context
->duration
!= static_cast<int64_t>(AV_NOPTS_VALUE
)) {
941 // If there is a duration value in the container use that to find the
942 // maximum between it and the duration from A/V streams.
943 const AVRational av_time_base
= {1, AV_TIME_BASE
};
945 std::max(max_duration
,
946 ConvertFromTimeBase(av_time_base
, format_context
->duration
));
948 // The duration is unknown, in which case this is likely a live stream.
949 max_duration
= kInfiniteDuration();
952 // Ogg has some peculiarities around negative timestamps, so use this flag to
953 // setup the FFmpegDemuxerStreams appropriately.
955 // Post-decode frame dropping for packets with negative timestamps is outlined
956 // in section A.2 in the Ogg Vorbis spec:
957 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
958 if (strcmp(format_context
->iformat
->name
, "ogg") == 0 && audio_stream
&&
959 audio_stream
->codec
->codec_id
== AV_CODEC_ID_VORBIS
) {
960 for (size_t i
= 0; i
< streams_
.size(); ++i
) {
962 streams_
[i
]->enable_negative_timestamp_fixups_for_ogg();
965 // Fixup the seeking information to avoid selecting the audio stream simply
966 // because it has a lower starting time.
967 if (fallback_stream_for_seeking_
.first
== audio_stream
->index
&&
968 fallback_stream_for_seeking_
.second
< base::TimeDelta()) {
969 fallback_stream_for_seeking_
.second
= base::TimeDelta();
973 // If no start time could be determined, default to zero and prefer the video
974 // stream over the audio stream for seeking. E.g., The WAV demuxer does not
975 // put timestamps on its frames.
976 if (start_time_
== kInfiniteDuration()) {
977 start_time_
= base::TimeDelta();
978 preferred_stream_for_seeking_
= StreamSeekInfo(
979 video_stream
? video_stream
->index
: audio_stream
->index
, start_time_
);
980 } else if (!video_stream
) {
981 // If no video stream exists, use the audio or text stream found above.
982 preferred_stream_for_seeking_
= fallback_stream_for_seeking_
;
985 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS
986 // generation so we always get timestamps, see http://crbug.com/169570
987 if (strcmp(format_context
->iformat
->name
, "avi") == 0)
988 format_context
->flags
|= AVFMT_FLAG_GENPTS
;
990 // For testing purposes, don't overwrite the timeline offset if set already.
991 if (timeline_offset_
.is_null())
992 timeline_offset_
= ExtractTimelineOffset(format_context
);
994 // Since we're shifting the externally visible start time to zero, we need to
995 // adjust the timeline offset to compensate.
996 if (!timeline_offset_
.is_null() && start_time_
< base::TimeDelta())
997 timeline_offset_
+= start_time_
;
999 if (max_duration
== kInfiniteDuration() && !timeline_offset_
.is_null()) {
1000 SetLiveness(DemuxerStream::LIVENESS_LIVE
);
1001 } else if (max_duration
!= kInfiniteDuration()) {
1002 SetLiveness(DemuxerStream::LIVENESS_RECORDED
);
1004 SetLiveness(DemuxerStream::LIVENESS_UNKNOWN
);
1007 // Good to go: set the duration and bitrate and notify we're done
1009 host_
->SetDuration(max_duration
);
1010 duration_known_
= (max_duration
!= kInfiniteDuration());
1012 int64 filesize_in_bytes
= 0;
1013 url_protocol_
->GetSize(&filesize_in_bytes
);
1014 bitrate_
= CalculateBitrate(format_context
, max_duration
, filesize_in_bytes
);
1016 data_source_
->SetBitrate(bitrate_
);
1020 AVCodecContext
* audio_codec
= audio_stream
->codec
;
1021 media_log_
->SetBooleanProperty("found_audio_stream", true);
1023 SampleFormat sample_format
= audio_config
.sample_format();
1024 std::string sample_name
= SampleFormatToString(sample_format
);
1026 media_log_
->SetStringProperty("audio_sample_format", sample_name
);
1028 AVCodec
* codec
= avcodec_find_decoder(audio_codec
->codec_id
);
1030 media_log_
->SetStringProperty("audio_codec_name", codec
->name
);
1033 media_log_
->SetIntegerProperty("audio_channels_count",
1034 audio_codec
->channels
);
1035 media_log_
->SetIntegerProperty("audio_samples_per_second",
1036 audio_config
.samples_per_second());
1038 media_log_
->SetBooleanProperty("found_audio_stream", false);
1043 AVCodecContext
* video_codec
= video_stream
->codec
;
1044 media_log_
->SetBooleanProperty("found_video_stream", true);
1046 AVCodec
* codec
= avcodec_find_decoder(video_codec
->codec_id
);
1048 media_log_
->SetStringProperty("video_codec_name", codec
->name
);
1049 } else if (video_codec
->codec_id
== AV_CODEC_ID_VP9
) {
1050 // ffmpeg doesn't know about VP9 decoder. So we need to log it explicitly.
1051 media_log_
->SetStringProperty("video_codec_name", "vp9");
1054 media_log_
->SetIntegerProperty("width", video_codec
->width
);
1055 media_log_
->SetIntegerProperty("height", video_codec
->height
);
1056 media_log_
->SetIntegerProperty("coded_width",
1057 video_codec
->coded_width
);
1058 media_log_
->SetIntegerProperty("coded_height",
1059 video_codec
->coded_height
);
1060 media_log_
->SetStringProperty(
1062 base::StringPrintf("%d/%d",
1063 video_codec
->time_base
.num
,
1064 video_codec
->time_base
.den
));
1065 media_log_
->SetStringProperty(
1066 "video_format", VideoFrame::FormatToString(video_config
.format()));
1067 media_log_
->SetBooleanProperty("video_is_encrypted",
1068 video_config
.is_encrypted());
1070 media_log_
->SetBooleanProperty("found_video_stream", false);
1073 media_log_
->SetTimeProperty("max_duration", max_duration
);
1074 media_log_
->SetTimeProperty("start_time", start_time_
);
1075 media_log_
->SetIntegerProperty("bitrate", bitrate_
);
1077 status_cb
.Run(PIPELINE_OK
);
1080 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB
& cb
, int result
) {
1081 DCHECK(task_runner_
->BelongsToCurrentThread());
1082 CHECK(pending_seek_
);
1083 pending_seek_
= false;
1085 if (!blocking_thread_
.IsRunning()) {
1086 cb
.Run(PIPELINE_ERROR_ABORT
);
1091 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being
1092 // captured from stdout and contaminates testing.
1093 // TODO(scherkus): Implement this properly and signal error (BUG=23447).
1094 VLOG(1) << "Not implemented";
1097 // Tell streams to flush buffers due to seeking.
1098 StreamVector::iterator iter
;
1099 for (iter
= streams_
.begin(); iter
!= streams_
.end(); ++iter
) {
1101 (*iter
)->FlushBuffers();
1104 // Resume reading until capacity.
1105 ReadFrameIfNeeded();
1107 // Notify we're finished seeking.
1108 cb
.Run(PIPELINE_OK
);
1111 void FFmpegDemuxer::ReadFrameIfNeeded() {
1112 DCHECK(task_runner_
->BelongsToCurrentThread());
1114 // Make sure we have work to do before reading.
1115 if (!blocking_thread_
.IsRunning() || !StreamsHaveAvailableCapacity() ||
1116 pending_read_
|| pending_seek_
) {
1120 // Allocate and read an AVPacket from the media. Save |packet_ptr| since
1121 // evaluation order of packet.get() and base::Passed(&packet) is
1123 ScopedAVPacket
packet(new AVPacket());
1124 AVPacket
* packet_ptr
= packet
.get();
1126 pending_read_
= true;
1127 base::PostTaskAndReplyWithResult(
1128 blocking_thread_
.message_loop_proxy().get(),
1130 base::Bind(&av_read_frame
, glue_
->format_context(), packet_ptr
),
1131 base::Bind(&FFmpegDemuxer::OnReadFrameDone
,
1132 weak_factory_
.GetWeakPtr(),
1133 base::Passed(&packet
)));
1136 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet
, int result
) {
1137 DCHECK(task_runner_
->BelongsToCurrentThread());
1138 DCHECK(pending_read_
);
1139 pending_read_
= false;
1141 if (!blocking_thread_
.IsRunning() || pending_seek_
) {
1145 // Consider the stream as ended if:
1146 // - either underlying ffmpeg returned an error
1147 // - or FFMpegDemuxer reached the maximum allowed memory usage.
1148 if (result
< 0 || IsMaxMemoryUsageReached()) {
1149 // Update the duration based on the highest elapsed time across all streams
1150 // if it was previously unknown.
1151 if (!duration_known_
) {
1152 base::TimeDelta max_duration
;
1154 for (StreamVector::iterator iter
= streams_
.begin();
1155 iter
!= streams_
.end();
1160 base::TimeDelta duration
= (*iter
)->GetElapsedTime();
1161 if (duration
!= kNoTimestamp() && duration
> max_duration
)
1162 max_duration
= duration
;
1165 if (max_duration
> base::TimeDelta()) {
1166 host_
->SetDuration(max_duration
);
1167 duration_known_
= true;
1170 // If we have reached the end of stream, tell the downstream filters about
1176 // Queue the packet with the appropriate stream.
1177 DCHECK_GE(packet
->stream_index
, 0);
1178 DCHECK_LT(packet
->stream_index
, static_cast<int>(streams_
.size()));
1180 // Defend against ffmpeg giving us a bad stream index.
1181 if (packet
->stream_index
>= 0 &&
1182 packet
->stream_index
< static_cast<int>(streams_
.size()) &&
1183 streams_
[packet
->stream_index
]) {
1184 // TODO(scherkus): Fix demuxing upstream to never return packets w/o data
1185 // when av_read_frame() returns success code. See bug comment for ideas:
1187 // https://code.google.com/p/chromium/issues/detail?id=169133#c10
1188 if (!packet
->data
) {
1189 ScopedAVPacket
new_packet(new AVPacket());
1190 av_new_packet(new_packet
.get(), 0);
1191 av_packet_copy_props(new_packet
.get(), packet
.get());
1192 packet
.swap(new_packet
);
1195 // Special case for opus in ogg. FFmpeg is pre-trimming the codec delay
1196 // from the packet timestamp. Chrome expects to handle this itself inside
1197 // the decoder, so shift timestamps by the delay in this case.
1198 // TODO(dalecurtis): Try to get fixed upstream. See http://crbug.com/328207
1199 if (strcmp(glue_
->format_context()->iformat
->name
, "ogg") == 0) {
1200 const AVCodecContext
* codec_context
=
1201 glue_
->format_context()->streams
[packet
->stream_index
]->codec
;
1202 if (codec_context
->codec_id
== AV_CODEC_ID_OPUS
&&
1203 codec_context
->delay
> 0) {
1204 packet
->pts
+= codec_context
->delay
;
1208 FFmpegDemuxerStream
* demuxer_stream
= streams_
[packet
->stream_index
];
1209 demuxer_stream
->EnqueuePacket(packet
.Pass());
1212 // Keep reading until we've reached capacity.
1213 ReadFrameIfNeeded();
1216 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
1217 DCHECK(task_runner_
->BelongsToCurrentThread());
1218 StreamVector::iterator iter
;
1219 for (iter
= streams_
.begin(); iter
!= streams_
.end(); ++iter
) {
1220 if (*iter
&& (*iter
)->HasAvailableCapacity()) {
1227 bool FFmpegDemuxer::IsMaxMemoryUsageReached() const {
1228 DCHECK(task_runner_
->BelongsToCurrentThread());
1230 // Max allowed memory usage, all streams combined.
1231 const size_t kDemuxerMemoryLimit
= 150 * 1024 * 1024;
1233 size_t memory_left
= kDemuxerMemoryLimit
;
1234 for (StreamVector::const_iterator iter
= streams_
.begin();
1235 iter
!= streams_
.end(); ++iter
) {
1239 size_t stream_memory_usage
= (*iter
)->MemoryUsage();
1240 if (stream_memory_usage
> memory_left
)
1242 memory_left
-= stream_memory_usage
;
1247 void FFmpegDemuxer::StreamHasEnded() {
1248 DCHECK(task_runner_
->BelongsToCurrentThread());
1249 StreamVector::iterator iter
;
1250 for (iter
= streams_
.begin(); iter
!= streams_
.end(); ++iter
) {
1253 (*iter
)->SetEndOfStream();
1257 void FFmpegDemuxer::OnEncryptedMediaInitData(
1258 EmeInitDataType init_data_type
,
1259 const std::string
& encryption_key_id
) {
1260 std::vector
<uint8
> key_id_local(encryption_key_id
.begin(),
1261 encryption_key_id
.end());
1262 encrypted_media_init_data_cb_
.Run(init_data_type
, key_id_local
);
1265 void FFmpegDemuxer::NotifyCapacityAvailable() {
1266 DCHECK(task_runner_
->BelongsToCurrentThread());
1267 ReadFrameIfNeeded();
1270 void FFmpegDemuxer::NotifyBufferingChanged() {
1271 DCHECK(task_runner_
->BelongsToCurrentThread());
1272 Ranges
<base::TimeDelta
> buffered
;
1273 FFmpegDemuxerStream
* audio
= GetFFmpegStream(DemuxerStream::AUDIO
);
1274 FFmpegDemuxerStream
* video
= GetFFmpegStream(DemuxerStream::VIDEO
);
1275 if (audio
&& video
) {
1276 buffered
= audio
->GetBufferedRanges().IntersectionWith(
1277 video
->GetBufferedRanges());
1279 buffered
= audio
->GetBufferedRanges();
1281 buffered
= video
->GetBufferedRanges();
1283 for (size_t i
= 0; i
< buffered
.size(); ++i
)
1284 host_
->AddBufferedTimeRange(buffered
.start(i
), buffered
.end(i
));
1287 void FFmpegDemuxer::OnDataSourceError() {
1288 host_
->OnDemuxerError(PIPELINE_ERROR_READ
);
1291 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness
) {
1292 DCHECK(task_runner_
->BelongsToCurrentThread());
1293 for (const auto& stream
: streams_
) { // |stream| is a ref to a pointer.
1295 stream
->SetLiveness(liveness
);
1299 } // namespace media