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 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
255 } | 255 } |
256 | 256 |
257 buffer->set_duration( | 257 buffer->set_duration( |
258 ConvertStreamTimestamp(stream_->time_base, packet->duration)); | 258 ConvertStreamTimestamp(stream_->time_base, packet->duration)); |
259 | 259 |
260 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). | 260 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). |
261 const base::TimeDelta stream_timestamp = | 261 const base::TimeDelta stream_timestamp = |
262 ConvertStreamTimestamp(stream_->time_base, packet->pts); | 262 ConvertStreamTimestamp(stream_->time_base, packet->pts); |
263 | 263 |
264 if (stream_timestamp != kNoTimestamp()) { | 264 if (stream_timestamp != kNoTimestamp()) { |
265 buffer->set_timestamp(stream_timestamp - demuxer_->start_time()); | 265 // When discarding audio packets with negative timestamps, don't rebase the |
266 // video timeline against the negative starting time. | |
267 base::TimeDelta start_time = demuxer_->start_time(); | |
268 if (discard_negative_timestamps_ && type() == VIDEO && | |
acolwell GONE FROM CHROMIUM
2014/06/25 16:32:44
This seems very Ogg specific without actually ment
DaleCurtis
2014/06/27 20:52:32
I tried this a couple different ways and they're a
| |
269 start_time < base::TimeDelta()) { | |
270 DCHECK(stream_timestamp >= base::TimeDelta()); | |
271 start_time = base::TimeDelta(); | |
272 } | |
273 | |
274 buffer->set_timestamp(stream_timestamp - start_time); | |
266 | 275 |
267 // If enabled, mark packets with negative timestamps for post-decode | 276 // If enabled, mark packets with negative timestamps for post-decode |
268 // discard. | 277 // discard. |
269 if (discard_negative_timestamps_ && stream_timestamp < base::TimeDelta()) { | 278 if (discard_negative_timestamps_ && stream_timestamp < base::TimeDelta()) { |
270 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { | 279 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { |
271 // Discard the entire packet if it's entirely before zero. | 280 // Discard the entire packet if it's entirely before zero. |
272 buffer->set_discard_padding( | 281 buffer->set_discard_padding( |
273 std::make_pair(kInfiniteDuration(), base::TimeDelta())); | 282 std::make_pair(kInfiniteDuration(), base::TimeDelta())); |
274 } else { | 283 } else { |
275 // Only discard part of the frame if it overlaps zero. | 284 // Only discard part of the frame if it overlaps zero. |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
461 const scoped_refptr<MediaLog>& media_log) | 470 const scoped_refptr<MediaLog>& media_log) |
462 : host_(NULL), | 471 : host_(NULL), |
463 task_runner_(task_runner), | 472 task_runner_(task_runner), |
464 blocking_thread_("FFmpegDemuxer"), | 473 blocking_thread_("FFmpegDemuxer"), |
465 pending_read_(false), | 474 pending_read_(false), |
466 pending_seek_(false), | 475 pending_seek_(false), |
467 data_source_(data_source), | 476 data_source_(data_source), |
468 media_log_(media_log), | 477 media_log_(media_log), |
469 bitrate_(0), | 478 bitrate_(0), |
470 start_time_(kNoTimestamp()), | 479 start_time_(kNoTimestamp()), |
471 preferred_stream_for_seeking_(-1, kNoTimestamp()), | 480 preferred_stream_for_seeking_(-1, kNoTimestamp(), std::string::npos), |
472 fallback_stream_for_seeking_(-1, kNoTimestamp()), | 481 fallback_stream_for_seeking_(-1, kNoTimestamp(), std::string::npos), |
473 liveness_(LIVENESS_UNKNOWN), | 482 liveness_(LIVENESS_UNKNOWN), |
474 text_enabled_(false), | 483 text_enabled_(false), |
475 duration_known_(false), | 484 duration_known_(false), |
476 need_key_cb_(need_key_cb), | 485 need_key_cb_(need_key_cb), |
477 weak_factory_(this) { | 486 weak_factory_(this) { |
478 DCHECK(task_runner_.get()); | 487 DCHECK(task_runner_.get()); |
479 DCHECK(data_source_); | 488 DCHECK(data_source_); |
480 } | 489 } |
481 | 490 |
482 FFmpegDemuxer::~FFmpegDemuxer() {} | 491 FFmpegDemuxer::~FFmpegDemuxer() {} |
(...skipping 13 matching lines...) Expand all Loading... | |
496 CHECK(!pending_seek_); | 505 CHECK(!pending_seek_); |
497 | 506 |
498 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 507 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
499 // otherwise we can end up waiting for a pre-seek read to complete even though | 508 // otherwise we can end up waiting for a pre-seek read to complete even though |
500 // we know we're going to drop it on the floor. | 509 // we know we're going to drop it on the floor. |
501 | 510 |
502 // FFmpeg requires seeks to be adjusted according to the lowest starting time. | 511 // FFmpeg requires seeks to be adjusted according to the lowest starting time. |
503 const base::TimeDelta seek_time = time + start_time_; | 512 const base::TimeDelta seek_time = time + start_time_; |
504 | 513 |
505 // Choose the preferred stream if |seek_time| occurs after its starting time, | 514 // Choose the preferred stream if |seek_time| occurs after its starting time, |
506 // otherwise use the fallback stream. | 515 // otherwise if the fallback stream index is lower than the preferred stream |
507 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); | 516 // index, use the fallback stream index. |
508 const int stream_index = seek_time >= preferred_stream_for_seeking_.second | 517 // |
509 ? preferred_stream_for_seeking_.first | 518 // The fallback stream should always be the stream with the lowest "file" |
510 : fallback_stream_for_seeking_.first; | 519 // position, no matter what its start time is. This is necessary because |
520 // ffmpeg will start demuxing all streams from the byte position of first | |
521 // matched packet after seeking. | |
522 DCHECK(preferred_stream_for_seeking_.start_time != kNoTimestamp()); | |
523 const int stream_index = | |
524 seek_time >= preferred_stream_for_seeking_.start_time || | |
525 preferred_stream_for_seeking_.pos < | |
526 fallback_stream_for_seeking_.pos | |
527 ? preferred_stream_for_seeking_.stream_index | |
528 : fallback_stream_for_seeking_.stream_index; | |
511 DCHECK_NE(stream_index, -1); | 529 DCHECK_NE(stream_index, -1); |
512 | 530 |
513 const AVStream* seeking_stream = | 531 const AVStream* seeking_stream = |
514 glue_->format_context()->streams[stream_index]; | 532 glue_->format_context()->streams[stream_index]; |
515 | 533 |
516 pending_seek_ = true; | 534 pending_seek_ = true; |
517 base::PostTaskAndReplyWithResult( | 535 base::PostTaskAndReplyWithResult( |
518 blocking_thread_.message_loop_proxy().get(), | 536 blocking_thread_.message_loop_proxy().get(), |
519 FROM_HERE, | 537 FROM_HERE, |
520 base::Bind(&av_seek_frame, | 538 base::Bind(&av_seek_frame, |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
710 | 728 |
711 AVStream* audio_stream = NULL; | 729 AVStream* audio_stream = NULL; |
712 AudioDecoderConfig audio_config; | 730 AudioDecoderConfig audio_config; |
713 | 731 |
714 AVStream* video_stream = NULL; | 732 AVStream* video_stream = NULL; |
715 VideoDecoderConfig video_config; | 733 VideoDecoderConfig video_config; |
716 | 734 |
717 // If available, |start_time_| will be set to the lowest stream start time. | 735 // If available, |start_time_| will be set to the lowest stream start time. |
718 start_time_ = kInfiniteDuration(); | 736 start_time_ = kInfiniteDuration(); |
719 | 737 |
738 // Ogg has some peculiarities around discarding negative timestamps, so use | |
739 // this flag to setup the FFmpegDemuxerStreams appropriately. | |
740 const bool is_ogg = | |
741 strcmp(glue_->format_context()->iformat->name, "ogg") == 0; | |
742 | |
720 base::TimeDelta max_duration; | 743 base::TimeDelta max_duration; |
721 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 744 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
722 AVStream* stream = format_context->streams[i]; | 745 AVStream* stream = format_context->streams[i]; |
723 const AVCodecContext* codec_context = stream->codec; | 746 const AVCodecContext* codec_context = stream->codec; |
724 const AVMediaType codec_type = codec_context->codec_type; | 747 const AVMediaType codec_type = codec_context->codec_type; |
725 bool discard_negative_timestamps = false; | 748 bool discard_negative_timestamps = false; |
726 | 749 |
727 if (codec_type == AVMEDIA_TYPE_AUDIO) { | 750 if (codec_type == AVMEDIA_TYPE_AUDIO) { |
728 if (audio_stream) | 751 if (audio_stream) |
729 continue; | 752 continue; |
730 | 753 |
731 // Log the codec detected, whether it is supported or not. | 754 // Log the codec detected, whether it is supported or not. |
732 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", | 755 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", |
733 codec_context->codec_id); | 756 codec_context->codec_id); |
734 // Ensure the codec is supported. IsValidConfig() also checks that the | 757 // Ensure the codec is supported. IsValidConfig() also checks that the |
735 // channel layout and sample format are valid. | 758 // channel layout and sample format are valid. |
736 AVStreamToAudioDecoderConfig(stream, &audio_config, false); | 759 AVStreamToAudioDecoderConfig(stream, &audio_config, false); |
737 if (!audio_config.IsValidConfig()) | 760 if (!audio_config.IsValidConfig()) |
738 continue; | 761 continue; |
739 audio_stream = stream; | 762 audio_stream = stream; |
740 | 763 |
741 // Enable post-decode frame dropping for packets with negative timestamps | 764 // Enable post-decode frame dropping for packets with negative timestamps |
742 // as outlined in section A.2 in the Ogg Vorbis spec: | 765 // as outlined in section A.2 in the Ogg Vorbis spec: |
743 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 766 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
744 discard_negative_timestamps = | 767 discard_negative_timestamps = |
745 audio_config.codec() == kCodecVorbis && | 768 audio_config.codec() == kCodecVorbis && is_ogg; |
746 strcmp(glue_->format_context()->iformat->name, "ogg") == 0; | |
747 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { | 769 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
748 if (video_stream) | 770 if (video_stream) |
749 continue; | 771 continue; |
750 | 772 |
751 // Log the codec detected, whether it is supported or not. | 773 // Log the codec detected, whether it is supported or not. |
752 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", | 774 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", |
753 codec_context->codec_id); | 775 codec_context->codec_id); |
754 // Ensure the codec is supported. IsValidConfig() also checks that the | 776 // Ensure the codec is supported. IsValidConfig() also checks that the |
755 // frame size and visible size are valid. | 777 // frame size and visible size are valid. |
756 AVStreamToVideoDecoderConfig(stream, &video_config, false); | 778 AVStreamToVideoDecoderConfig(stream, &video_config, false); |
757 | 779 |
758 if (!video_config.IsValidConfig()) | 780 if (!video_config.IsValidConfig()) |
759 continue; | 781 continue; |
760 video_stream = stream; | 782 video_stream = stream; |
783 discard_negative_timestamps = is_ogg; | |
761 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { | 784 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { |
762 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { | 785 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { |
763 continue; | 786 continue; |
764 } | 787 } |
765 } else { | 788 } else { |
766 continue; | 789 continue; |
767 } | 790 } |
768 | 791 |
769 streams_[i] = | 792 streams_[i] = |
770 new FFmpegDemuxerStream(this, stream, discard_negative_timestamps); | 793 new FFmpegDemuxerStream(this, stream, discard_negative_timestamps); |
771 max_duration = std::max(max_duration, streams_[i]->duration()); | 794 max_duration = std::max(max_duration, streams_[i]->duration()); |
772 | 795 |
773 const base::TimeDelta start_time = | 796 const base::TimeDelta start_time = |
774 ExtractStartTime(stream, start_time_estimates[i]); | 797 ExtractStartTime(stream, start_time_estimates[i]); |
775 if (start_time == kNoTimestamp()) | 798 const bool has_start_time = start_time != kNoTimestamp(); |
799 | |
800 // Always prefer the video stream for seeking. If none exists, we'll swap | |
801 // the fallback stream with the preferred stream below. | |
802 if (codec_type == AVMEDIA_TYPE_VIDEO) { | |
803 preferred_stream_for_seeking_ = | |
804 StreamSeekInfo(i, | |
805 has_start_time ? start_time : base::TimeDelta(), | |
806 std::string::npos); | |
807 } | |
808 | |
809 if (!has_start_time) | |
776 continue; | 810 continue; |
777 | 811 |
778 if (start_time < start_time_) { | 812 if (start_time < start_time_) { |
779 start_time_ = start_time; | 813 start_time_ = start_time; |
780 | 814 |
781 // Choose the stream with the lowest starting time as the fallback stream | 815 // Choose the stream with the lowest starting time as the fallback stream |
782 // for seeking. Video should always be preferred. | 816 // for seeking. Video should always be preferred. |
783 fallback_stream_for_seeking_ = std::make_pair(i, start_time); | 817 fallback_stream_for_seeking_ = |
818 StreamSeekInfo(i, start_time, std::string::npos); | |
784 } | 819 } |
785 | |
786 // Always prefer the video stream for seeking. If none exists, we'll swap | |
787 // the fallback stream with the preferred stream below. | |
788 if (codec_type == AVMEDIA_TYPE_VIDEO) | |
789 preferred_stream_for_seeking_ = std::make_pair(i, start_time); | |
790 } | 820 } |
791 | 821 |
792 if (!audio_stream && !video_stream) { | 822 if (!audio_stream && !video_stream) { |
793 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | 823 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
794 return; | 824 return; |
795 } | 825 } |
796 | 826 |
797 if (text_enabled_) | 827 if (text_enabled_) |
798 AddTextStreams(); | 828 AddTextStreams(); |
799 | 829 |
800 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 830 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
801 // If there is a duration value in the container use that to find the | 831 // If there is a duration value in the container use that to find the |
802 // maximum between it and the duration from A/V streams. | 832 // maximum between it and the duration from A/V streams. |
803 const AVRational av_time_base = {1, AV_TIME_BASE}; | 833 const AVRational av_time_base = {1, AV_TIME_BASE}; |
804 max_duration = | 834 max_duration = |
805 std::max(max_duration, | 835 std::max(max_duration, |
806 ConvertFromTimeBase(av_time_base, format_context->duration)); | 836 ConvertFromTimeBase(av_time_base, format_context->duration)); |
807 } else { | 837 } else { |
808 // The duration is unknown, in which case this is likely a live stream. | 838 // The duration is unknown, in which case this is likely a live stream. |
809 max_duration = kInfiniteDuration(); | 839 max_duration = kInfiniteDuration(); |
810 } | 840 } |
811 | 841 |
812 // If no start time could be determined, default to zero and prefer the video | 842 // If no start time could be determined, default to zero and prefer the video |
813 // stream over the audio stream for seeking. E.g., The WAV demuxer does not | 843 // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
814 // put timestamps on its frames. | 844 // put timestamps on its frames. |
815 if (start_time_ == kInfiniteDuration()) { | 845 if (start_time_ == kInfiniteDuration()) { |
816 start_time_ = base::TimeDelta(); | 846 start_time_ = base::TimeDelta(); |
817 preferred_stream_for_seeking_ = std::make_pair( | 847 preferred_stream_for_seeking_ = |
818 video_stream ? video_stream->index : audio_stream->index, start_time_); | 848 StreamSeekInfo(video_stream ? video_stream->index : audio_stream->index, |
849 start_time_, | |
850 std::string::npos); | |
819 } else if (!video_stream) { | 851 } else if (!video_stream) { |
820 // If no video stream exists, use the audio or text stream found above. | 852 // If no video stream exists, use the audio or text stream found above. |
821 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; | 853 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; |
822 } | 854 } |
823 | 855 |
824 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS | 856 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS |
825 // generation so we always get timestamps, see http://crbug.com/169570 | 857 // generation so we always get timestamps, see http://crbug.com/169570 |
826 if (strcmp(format_context->iformat->name, "avi") == 0) | 858 if (strcmp(format_context->iformat->name, "avi") == 0) |
827 format_context->flags |= AVFMT_FLAG_GENPTS; | 859 format_context->flags |= AVFMT_FLAG_GENPTS; |
828 | 860 |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1010 } | 1042 } |
1011 | 1043 |
1012 // Queue the packet with the appropriate stream. | 1044 // Queue the packet with the appropriate stream. |
1013 DCHECK_GE(packet->stream_index, 0); | 1045 DCHECK_GE(packet->stream_index, 0); |
1014 DCHECK_LT(packet->stream_index, static_cast<int>(streams_.size())); | 1046 DCHECK_LT(packet->stream_index, static_cast<int>(streams_.size())); |
1015 | 1047 |
1016 // Defend against ffmpeg giving us a bad stream index. | 1048 // Defend against ffmpeg giving us a bad stream index. |
1017 if (packet->stream_index >= 0 && | 1049 if (packet->stream_index >= 0 && |
1018 packet->stream_index < static_cast<int>(streams_.size()) && | 1050 packet->stream_index < static_cast<int>(streams_.size()) && |
1019 streams_[packet->stream_index]) { | 1051 streams_[packet->stream_index]) { |
1052 // Update seeking information based on packets with the lowest "file" | |
1053 // positions in the container. Sadly this can't be done during | |
1054 // avformat_find_stream_info() since FFmpeg doesn't consistently expose the | |
1055 // packets it parsed during probing. | |
1056 if (packet->pos >= 0) { | |
1057 const size_t packet_pos = static_cast<size_t>(packet->pos); | |
1058 if (preferred_stream_for_seeking_.stream_index == packet->stream_index) { | |
1059 if (packet_pos < preferred_stream_for_seeking_.pos) | |
1060 preferred_stream_for_seeking_.pos = packet_pos; | |
1061 } else if (packet_pos < fallback_stream_for_seeking_.pos) { | |
1062 fallback_stream_for_seeking_.stream_index = packet->stream_index; | |
1063 fallback_stream_for_seeking_.pos = packet_pos; | |
1064 // The start_time is unused for the fallback stream. | |
1065 fallback_stream_for_seeking_.start_time = kNoTimestamp(); | |
1066 } | |
1067 } | |
1068 | |
1020 // TODO(scherkus): Fix demuxing upstream to never return packets w/o data | 1069 // TODO(scherkus): Fix demuxing upstream to never return packets w/o data |
1021 // when av_read_frame() returns success code. See bug comment for ideas: | 1070 // when av_read_frame() returns success code. See bug comment for ideas: |
1022 // | 1071 // |
1023 // https://code.google.com/p/chromium/issues/detail?id=169133#c10 | 1072 // https://code.google.com/p/chromium/issues/detail?id=169133#c10 |
1024 if (!packet->data) { | 1073 if (!packet->data) { |
1025 ScopedAVPacket new_packet(new AVPacket()); | 1074 ScopedAVPacket new_packet(new AVPacket()); |
1026 av_new_packet(new_packet.get(), 0); | 1075 av_new_packet(new_packet.get(), 0); |
1027 av_packet_copy_props(new_packet.get(), packet.get()); | 1076 av_packet_copy_props(new_packet.get(), packet.get()); |
1028 packet.swap(new_packet); | 1077 packet.swap(new_packet); |
1029 } | 1078 } |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1134 } | 1183 } |
1135 for (size_t i = 0; i < buffered.size(); ++i) | 1184 for (size_t i = 0; i < buffered.size(); ++i) |
1136 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 1185 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
1137 } | 1186 } |
1138 | 1187 |
1139 void FFmpegDemuxer::OnDataSourceError() { | 1188 void FFmpegDemuxer::OnDataSourceError() { |
1140 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 1189 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
1141 } | 1190 } |
1142 | 1191 |
1143 } // namespace media | 1192 } // namespace media |
OLD | NEW |