Chromium Code Reviews| 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 |