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

Side by Side Diff: media/filters/ffmpeg_demuxer.cc

Issue 325503003: Fix seeking when the start time is non-zero. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: FFmpeg only start time. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698