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 <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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |