OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
48 } | 48 } |
49 | 49 |
50 return base::Time(); | 50 return base::Time(); |
51 } | 51 } |
52 | 52 |
53 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) { | 53 static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) { |
54 return base::TimeDelta::FromMicroseconds( | 54 return base::TimeDelta::FromMicroseconds( |
55 frames * base::Time::kMicrosecondsPerSecond / sample_rate); | 55 frames * base::Time::kMicrosecondsPerSecond / sample_rate); |
56 } | 56 } |
57 | 57 |
58 static base::TimeDelta ExtractStartTime(AVStream* stream) { | |
59 if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) | |
60 return kNoTimestamp(); | |
61 | |
62 // First try to use the |start_time| value directly. | |
63 const base::TimeDelta start_time = | |
64 ConvertFromTimeBase(stream->time_base, stream->start_time); | |
65 | |
66 // Then compare against the first timestamp to see if adjustment is required. | |
67 if (stream->first_dts == static_cast<int64_t>(AV_NOPTS_VALUE)) | |
68 return start_time; | |
69 | |
70 const base::TimeDelta first_dts = | |
71 ConvertFromTimeBase(stream->time_base, stream->first_dts); | |
72 | |
73 return first_dts < start_time ? first_dts : start_time; | |
74 } | |
75 | |
58 // | 76 // |
59 // FFmpegDemuxerStream | 77 // FFmpegDemuxerStream |
60 // | 78 // |
61 FFmpegDemuxerStream::FFmpegDemuxerStream( | 79 FFmpegDemuxerStream::FFmpegDemuxerStream( |
62 FFmpegDemuxer* demuxer, | 80 FFmpegDemuxer* demuxer, |
63 AVStream* stream) | 81 AVStream* stream) |
64 : demuxer_(demuxer), | 82 : demuxer_(demuxer), |
65 task_runner_(base::MessageLoopProxy::current()), | 83 task_runner_(base::MessageLoopProxy::current()), |
66 stream_(stream), | 84 stream_(stream), |
67 type_(UNKNOWN), | 85 type_(UNKNOWN), |
68 end_of_stream_(false), | 86 end_of_stream_(false), |
69 last_packet_timestamp_(kNoTimestamp()), | 87 last_packet_timestamp_(kNoTimestamp()), |
70 bitstream_converter_enabled_(false) { | 88 bitstream_converter_enabled_(false), |
89 discard_negative_timestamps_(false) { | |
71 DCHECK(demuxer_); | 90 DCHECK(demuxer_); |
72 | 91 |
73 bool is_encrypted = false; | 92 bool is_encrypted = false; |
74 | 93 |
75 // Determine our media format. | 94 // Determine our media format. |
76 switch (stream->codec->codec_type) { | 95 switch (stream->codec->codec_type) { |
77 case AVMEDIA_TYPE_AUDIO: | 96 case AVMEDIA_TYPE_AUDIO: |
78 type_ = AUDIO; | 97 type_ = AUDIO; |
79 AVStreamToAudioDecoderConfig(stream, &audio_config_, true); | 98 AVStreamToAudioDecoderConfig(stream, &audio_config_, true); |
80 is_encrypted = audio_config_.is_encrypted(); | 99 is_encrypted = audio_config_.is_encrypted(); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 audio_decoder_config().samples_per_second(); | 238 audio_decoder_config().samples_per_second(); |
220 buffer->set_discard_padding(std::make_pair( | 239 buffer->set_discard_padding(std::make_pair( |
221 FramesToTimeDelta(discard_front_samples, samples_per_second), | 240 FramesToTimeDelta(discard_front_samples, samples_per_second), |
222 FramesToTimeDelta(discard_end_samples, samples_per_second))); | 241 FramesToTimeDelta(discard_end_samples, samples_per_second))); |
223 } | 242 } |
224 | 243 |
225 if (decrypt_config) | 244 if (decrypt_config) |
226 buffer->set_decrypt_config(decrypt_config.Pass()); | 245 buffer->set_decrypt_config(decrypt_config.Pass()); |
227 } | 246 } |
228 | 247 |
229 buffer->set_timestamp(ConvertStreamTimestamp( | 248 const base::TimeDelta stream_timestamp = |
230 stream_->time_base, packet->pts)); | 249 ConvertStreamTimestamp(stream_->time_base, packet->pts); |
231 buffer->set_duration(ConvertStreamTimestamp( | 250 |
232 stream_->time_base, packet->duration)); | 251 buffer->set_timestamp(stream_timestamp - demuxer_->start_time()); |
acolwell GONE FROM CHROMIUM
2014/06/10 23:20:38
Does this hold up with chained Ogg? Does FFmpeg gu
DaleCurtis
2014/06/11 16:59:07
Depends on how short the first link in the chain i
acolwell GONE FROM CHROMIUM
2014/06/11 17:40:24
I guess my concern is that if FFmpeg allows timest
DaleCurtis
2014/06/12 00:21:56
I chopped the file as you described it seems times
| |
252 buffer->set_duration( | |
253 ConvertStreamTimestamp(stream_->time_base, packet->duration)); | |
254 | |
255 // If enabled, mark packets with negative timestamps for post-decode discard. | |
256 if (discard_negative_timestamps_ && stream_timestamp < base::TimeDelta()) { | |
257 buffer->set_discard_padding( | |
258 std::make_pair(kInfiniteDuration(), base::TimeDelta())); | |
259 } | |
260 | |
233 if (buffer->timestamp() != kNoTimestamp() && | 261 if (buffer->timestamp() != kNoTimestamp() && |
234 last_packet_timestamp_ != kNoTimestamp() && | 262 last_packet_timestamp_ != kNoTimestamp() && |
235 last_packet_timestamp_ < buffer->timestamp()) { | 263 last_packet_timestamp_ < buffer->timestamp()) { |
236 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); | 264 buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
237 demuxer_->NotifyBufferingChanged(); | 265 demuxer_->NotifyBufferingChanged(); |
238 } | 266 } |
239 last_packet_timestamp_ = buffer->timestamp(); | 267 last_packet_timestamp_ = buffer->timestamp(); |
240 | 268 |
241 buffer_queue_.Push(buffer); | 269 buffer_queue_.Push(buffer); |
242 SatisfyPendingRead(); | 270 SatisfyPendingRead(); |
(...skipping 18 matching lines...) Expand all Loading... | |
261 buffer_queue_.Clear(); | 289 buffer_queue_.Clear(); |
262 if (!read_cb_.is_null()) { | 290 if (!read_cb_.is_null()) { |
263 base::ResetAndReturn(&read_cb_).Run( | 291 base::ResetAndReturn(&read_cb_).Run( |
264 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); | 292 DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
265 } | 293 } |
266 demuxer_ = NULL; | 294 demuxer_ = NULL; |
267 stream_ = NULL; | 295 stream_ = NULL; |
268 end_of_stream_ = true; | 296 end_of_stream_ = true; |
269 } | 297 } |
270 | 298 |
271 base::TimeDelta FFmpegDemuxerStream::duration() { | |
272 return duration_; | |
273 } | |
274 | |
275 DemuxerStream::Type FFmpegDemuxerStream::type() { | 299 DemuxerStream::Type FFmpegDemuxerStream::type() { |
276 DCHECK(task_runner_->BelongsToCurrentThread()); | 300 DCHECK(task_runner_->BelongsToCurrentThread()); |
277 return type_; | 301 return type_; |
278 } | 302 } |
279 | 303 |
280 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { | 304 void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
281 DCHECK(task_runner_->BelongsToCurrentThread()); | 305 DCHECK(task_runner_->BelongsToCurrentThread()); |
282 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; | 306 CHECK(read_cb_.is_null()) << "Overlapping reads are not supported"; |
283 read_cb_ = BindToCurrentLoop(read_cb); | 307 read_cb_ = BindToCurrentLoop(read_cb); |
284 | 308 |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
408 const NeedKeyCB& need_key_cb, | 432 const NeedKeyCB& need_key_cb, |
409 const scoped_refptr<MediaLog>& media_log) | 433 const scoped_refptr<MediaLog>& media_log) |
410 : host_(NULL), | 434 : host_(NULL), |
411 task_runner_(task_runner), | 435 task_runner_(task_runner), |
412 blocking_thread_("FFmpegDemuxer"), | 436 blocking_thread_("FFmpegDemuxer"), |
413 pending_read_(false), | 437 pending_read_(false), |
414 pending_seek_(false), | 438 pending_seek_(false), |
415 data_source_(data_source), | 439 data_source_(data_source), |
416 media_log_(media_log), | 440 media_log_(media_log), |
417 bitrate_(0), | 441 bitrate_(0), |
418 start_time_(kNoTimestamp()), | 442 start_time_(kInfiniteDuration()), |
acolwell GONE FROM CHROMIUM
2014/06/10 23:20:38
This seems a little out of place since we typicall
DaleCurtis
2014/06/12 00:21:55
Done.
| |
419 liveness_(LIVENESS_UNKNOWN), | 443 liveness_(LIVENESS_UNKNOWN), |
420 text_enabled_(false), | 444 text_enabled_(false), |
421 duration_known_(false), | 445 duration_known_(false), |
422 need_key_cb_(need_key_cb), | 446 need_key_cb_(need_key_cb), |
447 stream_index_for_seeking_(0), | |
423 weak_factory_(this) { | 448 weak_factory_(this) { |
424 DCHECK(task_runner_.get()); | 449 DCHECK(task_runner_.get()); |
425 DCHECK(data_source_); | 450 DCHECK(data_source_); |
426 } | 451 } |
427 | 452 |
428 FFmpegDemuxer::~FFmpegDemuxer() {} | 453 FFmpegDemuxer::~FFmpegDemuxer() {} |
429 | 454 |
430 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 455 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
431 DCHECK(task_runner_->BelongsToCurrentThread()); | 456 DCHECK(task_runner_->BelongsToCurrentThread()); |
432 url_protocol_->Abort(); | 457 url_protocol_->Abort(); |
433 data_source_->Stop( | 458 data_source_->Stop( |
434 BindToCurrentLoop(base::Bind(&FFmpegDemuxer::OnDataSourceStopped, | 459 BindToCurrentLoop(base::Bind(&FFmpegDemuxer::OnDataSourceStopped, |
435 weak_factory_.GetWeakPtr(), | 460 weak_factory_.GetWeakPtr(), |
436 BindToCurrentLoop(callback)))); | 461 BindToCurrentLoop(callback)))); |
437 data_source_ = NULL; | 462 data_source_ = NULL; |
438 } | 463 } |
439 | 464 |
440 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 465 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
441 DCHECK(task_runner_->BelongsToCurrentThread()); | 466 DCHECK(task_runner_->BelongsToCurrentThread()); |
442 CHECK(!pending_seek_); | 467 CHECK(!pending_seek_); |
443 | 468 |
444 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 469 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
445 // otherwise we can end up waiting for a pre-seek read to complete even though | 470 // otherwise we can end up waiting for a pre-seek read to complete even though |
446 // we know we're going to drop it on the floor. | 471 // we know we're going to drop it on the floor. |
447 | 472 |
473 const AVStream* seeking_stream = | |
474 glue_->format_context()->streams[stream_index_for_seeking_]; | |
475 | |
448 // Always seek to a timestamp less than or equal to the desired timestamp. | 476 // Always seek to a timestamp less than or equal to the desired timestamp. |
449 int flags = AVSEEK_FLAG_BACKWARD; | 477 const int flags = AVSEEK_FLAG_BACKWARD; |
acolwell GONE FROM CHROMIUM
2014/06/10 23:20:38
Why not simply inline this below?
DaleCurtis
2014/06/12 00:21:55
Done.
| |
450 | 478 |
451 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | |
452 // will attempt to use the lowest-index video stream, if present, followed by | |
453 // the lowest-index audio stream. | |
454 pending_seek_ = true; | 479 pending_seek_ = true; |
455 base::PostTaskAndReplyWithResult( | 480 base::PostTaskAndReplyWithResult( |
456 blocking_thread_.message_loop_proxy().get(), | 481 blocking_thread_.message_loop_proxy().get(), |
457 FROM_HERE, | 482 FROM_HERE, |
458 base::Bind(&av_seek_frame, | 483 base::Bind( |
459 glue_->format_context(), | 484 &av_seek_frame, |
460 -1, | 485 glue_->format_context(), |
461 time.InMicroseconds(), | 486 stream_index_for_seeking_, |
462 flags), | 487 ConvertToTimeBase(seeking_stream->time_base, time + start_time()), |
488 flags), | |
463 base::Bind( | 489 base::Bind( |
464 &FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb)); | 490 &FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb)); |
465 } | 491 } |
466 | 492 |
467 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 493 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
468 const PipelineStatusCB& status_cb, | 494 const PipelineStatusCB& status_cb, |
469 bool enable_text_tracks) { | 495 bool enable_text_tracks) { |
470 DCHECK(task_runner_->BelongsToCurrentThread()); | 496 DCHECK(task_runner_->BelongsToCurrentThread()); |
471 host_ = host; | 497 host_ = host; |
472 text_enabled_ = enable_text_tracks; | 498 text_enabled_ = enable_text_tracks; |
(...skipping 28 matching lines...) Expand all Loading... | |
501 DemuxerStream::Type type) const { | 527 DemuxerStream::Type type) const { |
502 StreamVector::const_iterator iter; | 528 StreamVector::const_iterator iter; |
503 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 529 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
504 if (*iter && (*iter)->type() == type) { | 530 if (*iter && (*iter)->type() == type) { |
505 return *iter; | 531 return *iter; |
506 } | 532 } |
507 } | 533 } |
508 return NULL; | 534 return NULL; |
509 } | 535 } |
510 | 536 |
511 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | |
512 DCHECK(task_runner_->BelongsToCurrentThread()); | |
513 return start_time_; | |
514 } | |
515 | |
516 base::Time FFmpegDemuxer::GetTimelineOffset() const { | 537 base::Time FFmpegDemuxer::GetTimelineOffset() const { |
517 return timeline_offset_; | 538 return timeline_offset_; |
518 } | 539 } |
519 | 540 |
520 Demuxer::Liveness FFmpegDemuxer::GetLiveness() const { | 541 Demuxer::Liveness FFmpegDemuxer::GetLiveness() const { |
521 DCHECK(task_runner_->BelongsToCurrentThread()); | 542 DCHECK(task_runner_->BelongsToCurrentThread()); |
522 return liveness_; | 543 return liveness_; |
523 } | 544 } |
524 | 545 |
525 void FFmpegDemuxer::AddTextStreams() { | 546 void FFmpegDemuxer::AddTextStreams() { |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
626 AudioDecoderConfig audio_config; | 647 AudioDecoderConfig audio_config; |
627 | 648 |
628 AVStream* video_stream = NULL; | 649 AVStream* video_stream = NULL; |
629 VideoDecoderConfig video_config; | 650 VideoDecoderConfig video_config; |
630 | 651 |
631 base::TimeDelta max_duration; | 652 base::TimeDelta max_duration; |
632 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 653 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
633 AVStream* stream = format_context->streams[i]; | 654 AVStream* stream = format_context->streams[i]; |
634 AVCodecContext* codec_context = stream->codec; | 655 AVCodecContext* codec_context = stream->codec; |
635 AVMediaType codec_type = codec_context->codec_type; | 656 AVMediaType codec_type = codec_context->codec_type; |
657 bool discard_negative_timestamps = false; | |
636 | 658 |
637 if (codec_type == AVMEDIA_TYPE_AUDIO) { | 659 if (codec_type == AVMEDIA_TYPE_AUDIO) { |
638 if (audio_stream) | 660 if (audio_stream) |
639 continue; | 661 continue; |
640 | 662 |
641 // Log the codec detected, whether it is supported or not. | 663 // Log the codec detected, whether it is supported or not. |
642 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", | 664 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", |
643 codec_context->codec_id); | 665 codec_context->codec_id); |
644 // Ensure the codec is supported. IsValidConfig() also checks that the | 666 // Ensure the codec is supported. IsValidConfig() also checks that the |
645 // channel layout and sample format are valid. | 667 // channel layout and sample format are valid. |
646 AVStreamToAudioDecoderConfig(stream, &audio_config, false); | 668 AVStreamToAudioDecoderConfig(stream, &audio_config, false); |
647 if (!audio_config.IsValidConfig()) | 669 if (!audio_config.IsValidConfig()) |
648 continue; | 670 continue; |
649 audio_stream = stream; | 671 audio_stream = stream; |
672 | |
673 const base::TimeDelta audio_start_time = ExtractStartTime(stream); | |
674 if (audio_start_time != kNoTimestamp() && | |
675 audio_start_time < start_time_) { | |
676 stream_index_for_seeking_ = i; | |
677 start_time_ = audio_start_time; | |
678 } | |
679 | |
680 // Enable post-decode frame dropping for packets with negative timestamps | |
681 // as outlined in section A.2 in the Ogg Vorbis spec: | |
682 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html | |
683 discard_negative_timestamps = | |
684 audio_config.codec() == kCodecVorbis && | |
685 strcmp(glue_->format_context()->iformat->name, "ogg") == 0; | |
650 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { | 686 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
651 if (video_stream) | 687 if (video_stream) |
652 continue; | 688 continue; |
653 | 689 |
654 // Log the codec detected, whether it is supported or not. | 690 // Log the codec detected, whether it is supported or not. |
655 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", | 691 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", |
656 codec_context->codec_id); | 692 codec_context->codec_id); |
657 // Ensure the codec is supported. IsValidConfig() also checks that the | 693 // Ensure the codec is supported. IsValidConfig() also checks that the |
658 // frame size and visible size are valid. | 694 // frame size and visible size are valid. |
659 AVStreamToVideoDecoderConfig(stream, &video_config, false); | 695 AVStreamToVideoDecoderConfig(stream, &video_config, false); |
660 | 696 |
661 if (!video_config.IsValidConfig()) | 697 if (!video_config.IsValidConfig()) |
662 continue; | 698 continue; |
663 video_stream = stream; | 699 video_stream = stream; |
700 | |
701 // Find the video start time. Note the <= comparison causes the video | |
702 // stream to be preferred for seeking when start times match. | |
703 const base::TimeDelta video_start_time = ExtractStartTime(stream); | |
704 if (video_start_time != kNoTimestamp() && | |
705 video_start_time <= start_time_) { | |
706 stream_index_for_seeking_ = i; | |
707 start_time_ = video_start_time; | |
708 } | |
664 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { | 709 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { |
665 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { | 710 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { |
666 continue; | 711 continue; |
667 } | 712 } |
668 } else { | 713 } else { |
669 continue; | 714 continue; |
670 } | 715 } |
671 | 716 |
672 streams_[i] = new FFmpegDemuxerStream(this, stream); | 717 streams_[i] = new FFmpegDemuxerStream(this, stream); |
673 max_duration = std::max(max_duration, streams_[i]->duration()); | 718 max_duration = std::max(max_duration, streams_[i]->duration()); |
674 | 719 if (discard_negative_timestamps) |
675 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 720 streams_[i]->enable_negative_timestamp_discard(); |
acolwell GONE FROM CHROMIUM
2014/06/10 23:20:38
nit: Just pass this via the constructor 2 lines up
DaleCurtis
2014/06/12 00:21:55
Done.
| |
676 const base::TimeDelta first_dts = ConvertFromTimeBase( | |
677 stream->time_base, stream->first_dts); | |
678 if (start_time_ == kNoTimestamp() || first_dts < start_time_) | |
679 start_time_ = first_dts; | |
680 } | |
681 } | 721 } |
682 | 722 |
683 if (!audio_stream && !video_stream) { | 723 if (!audio_stream && !video_stream) { |
684 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | 724 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
685 return; | 725 return; |
686 } | 726 } |
687 | 727 |
688 if (text_enabled_) | 728 if (text_enabled_) |
689 AddTextStreams(); | 729 AddTextStreams(); |
690 | 730 |
691 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 731 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
692 // If there is a duration value in the container use that to find the | 732 // If there is a duration value in the container use that to find the |
693 // maximum between it and the duration from A/V streams. | 733 // maximum between it and the duration from A/V streams. |
694 const AVRational av_time_base = {1, AV_TIME_BASE}; | 734 const AVRational av_time_base = {1, AV_TIME_BASE}; |
695 max_duration = | 735 max_duration = |
696 std::max(max_duration, | 736 std::max(max_duration, |
697 ConvertFromTimeBase(av_time_base, format_context->duration)); | 737 ConvertFromTimeBase(av_time_base, format_context->duration)); |
698 } else { | 738 } else { |
699 // The duration is unknown, in which case this is likely a live stream. | 739 // The duration is unknown, in which case this is likely a live stream. |
700 max_duration = kInfiniteDuration(); | 740 max_duration = kInfiniteDuration(); |
701 } | 741 } |
702 | 742 |
703 // Some demuxers, like WAV, do not put timestamps on their frames. We | 743 // If no start time could be determined, default to zero and prefer the video |
704 // assume the the start time is 0. | 744 // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
705 if (start_time_ == kNoTimestamp()) | 745 // put timestamps on its frames. |
746 if (start_time_ == kInfiniteDuration()) { | |
706 start_time_ = base::TimeDelta(); | 747 start_time_ = base::TimeDelta(); |
acolwell GONE FROM CHROMIUM
2014/06/10 23:20:38
If we don't know at this point, shouldn't we just
DaleCurtis
2014/06/11 16:59:07
Maybe. Couldn't a seek happen before the first fr
acolwell GONE FROM CHROMIUM
2014/06/11 17:40:24
A seek can't happen until we transition to HAVE_ME
DaleCurtis
2014/06/12 00:21:55
This is not easy to do since the preroll decision
| |
748 stream_index_for_seeking_ = | |
749 video_stream ? video_stream->index : audio_stream->index; | |
750 } | |
707 | 751 |
708 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS | 752 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS |
709 // generation so we always get timestamps, see http://crbug.com/169570 | 753 // generation so we always get timestamps, see http://crbug.com/169570 |
710 if (strcmp(format_context->iformat->name, "avi") == 0) | 754 if (strcmp(format_context->iformat->name, "avi") == 0) |
711 format_context->flags |= AVFMT_FLAG_GENPTS; | 755 format_context->flags |= AVFMT_FLAG_GENPTS; |
712 | 756 |
713 timeline_offset_ = ExtractTimelineOffset(format_context); | 757 timeline_offset_ = ExtractTimelineOffset(format_context); |
714 | 758 |
759 // Since we're shifting the externally visible start time to zero, we need to | |
760 // adjust the timeline offset to compensate. | |
761 if (!timeline_offset_.is_null()) | |
762 timeline_offset_ += start_time_; | |
763 | |
715 if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) { | 764 if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) { |
716 liveness_ = LIVENESS_LIVE; | 765 liveness_ = LIVENESS_LIVE; |
717 } else if (max_duration != kInfiniteDuration()) { | 766 } else if (max_duration != kInfiniteDuration()) { |
718 liveness_ = LIVENESS_RECORDED; | 767 liveness_ = LIVENESS_RECORDED; |
719 } else { | 768 } else { |
720 liveness_ = LIVENESS_UNKNOWN; | 769 liveness_ = LIVENESS_UNKNOWN; |
721 } | 770 } |
722 | 771 |
723 // Good to go: set the duration and bitrate and notify we're done | 772 // Good to go: set the duration and bitrate and notify we're done |
724 // initializing. | 773 // initializing. |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
776 video_codec->time_base.num, | 825 video_codec->time_base.num, |
777 video_codec->time_base.den)); | 826 video_codec->time_base.den)); |
778 media_log_->SetStringProperty( | 827 media_log_->SetStringProperty( |
779 "video_format", VideoFrame::FormatToString(video_config.format())); | 828 "video_format", VideoFrame::FormatToString(video_config.format())); |
780 media_log_->SetBooleanProperty("video_is_encrypted", | 829 media_log_->SetBooleanProperty("video_is_encrypted", |
781 video_config.is_encrypted()); | 830 video_config.is_encrypted()); |
782 } else { | 831 } else { |
783 media_log_->SetBooleanProperty("found_video_stream", false); | 832 media_log_->SetBooleanProperty("found_video_stream", false); |
784 } | 833 } |
785 | 834 |
786 | |
787 media_log_->SetTimeProperty("max_duration", max_duration); | 835 media_log_->SetTimeProperty("max_duration", max_duration); |
788 media_log_->SetTimeProperty("start_time", start_time_); | 836 media_log_->SetTimeProperty("start_time", start_time_); |
789 media_log_->SetIntegerProperty("bitrate", bitrate_); | 837 media_log_->SetIntegerProperty("bitrate", bitrate_); |
790 | 838 |
791 status_cb.Run(PIPELINE_OK); | 839 status_cb.Run(PIPELINE_OK); |
792 } | 840 } |
793 | 841 |
794 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { | 842 void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
795 DCHECK(task_runner_->BelongsToCurrentThread()); | 843 DCHECK(task_runner_->BelongsToCurrentThread()); |
796 CHECK(pending_seek_); | 844 CHECK(pending_seek_); |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1012 } | 1060 } |
1013 for (size_t i = 0; i < buffered.size(); ++i) | 1061 for (size_t i = 0; i < buffered.size(); ++i) |
1014 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 1062 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
1015 } | 1063 } |
1016 | 1064 |
1017 void FFmpegDemuxer::OnDataSourceError() { | 1065 void FFmpegDemuxer::OnDataSourceError() { |
1018 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 1066 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
1019 } | 1067 } |
1020 | 1068 |
1021 } // namespace media | 1069 } // namespace media |
OLD | NEW |