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

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

Issue 2855373002: Use ffmpeg packet.pos for restarting reading after reenabling video
Patch Set: Extract some more seek logic and add comments for SeekInternal Created 3 years, 7 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
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 <memory> 8 #include <memory>
9 #include <set> 9 #include <set>
10 #include <utility> 10 #include <utility>
(...skipping 849 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 stopped_(false), 860 stopped_(false),
861 pending_read_(false), 861 pending_read_(false),
862 data_source_(data_source), 862 data_source_(data_source),
863 media_log_(media_log), 863 media_log_(media_log),
864 bitrate_(0), 864 bitrate_(0),
865 start_time_(kNoTimestamp), 865 start_time_(kNoTimestamp),
866 text_enabled_(false), 866 text_enabled_(false),
867 duration_known_(false), 867 duration_known_(false),
868 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), 868 encrypted_media_init_data_cb_(encrypted_media_init_data_cb),
869 media_tracks_updated_cb_(media_tracks_updated_cb), 869 media_tracks_updated_cb_(media_tracks_updated_cb),
870 last_packet_pos_(-1),
871 restarting_stream_(nullptr),
870 cancel_pending_seek_factory_(this), 872 cancel_pending_seek_factory_(this),
871 weak_factory_(this) { 873 weak_factory_(this) {
872 DCHECK(task_runner_.get()); 874 DCHECK(task_runner_.get());
873 DCHECK(data_source_); 875 DCHECK(data_source_);
874 DCHECK(!media_tracks_updated_cb_.is_null()); 876 DCHECK(!media_tracks_updated_cb_.is_null());
875 } 877 }
876 878
877 FFmpegDemuxer::~FFmpegDemuxer() { 879 FFmpegDemuxer::~FFmpegDemuxer() {
878 // NOTE: This class is not destroyed on |task_runner|, so we must ensure that 880 // NOTE: This class is not destroyed on |task_runner|, so we must ensure that
879 // there are no outstanding WeakPtrs by the time we reach here. 881 // there are no outstanding WeakPtrs by the time we reach here.
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
991 } else { 993 } else {
992 // Don't use GetWeakPtr() here since we are on the wrong thread. 994 // Don't use GetWeakPtr() here since we are on the wrong thread.
993 task_runner_->PostTask( 995 task_runner_->PostTask(
994 FROM_HERE, base::Bind(&FFmpegDemuxer::AbortPendingReads, weak_this_)); 996 FROM_HERE, base::Bind(&FFmpegDemuxer::AbortPendingReads, weak_this_));
995 } 997 }
996 } 998 }
997 999
998 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { 1000 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
999 DCHECK(task_runner_->BelongsToCurrentThread()); 1001 DCHECK(task_runner_->BelongsToCurrentThread());
1000 CHECK(pending_seek_cb_.is_null()); 1002 CHECK(pending_seek_cb_.is_null());
1001
1002 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
1003 // Since EnqueuePacket() rebased negative timestamps by the start time, we
1004 // must correct the shift here.
1005 //
1006 // Additionally, to workaround limitations in how we expose seekable ranges to
1007 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
1008 // start time to the start time.
1009 base::TimeDelta seek_time = start_time_ < base::TimeDelta()
1010 ? time + start_time_
1011 : time < start_time_ ? start_time_ : time;
1012
1013 // When seeking in an opus stream we need to ensure we deliver enough data to
1014 // satisfy the seek preroll; otherwise the audio at the actual seek time will
1015 // not be entirely accurate.
1016 FFmpegDemuxerStream* audio_stream =
1017 GetFirstEnabledFFmpegStream(DemuxerStream::AUDIO);
1018 if (audio_stream) {
1019 const AudioDecoderConfig& config = audio_stream->audio_decoder_config();
1020 if (config.codec() == kCodecOpus)
1021 seek_time = std::max(start_time_, seek_time - config.seek_preroll());
1022 }
1023
1024 // Choose the seeking stream based on whether it contains the seek time, if no
1025 // match can be found prefer the preferred stream.
1026 //
1027 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a
1028 // given container will demux all packets after the seek point. Instead it
1029 // only guarantees that all packets after the file position of the seek will
1030 // be demuxed. It's an open question whether FFmpeg should fix this:
1031 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
1032 // Tracked by http://crbug.com/387996.
1033 FFmpegDemuxerStream* demux_stream = FindPreferredStreamForSeeking(seek_time);
1034 DCHECK(demux_stream);
1035 const AVStream* seeking_stream = demux_stream->av_stream();
1036 DCHECK(seeking_stream);
1037
1038 pending_seek_cb_ = cb; 1003 pending_seek_cb_ = cb;
1039 base::PostTaskAndReplyWithResult( 1004 SeekInternal(
1040 blocking_task_runner_.get(), FROM_HERE, 1005 time, nullptr,
1041 base::Bind(&av_seek_frame, glue_->format_context(), seeking_stream->index,
1042 ConvertToTimeBase(seeking_stream->time_base, seek_time),
1043 // Always seek to a timestamp <= to the desired timestamp.
1044 AVSEEK_FLAG_BACKWARD),
1045 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr())); 1006 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr()));
1046 } 1007 }
1047 1008
1048 base::Time FFmpegDemuxer::GetTimelineOffset() const { 1009 base::Time FFmpegDemuxer::GetTimelineOffset() const {
1049 return timeline_offset_; 1010 return timeline_offset_;
1050 } 1011 }
1051 1012
1052 std::vector<DemuxerStream*> FFmpegDemuxer::GetAllStreams() { 1013 std::vector<DemuxerStream*> FFmpegDemuxer::GetAllStreams() {
1053 DCHECK(task_runner_->BelongsToCurrentThread()); 1014 DCHECK(task_runner_->BelongsToCurrentThread());
1054 std::vector<DemuxerStream*> result; 1015 std::vector<DemuxerStream*> result;
(...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after
1648 return; 1609 return;
1649 } 1610 }
1650 1611
1651 if (result < 0) { 1612 if (result < 0) {
1652 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being 1613 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being
1653 // captured from stdout and contaminates testing. 1614 // captured from stdout and contaminates testing.
1654 // TODO(scherkus): Implement this properly and signal error (BUG=23447). 1615 // TODO(scherkus): Implement this properly and signal error (BUG=23447).
1655 VLOG(1) << "Not implemented"; 1616 VLOG(1) << "Not implemented";
1656 } 1617 }
1657 1618
1619 last_packet_pos_ = -1;
1620
1658 // Tell streams to flush buffers due to seeking. 1621 // Tell streams to flush buffers due to seeking.
1659 for (const auto& stream : streams_) { 1622 for (const auto& stream : streams_) {
1660 if (stream) 1623 if (stream)
1661 stream->FlushBuffers(); 1624 stream->FlushBuffers();
1662 } 1625 }
1663 1626
1664 // Resume reading until capacity. 1627 // Resume reading until capacity.
1665 ReadFrameIfNeeded(); 1628 ReadFrameIfNeeded();
1666 1629
1667 // Notify we're finished seeking. 1630 // Notify we're finished seeking.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1721 for (const auto& stream : streams_) { 1684 for (const auto& stream : streams_) {
1722 if (stream && stream->type() == DemuxerStream::VIDEO && 1685 if (stream && stream->type() == DemuxerStream::VIDEO &&
1723 stream.get() != selected_stream) { 1686 stream.get() != selected_stream) {
1724 DVLOG(1) << __func__ << ": disabling stream " << stream.get(); 1687 DVLOG(1) << __func__ << ": disabling stream " << stream.get();
1725 stream->SetEnabled(false, curr_time); 1688 stream->SetEnabled(false, curr_time);
1726 } 1689 }
1727 } 1690 }
1728 if (selected_stream) { 1691 if (selected_stream) {
1729 DVLOG(1) << __func__ << ": enabling stream " << selected_stream; 1692 DVLOG(1) << __func__ << ": enabling stream " << selected_stream;
1730 selected_stream->SetEnabled(true, curr_time); 1693 selected_stream->SetEnabled(true, curr_time);
1694 selected_stream->FlushBuffers();
1695 SeekInternal(curr_time, selected_stream,
wolenetz 2017/05/08 20:55:33 could we already be in the middle of either a norm
servolk 2017/05/12 00:55:37 Yeah, I looked closer at how FFmpegDemuxer is call
1696 base::Bind(&FFmpegDemuxer::OnSeekDoneForRestartingStream,
1697 weak_factory_.GetWeakPtr(), selected_stream));
1731 } 1698 }
1732 } 1699 }
1733 1700
1701 void FFmpegDemuxer::OnSeekDoneForRestartingStream(FFmpegDemuxerStream* stream,
wolenetz 2017/05/08 20:55:33 What if ::Stop() occurs while this seek was in pro
servolk 2017/05/12 00:55:37 I believe the destruction of FFmpegDemuxer should
1702 int result) {
1703 DCHECK(task_runner_->BelongsToCurrentThread());
1704 if (result < 0) {
1705 DVLOG(1) << __func__ << ": seek failed: " << AVErrorToString(result);
sandersd (OOO until July 31) 2017/05/10 22:27:49 Looks like this should probably be logged to the m
servolk 2017/05/12 00:55:37 Done.
1706 return;
1707 }
1708 DVLOG(2) << __func__
1709 << ": will drop packets until last_packet_pos_=" << last_packet_pos_;
1710 if (restarting_stream_)
1711 restarting_stream_->FlushBuffers();
1712 restarting_stream_ = stream;
1713 }
1714
1734 void FFmpegDemuxer::ReadFrameIfNeeded() { 1715 void FFmpegDemuxer::ReadFrameIfNeeded() {
1735 DCHECK(task_runner_->BelongsToCurrentThread()); 1716 DCHECK(task_runner_->BelongsToCurrentThread());
1736 1717
1737 // Make sure we have work to do before reading. 1718 // Make sure we have work to do before reading.
1738 if (stopped_ || !StreamsHaveAvailableCapacity() || pending_read_ || 1719 if (stopped_ || !StreamsHaveAvailableCapacity() || pending_read_ ||
1739 !pending_seek_cb_.is_null()) { 1720 !pending_seek_cb_.is_null()) {
1740 return; 1721 return;
1741 } 1722 }
1742 1723
1743 // Allocate and read an AVPacket from the media. Save |packet_ptr| since 1724 // Allocate and read an AVPacket from the media. Save |packet_ptr| since
1744 // evaluation order of packet.get() and base::Passed(&packet) is 1725 // evaluation order of packet.get() and base::Passed(&packet) is
1745 // undefined. 1726 // undefined.
1746 ScopedAVPacket packet(new AVPacket()); 1727 ScopedAVPacket packet(new AVPacket());
1747 AVPacket* packet_ptr = packet.get(); 1728 AVPacket* packet_ptr = packet.get();
1748 1729
1749 pending_read_ = true; 1730 pending_read_ = true;
1750 base::PostTaskAndReplyWithResult( 1731 base::PostTaskAndReplyWithResult(
1751 blocking_task_runner_.get(), FROM_HERE, 1732 blocking_task_runner_.get(), FROM_HERE,
1752 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), 1733 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr),
1753 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_factory_.GetWeakPtr(), 1734 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_factory_.GetWeakPtr(),
1754 base::Passed(&packet))); 1735 base::Passed(&packet)));
1755 } 1736 }
1756 1737
1757 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { 1738 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) {
1758 DCHECK(task_runner_->BelongsToCurrentThread()); 1739 DCHECK(task_runner_->BelongsToCurrentThread());
1759 DCHECK(pending_read_); 1740 DCHECK(pending_read_);
1760 pending_read_ = false; 1741 pending_read_ = false;
1761 1742
1762 if (stopped_ || !pending_seek_cb_.is_null()) 1743 if (stopped_ || !pending_seek_cb_.is_null())
wolenetz 2017/05/08 20:55:33 probably need to also cancel the read result that
1763 return; 1744 return;
1764 1745
1765 // Consider the stream as ended if: 1746 // Consider the stream as ended if:
1766 // - either underlying ffmpeg returned an error 1747 // - either underlying ffmpeg returned an error
1767 // - or FFMpegDemuxer reached the maximum allowed memory usage. 1748 // - or FFMpegDemuxer reached the maximum allowed memory usage.
1768 if (result < 0 || IsMaxMemoryUsageReached()) { 1749 if (result < 0 || IsMaxMemoryUsageReached()) {
1769 if (result < 0) { 1750 if (result < 0) {
1770 MEDIA_LOG(DEBUG, media_log_) 1751 MEDIA_LOG(DEBUG, media_log_)
1771 << GetDisplayName() 1752 << GetDisplayName()
1772 << ": av_read_frame(): " << AVErrorToString(result); 1753 << ": av_read_frame(): " << AVErrorToString(result);
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1808 // 1789 //
1809 // https://code.google.com/p/chromium/issues/detail?id=169133#c10 1790 // https://code.google.com/p/chromium/issues/detail?id=169133#c10
1810 if (!packet->data) { 1791 if (!packet->data) {
1811 ScopedAVPacket new_packet(new AVPacket()); 1792 ScopedAVPacket new_packet(new AVPacket());
1812 av_new_packet(new_packet.get(), 0); 1793 av_new_packet(new_packet.get(), 0);
1813 av_packet_copy_props(new_packet.get(), packet.get()); 1794 av_packet_copy_props(new_packet.get(), packet.get());
1814 packet.swap(new_packet); 1795 packet.swap(new_packet);
1815 } 1796 }
1816 1797
1817 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index].get(); 1798 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index].get();
1818 if (demuxer_stream->IsEnabled()) 1799
1800 // If |restarting_stream_| is not null, we are in stream restart mode, which
1801 // means we seeked back the ffmpeg reading position and now we need to drop
1802 // packets from other streams until we reach the previously seen read
1803 // position |last_packet_pos_|.
1804 bool drop_seen_packet =
1805 restarting_stream_ && demuxer_stream != restarting_stream_ &&
1806 last_packet_pos_ >= 0 && packet.get()->pos <= last_packet_pos_;
wolenetz 2017/05/08 20:55:33 Is pos non-decreasing (during normal sequence of r
servolk 2017/05/12 00:55:37 IIUC the answer is yes to both questions. AVPacket
1807 if (drop_seen_packet) {
1808 DVLOG(4) << "Dropping already seen packet packet: pos="
1809 << packet.get()->pos;
1810 } else if (restarting_stream_ && demuxer_stream != restarting_stream_) {
1811 DVLOG(2) << "Restarting reading packets: pos=" << packet.get()->pos;
1812 restarting_stream_ = nullptr;
1813 }
1814
1815 if (!restarting_stream_)
1816 last_packet_pos_ = packet.get()->pos;
1817
1818 if (demuxer_stream->IsEnabled() && !drop_seen_packet)
1819 demuxer_stream->EnqueuePacket(std::move(packet)); 1819 demuxer_stream->EnqueuePacket(std::move(packet));
1820 1820
1821 // If duration estimate was incorrect, update it and tell higher layers. 1821 // If duration estimate was incorrect, update it and tell higher layers.
1822 if (duration_known_) { 1822 if (duration_known_) {
1823 const base::TimeDelta duration = demuxer_stream->duration(); 1823 const base::TimeDelta duration = demuxer_stream->duration();
1824 if (duration != kNoTimestamp && duration > duration_) { 1824 if (duration != kNoTimestamp && duration > duration_) {
1825 duration_ = duration; 1825 duration_ = duration;
1826 host_->SetDuration(duration_); 1826 host_->SetDuration(duration_);
1827 } 1827 }
1828 } 1828 }
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1880 } 1880 }
1881 1881
1882 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { 1882 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) {
1883 DCHECK(task_runner_->BelongsToCurrentThread()); 1883 DCHECK(task_runner_->BelongsToCurrentThread());
1884 for (const auto& stream : streams_) { 1884 for (const auto& stream : streams_) {
1885 if (stream) 1885 if (stream)
1886 stream->SetLiveness(liveness); 1886 stream->SetLiveness(liveness);
1887 } 1887 }
1888 } 1888 }
1889 1889
1890 void FFmpegDemuxer::SeekInternal(base::TimeDelta time,
1891 FFmpegDemuxerStream* preferred_stream,
1892 const FFmpegSeekDoneCB& ffmpeg_seek_done_cb) {
1893 DCHECK(task_runner_->BelongsToCurrentThread());
1894
1895 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
1896 // Since EnqueuePacket() rebased negative timestamps by the start time, we
1897 // must correct the shift here.
1898 //
1899 // Additionally, to workaround limitations in how we expose seekable ranges to
1900 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
1901 // start time to the start time.
1902 base::TimeDelta seek_time = start_time_ < base::TimeDelta()
1903 ? time + start_time_
1904 : time < start_time_ ? start_time_ : time;
1905
1906 // When seeking in an opus stream we need to ensure we deliver enough data to
1907 // satisfy the seek preroll; otherwise the audio at the actual seek time will
1908 // not be entirely accurate.
1909 FFmpegDemuxerStream* audio_stream =
1910 GetFirstEnabledFFmpegStream(DemuxerStream::AUDIO);
1911 if (audio_stream) {
1912 const AudioDecoderConfig& config = audio_stream->audio_decoder_config();
1913 if (config.codec() == kCodecOpus)
1914 seek_time = std::max(start_time_, seek_time - config.seek_preroll());
1915 }
1916
1917 if (!preferred_stream) {
1918 // Choose the seeking stream based on whether it contains the seek time, if
1919 // no match can be found prefer the preferred stream.
1920 //
1921 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a
1922 // given container will demux all packets after the seek point. Instead it
1923 // only guarantees that all packets after the file position of the seek will
1924 // be demuxed. It's an open question whether FFmpeg should fix this:
1925 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
1926 // Tracked by http://crbug.com/387996.
1927 preferred_stream = FindPreferredStreamForSeeking(seek_time);
1928 DCHECK(preferred_stream);
1929 }
1930 const AVStream* ffmpeg_stream = preferred_stream->av_stream();
1931 DCHECK(ffmpeg_stream);
1932
1933 base::PostTaskAndReplyWithResult(
1934 blocking_task_runner_.get(), FROM_HERE,
1935 base::Bind(&av_seek_frame, glue_->format_context(), ffmpeg_stream->index,
1936 ConvertToTimeBase(ffmpeg_stream->time_base, seek_time),
1937 // Always seek to a timestamp <= to the desired timestamp.
1938 AVSEEK_FLAG_BACKWARD),
1939 ffmpeg_seek_done_cb);
1940 }
1941
1890 } // namespace media 1942 } // namespace media
OLDNEW
« media/filters/ffmpeg_demuxer.h ('K') | « media/filters/ffmpeg_demuxer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698