| 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> |
| 11 | 11 |
| 12 #include "base/base64.h" | 12 #include "base/base64.h" |
| 13 #include "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/callback_helpers.h" | 14 #include "base/callback_helpers.h" |
| 15 #include "base/macros.h" | 15 #include "base/macros.h" |
| 16 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
| 17 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
| 18 #include "base/metrics/sparse_histogram.h" | 18 #include "base/metrics/sparse_histogram.h" |
| 19 #include "base/single_thread_task_runner.h" | 19 #include "base/single_thread_task_runner.h" |
| 20 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 21 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
| 22 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
| 23 #include "base/sys_byteorder.h" | 23 #include "base/sys_byteorder.h" |
| 24 #include "base/task_runner_util.h" | 24 #include "base/task_runner_util.h" |
| 25 #include "base/threading/sequenced_worker_pool.h" |
| 25 #include "base/threading/thread_task_runner_handle.h" | 26 #include "base/threading/thread_task_runner_handle.h" |
| 26 #include "base/time/time.h" | 27 #include "base/time/time.h" |
| 27 #include "media/audio/sample_rates.h" | 28 #include "media/audio/sample_rates.h" |
| 28 #include "media/base/bind_to_current_loop.h" | 29 #include "media/base/bind_to_current_loop.h" |
| 29 #include "media/base/decrypt_config.h" | 30 #include "media/base/decrypt_config.h" |
| 30 #include "media/base/limits.h" | 31 #include "media/base/limits.h" |
| 31 #include "media/base/media_log.h" | 32 #include "media/base/media_log.h" |
| 32 #include "media/base/media_tracks.h" | 33 #include "media/base/media_tracks.h" |
| 33 #include "media/base/timestamp_constants.h" | 34 #include "media/base/timestamp_constants.h" |
| 34 #include "media/base/video_codecs.h" | 35 #include "media/base/video_codecs.h" |
| 35 #include "media/ffmpeg/ffmpeg_common.h" | 36 #include "media/ffmpeg/ffmpeg_common.h" |
| 36 #include "media/filters/ffmpeg_aac_bitstream_converter.h" | 37 #include "media/filters/ffmpeg_aac_bitstream_converter.h" |
| 37 #include "media/filters/ffmpeg_bitstream_converter.h" | 38 #include "media/filters/ffmpeg_bitstream_converter.h" |
| 38 #include "media/filters/ffmpeg_glue.h" | 39 #include "media/filters/ffmpeg_glue.h" |
| 39 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" | 40 #include "media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h" |
| 40 #include "media/filters/webvtt_util.h" | 41 #include "media/filters/webvtt_util.h" |
| 41 #include "media/formats/webm/webm_crypto_helpers.h" | 42 #include "media/formats/webm/webm_crypto_helpers.h" |
| 42 #include "media/media_features.h" | 43 #include "media/media_features.h" |
| 43 | 44 |
| 44 #if BUILDFLAG(ENABLE_HEVC_DEMUXING) | 45 #if BUILDFLAG(ENABLE_HEVC_DEMUXING) |
| 45 #include "media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h" | 46 #include "media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h" |
| 46 #endif | 47 #endif |
| 47 | 48 |
| 48 namespace media { | 49 namespace media { |
| 49 | 50 |
| 51 static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() { |
| 52 // We cap the worker pool to 6 threads since there are at most 6 connections |
| 53 // allowed per-origin. |
| 54 // TODO(dalecurtis): This may be a bit restrictive if there are many videos |
| 55 // from different origins on a page; is this common in practice? |
| 56 // TODO(dalecurtis): Instead of being leaky, should we have a PoolManager type |
| 57 // object which drops the pool when the last FFmpegDemuxer goes away? |
| 58 static scoped_refptr<base::SequencedWorkerPool> pool( |
| 59 new base::SequencedWorkerPool(6, "FFmpegDemuxer", |
| 60 base::TaskPriority::USER_VISIBLE)); |
| 61 return pool->GetSequencedTaskRunner( |
| 62 base::SequencedWorkerPool::GetSequenceToken()); |
| 63 } |
| 64 |
| 50 static base::Time ExtractTimelineOffset(AVFormatContext* format_context) { | 65 static base::Time ExtractTimelineOffset(AVFormatContext* format_context) { |
| 51 if (strstr(format_context->iformat->name, "webm") || | 66 if (strstr(format_context->iformat->name, "webm") || |
| 52 strstr(format_context->iformat->name, "matroska")) { | 67 strstr(format_context->iformat->name, "matroska")) { |
| 53 const AVDictionaryEntry* entry = | 68 const AVDictionaryEntry* entry = |
| 54 av_dict_get(format_context->metadata, "creation_time", NULL, 0); | 69 av_dict_get(format_context->metadata, "creation_time", NULL, 0); |
| 55 | 70 |
| 56 base::Time timeline_offset; | 71 base::Time timeline_offset; |
| 57 | 72 |
| 58 // FFmpegDemuxerTests assume base::Time::FromUTCString() is used here. | 73 // FFmpegDemuxerTests assume base::Time::FromUTCString() is used here. |
| 59 if (entry != NULL && entry->value != NULL && | 74 if (entry != NULL && entry->value != NULL && |
| (...skipping 778 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 838 // FFmpegDemuxer | 853 // FFmpegDemuxer |
| 839 // | 854 // |
| 840 FFmpegDemuxer::FFmpegDemuxer( | 855 FFmpegDemuxer::FFmpegDemuxer( |
| 841 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 856 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 842 DataSource* data_source, | 857 DataSource* data_source, |
| 843 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | 858 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, |
| 844 const MediaTracksUpdatedCB& media_tracks_updated_cb, | 859 const MediaTracksUpdatedCB& media_tracks_updated_cb, |
| 845 const scoped_refptr<MediaLog>& media_log) | 860 const scoped_refptr<MediaLog>& media_log) |
| 846 : host_(NULL), | 861 : host_(NULL), |
| 847 task_runner_(task_runner), | 862 task_runner_(task_runner), |
| 848 blocking_thread_("FFmpegDemuxer"), | 863 blocking_task_runner_(GetSequencedTaskRunner()), |
| 864 stopped_(false), |
| 849 pending_read_(false), | 865 pending_read_(false), |
| 850 data_source_(data_source), | 866 data_source_(data_source), |
| 851 media_log_(media_log), | 867 media_log_(media_log), |
| 852 bitrate_(0), | 868 bitrate_(0), |
| 853 start_time_(kNoTimestamp), | 869 start_time_(kNoTimestamp), |
| 854 text_enabled_(false), | 870 text_enabled_(false), |
| 855 duration_known_(false), | 871 duration_known_(false), |
| 856 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), | 872 encrypted_media_init_data_cb_(encrypted_media_init_data_cb), |
| 857 media_tracks_updated_cb_(media_tracks_updated_cb), | 873 media_tracks_updated_cb_(media_tracks_updated_cb), |
| 858 cancel_pending_seek_factory_(this), | 874 cancel_pending_seek_factory_(this), |
| 859 weak_factory_(this) { | 875 weak_factory_(this) { |
| 860 DCHECK(task_runner_.get()); | 876 DCHECK(task_runner_.get()); |
| 861 DCHECK(data_source_); | 877 DCHECK(data_source_); |
| 862 DCHECK(!media_tracks_updated_cb_.is_null()); | 878 DCHECK(!media_tracks_updated_cb_.is_null()); |
| 863 } | 879 } |
| 864 | 880 |
| 865 FFmpegDemuxer::~FFmpegDemuxer() { | 881 FFmpegDemuxer::~FFmpegDemuxer() { |
| 866 // NOTE: This class is not destroyed on |task_runner|, so we must ensure that | 882 // NOTE: This class is not destroyed on |task_runner|, so we must ensure that |
| 867 // there are no outstanding WeakPtrs by the time we reach here. | 883 // there are no outstanding WeakPtrs by the time we reach here. |
| 868 DCHECK(!weak_factory_.HasWeakPtrs()); | 884 DCHECK(!weak_factory_.HasWeakPtrs()); |
| 885 |
| 886 // There may be outstanding tasks in the blocking pool which are trying to use |
| 887 // these members, so release them in sequence with any outstanding calls. The |
| 888 // earlier call to Abort() on |data_source_| prevents further access to it. |
| 889 blocking_task_runner_->DeleteSoon(FROM_HERE, url_protocol_.release()); |
| 890 blocking_task_runner_->DeleteSoon(FROM_HERE, glue_.release()); |
| 869 } | 891 } |
| 870 | 892 |
| 871 std::string FFmpegDemuxer::GetDisplayName() const { | 893 std::string FFmpegDemuxer::GetDisplayName() const { |
| 872 return "FFmpegDemuxer"; | 894 return "FFmpegDemuxer"; |
| 873 } | 895 } |
| 874 | 896 |
| 875 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 897 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| 876 const PipelineStatusCB& status_cb, | 898 const PipelineStatusCB& status_cb, |
| 877 bool enable_text_tracks) { | 899 bool enable_text_tracks) { |
| 878 DCHECK(task_runner_->BelongsToCurrentThread()); | 900 DCHECK(task_runner_->BelongsToCurrentThread()); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 892 // available, so add a metadata entry to ensure some is always present. | 914 // available, so add a metadata entry to ensure some is always present. |
| 893 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 915 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| 894 | 916 |
| 895 // Ensure ffmpeg doesn't give up too early while looking for stream params; | 917 // Ensure ffmpeg doesn't give up too early while looking for stream params; |
| 896 // this does not increase the amount of data downloaded. The default value | 918 // this does not increase the amount of data downloaded. The default value |
| 897 // is 5 AV_TIME_BASE units (1 second each), which prevents some oddly muxed | 919 // is 5 AV_TIME_BASE units (1 second each), which prevents some oddly muxed |
| 898 // streams from being detected properly; this value was chosen arbitrarily. | 920 // streams from being detected properly; this value was chosen arbitrarily. |
| 899 format_context->max_analyze_duration = 60 * AV_TIME_BASE; | 921 format_context->max_analyze_duration = 60 * AV_TIME_BASE; |
| 900 | 922 |
| 901 // Open the AVFormatContext using our glue layer. | 923 // Open the AVFormatContext using our glue layer. |
| 902 CHECK(blocking_thread_.Start()); | |
| 903 base::PostTaskAndReplyWithResult( | 924 base::PostTaskAndReplyWithResult( |
| 904 blocking_thread_.task_runner().get(), FROM_HERE, | 925 blocking_task_runner_.get(), FROM_HERE, |
| 905 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 926 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| 906 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_factory_.GetWeakPtr(), | 927 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_factory_.GetWeakPtr(), |
| 907 status_cb)); | 928 status_cb)); |
| 908 } | 929 } |
| 909 | 930 |
| 910 void FFmpegDemuxer::AbortPendingReads() { | 931 void FFmpegDemuxer::AbortPendingReads() { |
| 911 DCHECK(task_runner_->BelongsToCurrentThread()); | 932 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 912 | 933 |
| 913 // If Stop() has been called, then drop this call. | 934 // If Stop() has been called, then drop this call. |
| 914 if (!blocking_thread_.IsRunning()) | 935 if (stopped_) |
| 915 return; | 936 return; |
| 916 | 937 |
| 917 // This should only be called after the demuxer has been initialized. | 938 // This should only be called after the demuxer has been initialized. |
| 918 DCHECK_GT(streams_.size(), 0u); | 939 DCHECK_GT(streams_.size(), 0u); |
| 919 | 940 |
| 920 // Abort all outstanding reads. | 941 // Abort all outstanding reads. |
| 921 for (const auto& stream : streams_) { | 942 for (const auto& stream : streams_) { |
| 922 if (stream) | 943 if (stream) |
| 923 stream->Abort(); | 944 stream->Abort(); |
| 924 } | 945 } |
| 925 | 946 |
| 926 // It's important to invalidate read/seek completion callbacks to avoid any | 947 // It's important to invalidate read/seek completion callbacks to avoid any |
| 927 // errors that occur because of the data source abort. | 948 // errors that occur because of the data source abort. |
| 928 weak_factory_.InvalidateWeakPtrs(); | 949 weak_factory_.InvalidateWeakPtrs(); |
| 929 data_source_->Abort(); | 950 data_source_->Abort(); |
| 930 | 951 |
| 931 // Aborting the read may cause EOF to be marked, undo this. | 952 // Aborting the read may cause EOF to be marked, undo this. |
| 932 blocking_thread_.task_runner()->PostTask( | 953 blocking_task_runner_->PostTask( |
| 933 FROM_HERE, base::Bind(&UnmarkEndOfStream, glue_->format_context())); | 954 FROM_HERE, base::Bind(&UnmarkEndOfStream, glue_->format_context())); |
| 934 pending_read_ = false; | 955 pending_read_ = false; |
| 935 | 956 |
| 936 // TODO(dalecurtis): We probably should report PIPELINE_ERROR_ABORT here | 957 // TODO(dalecurtis): We probably should report PIPELINE_ERROR_ABORT here |
| 937 // instead to avoid any preroll work that may be started upon return, but | 958 // instead to avoid any preroll work that may be started upon return, but |
| 938 // currently the PipelineImpl does not know how to handle this. | 959 // currently the PipelineImpl does not know how to handle this. |
| 939 if (!pending_seek_cb_.is_null()) | 960 if (!pending_seek_cb_.is_null()) |
| 940 base::ResetAndReturn(&pending_seek_cb_).Run(PIPELINE_OK); | 961 base::ResetAndReturn(&pending_seek_cb_).Run(PIPELINE_OK); |
| 941 } | 962 } |
| 942 | 963 |
| 943 void FFmpegDemuxer::Stop() { | 964 void FFmpegDemuxer::Stop() { |
| 944 DCHECK(task_runner_->BelongsToCurrentThread()); | 965 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 945 | 966 |
| 946 // The order of Stop() and Abort() is important here. If Abort() is called | 967 // The order of Stop() and Abort() is important here. If Abort() is called |
| 947 // first, control may pass into FFmpeg where it can destruct buffers that are | 968 // first, control may pass into FFmpeg where it can destruct buffers that are |
| 948 // in the process of being fulfilled by the DataSource. | 969 // in the process of being fulfilled by the DataSource. |
| 949 data_source_->Stop(); | 970 data_source_->Stop(); |
| 950 url_protocol_->Abort(); | 971 url_protocol_->Abort(); |
| 951 | 972 |
| 952 // This will block until all tasks complete. Note that after this returns it's | |
| 953 // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this | |
| 954 // thread. Each of the reply task methods must check whether we've stopped the | |
| 955 // thread and drop their results on the floor. | |
| 956 blocking_thread_.Stop(); | |
| 957 | |
| 958 for (const auto& stream : streams_) { | 973 for (const auto& stream : streams_) { |
| 959 if (stream) | 974 if (stream) |
| 960 stream->Stop(); | 975 stream->Stop(); |
| 961 } | 976 } |
| 962 | 977 |
| 963 data_source_ = NULL; | 978 data_source_ = NULL; |
| 964 | 979 |
| 965 // Invalidate WeakPtrs on |task_runner_|, destruction may happen on another | 980 // Invalidate WeakPtrs on |task_runner_|, destruction may happen on another |
| 966 // thread. | 981 // thread. We don't need to wait for any outstanding tasks since they will all |
| 982 // fail to return after invalidating WeakPtrs. |
| 983 stopped_ = true; |
| 967 weak_factory_.InvalidateWeakPtrs(); | 984 weak_factory_.InvalidateWeakPtrs(); |
| 968 cancel_pending_seek_factory_.InvalidateWeakPtrs(); | 985 cancel_pending_seek_factory_.InvalidateWeakPtrs(); |
| 969 } | 986 } |
| 970 | 987 |
| 971 void FFmpegDemuxer::StartWaitingForSeek(base::TimeDelta seek_time) {} | 988 void FFmpegDemuxer::StartWaitingForSeek(base::TimeDelta seek_time) {} |
| 972 | 989 |
| 973 void FFmpegDemuxer::CancelPendingSeek(base::TimeDelta seek_time) { | 990 void FFmpegDemuxer::CancelPendingSeek(base::TimeDelta seek_time) { |
| 974 if (task_runner_->BelongsToCurrentThread()) { | 991 if (task_runner_->BelongsToCurrentThread()) { |
| 975 AbortPendingReads(); | 992 AbortPendingReads(); |
| 976 } else { | 993 } else { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1015 // be demuxed. It's an open question whether FFmpeg should fix this: | 1032 // be demuxed. It's an open question whether FFmpeg should fix this: |
| 1016 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html | 1033 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html |
| 1017 // Tracked by http://crbug.com/387996. | 1034 // Tracked by http://crbug.com/387996. |
| 1018 FFmpegDemuxerStream* demux_stream = FindPreferredStreamForSeeking(seek_time); | 1035 FFmpegDemuxerStream* demux_stream = FindPreferredStreamForSeeking(seek_time); |
| 1019 DCHECK(demux_stream); | 1036 DCHECK(demux_stream); |
| 1020 const AVStream* seeking_stream = demux_stream->av_stream(); | 1037 const AVStream* seeking_stream = demux_stream->av_stream(); |
| 1021 DCHECK(seeking_stream); | 1038 DCHECK(seeking_stream); |
| 1022 | 1039 |
| 1023 pending_seek_cb_ = cb; | 1040 pending_seek_cb_ = cb; |
| 1024 base::PostTaskAndReplyWithResult( | 1041 base::PostTaskAndReplyWithResult( |
| 1025 blocking_thread_.task_runner().get(), FROM_HERE, | 1042 blocking_task_runner_.get(), FROM_HERE, |
| 1026 base::Bind(&av_seek_frame, glue_->format_context(), seeking_stream->index, | 1043 base::Bind(&av_seek_frame, glue_->format_context(), seeking_stream->index, |
| 1027 ConvertToTimeBase(seeking_stream->time_base, seek_time), | 1044 ConvertToTimeBase(seeking_stream->time_base, seek_time), |
| 1028 // Always seek to a timestamp <= to the desired timestamp. | 1045 // Always seek to a timestamp <= to the desired timestamp. |
| 1029 AVSEEK_FLAG_BACKWARD), | 1046 AVSEEK_FLAG_BACKWARD), |
| 1030 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr())); | 1047 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr())); |
| 1031 } | 1048 } |
| 1032 | 1049 |
| 1033 base::Time FFmpegDemuxer::GetTimelineOffset() const { | 1050 base::Time FFmpegDemuxer::GetTimelineOffset() const { |
| 1034 return timeline_offset_; | 1051 return timeline_offset_; |
| 1035 } | 1052 } |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1152 // Do math in floating point as we'd overflow an int64_t if the filesize was | 1169 // Do math in floating point as we'd overflow an int64_t if the filesize was |
| 1153 // larger than ~1073GB. | 1170 // larger than ~1073GB. |
| 1154 double bytes = filesize_in_bytes; | 1171 double bytes = filesize_in_bytes; |
| 1155 double duration_us = duration.InMicroseconds(); | 1172 double duration_us = duration.InMicroseconds(); |
| 1156 return bytes * 8000000.0 / duration_us; | 1173 return bytes * 8000000.0 / duration_us; |
| 1157 } | 1174 } |
| 1158 | 1175 |
| 1159 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, | 1176 void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, |
| 1160 bool result) { | 1177 bool result) { |
| 1161 DCHECK(task_runner_->BelongsToCurrentThread()); | 1178 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1162 if (!blocking_thread_.IsRunning()) { | 1179 if (stopped_) { |
| 1163 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; | 1180 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; |
| 1164 status_cb.Run(PIPELINE_ERROR_ABORT); | 1181 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 1165 return; | 1182 return; |
| 1166 } | 1183 } |
| 1167 | 1184 |
| 1168 if (!result) { | 1185 if (!result) { |
| 1169 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": open context failed"; | 1186 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": open context failed"; |
| 1170 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 1187 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 1171 return; | 1188 return; |
| 1172 } | 1189 } |
| 1173 | 1190 |
| 1174 // Fully initialize AVFormatContext by parsing the stream a little. | 1191 // Fully initialize AVFormatContext by parsing the stream a little. |
| 1175 base::PostTaskAndReplyWithResult( | 1192 base::PostTaskAndReplyWithResult( |
| 1176 blocking_thread_.task_runner().get(), | 1193 blocking_task_runner_.get(), FROM_HERE, |
| 1177 FROM_HERE, | 1194 base::Bind(&avformat_find_stream_info, glue_->format_context(), |
| 1178 base::Bind(&avformat_find_stream_info, | |
| 1179 glue_->format_context(), | |
| 1180 static_cast<AVDictionary**>(NULL)), | 1195 static_cast<AVDictionary**>(NULL)), |
| 1181 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, | 1196 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, |
| 1182 weak_factory_.GetWeakPtr(), | 1197 weak_factory_.GetWeakPtr(), status_cb)); |
| 1183 status_cb)); | |
| 1184 } | 1198 } |
| 1185 | 1199 |
| 1186 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 1200 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
| 1187 int result) { | 1201 int result) { |
| 1188 DCHECK(task_runner_->BelongsToCurrentThread()); | 1202 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1189 if (!blocking_thread_.IsRunning() || !data_source_) { | 1203 if (stopped_ || !data_source_) { |
| 1190 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; | 1204 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; |
| 1191 status_cb.Run(PIPELINE_ERROR_ABORT); | 1205 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 1192 return; | 1206 return; |
| 1193 } | 1207 } |
| 1194 | 1208 |
| 1195 if (result < 0) { | 1209 if (result < 0) { |
| 1196 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() | 1210 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() |
| 1197 << ": find stream info failed"; | 1211 << ": find stream info failed"; |
| 1198 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); | 1212 status_cb.Run(DEMUXER_ERROR_COULD_NOT_PARSE); |
| 1199 return; | 1213 return; |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1599 } | 1613 } |
| 1600 | 1614 |
| 1601 NOTREACHED(); | 1615 NOTREACHED(); |
| 1602 return nullptr; | 1616 return nullptr; |
| 1603 } | 1617 } |
| 1604 | 1618 |
| 1605 void FFmpegDemuxer::OnSeekFrameDone(int result) { | 1619 void FFmpegDemuxer::OnSeekFrameDone(int result) { |
| 1606 DCHECK(task_runner_->BelongsToCurrentThread()); | 1620 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1607 CHECK(!pending_seek_cb_.is_null()); | 1621 CHECK(!pending_seek_cb_.is_null()); |
| 1608 | 1622 |
| 1609 if (!blocking_thread_.IsRunning()) { | 1623 if (stopped_) { |
| 1610 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; | 1624 MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": bad state"; |
| 1611 base::ResetAndReturn(&pending_seek_cb_).Run(PIPELINE_ERROR_ABORT); | 1625 base::ResetAndReturn(&pending_seek_cb_).Run(PIPELINE_ERROR_ABORT); |
| 1612 return; | 1626 return; |
| 1613 } | 1627 } |
| 1614 | 1628 |
| 1615 if (result < 0) { | 1629 if (result < 0) { |
| 1616 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 1630 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
| 1617 // captured from stdout and contaminates testing. | 1631 // captured from stdout and contaminates testing. |
| 1618 // TODO(scherkus): Implement this properly and signal error (BUG=23447). | 1632 // TODO(scherkus): Implement this properly and signal error (BUG=23447). |
| 1619 VLOG(1) << "Not implemented"; | 1633 VLOG(1) << "Not implemented"; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1686 if (selected_stream) { | 1700 if (selected_stream) { |
| 1687 DVLOG(1) << __func__ << ": enabling stream " << selected_stream; | 1701 DVLOG(1) << __func__ << ": enabling stream " << selected_stream; |
| 1688 selected_stream->set_enabled(true, currTime); | 1702 selected_stream->set_enabled(true, currTime); |
| 1689 } | 1703 } |
| 1690 } | 1704 } |
| 1691 | 1705 |
| 1692 void FFmpegDemuxer::ReadFrameIfNeeded() { | 1706 void FFmpegDemuxer::ReadFrameIfNeeded() { |
| 1693 DCHECK(task_runner_->BelongsToCurrentThread()); | 1707 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1694 | 1708 |
| 1695 // Make sure we have work to do before reading. | 1709 // Make sure we have work to do before reading. |
| 1696 if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || | 1710 if (stopped_ || !StreamsHaveAvailableCapacity() || pending_read_ || |
| 1697 pending_read_ || !pending_seek_cb_.is_null()) { | 1711 !pending_seek_cb_.is_null()) { |
| 1698 return; | 1712 return; |
| 1699 } | 1713 } |
| 1700 | 1714 |
| 1701 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 1715 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
| 1702 // evaluation order of packet.get() and base::Passed(&packet) is | 1716 // evaluation order of packet.get() and base::Passed(&packet) is |
| 1703 // undefined. | 1717 // undefined. |
| 1704 ScopedAVPacket packet(new AVPacket()); | 1718 ScopedAVPacket packet(new AVPacket()); |
| 1705 AVPacket* packet_ptr = packet.get(); | 1719 AVPacket* packet_ptr = packet.get(); |
| 1706 | 1720 |
| 1707 pending_read_ = true; | 1721 pending_read_ = true; |
| 1708 base::PostTaskAndReplyWithResult( | 1722 base::PostTaskAndReplyWithResult( |
| 1709 blocking_thread_.task_runner().get(), | 1723 blocking_task_runner_.get(), FROM_HERE, |
| 1710 FROM_HERE, | |
| 1711 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 1724 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
| 1712 base::Bind(&FFmpegDemuxer::OnReadFrameDone, | 1725 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_factory_.GetWeakPtr(), |
| 1713 weak_factory_.GetWeakPtr(), | |
| 1714 base::Passed(&packet))); | 1726 base::Passed(&packet))); |
| 1715 } | 1727 } |
| 1716 | 1728 |
| 1717 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 1729 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
| 1718 DCHECK(task_runner_->BelongsToCurrentThread()); | 1730 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1719 DCHECK(pending_read_); | 1731 DCHECK(pending_read_); |
| 1720 pending_read_ = false; | 1732 pending_read_ = false; |
| 1721 | 1733 |
| 1722 if (!blocking_thread_.IsRunning() || !pending_seek_cb_.is_null()) | 1734 if (stopped_ || !pending_seek_cb_.is_null()) |
| 1723 return; | 1735 return; |
| 1724 | 1736 |
| 1725 // Consider the stream as ended if: | 1737 // Consider the stream as ended if: |
| 1726 // - either underlying ffmpeg returned an error | 1738 // - either underlying ffmpeg returned an error |
| 1727 // - or FFMpegDemuxer reached the maximum allowed memory usage. | 1739 // - or FFMpegDemuxer reached the maximum allowed memory usage. |
| 1728 if (result < 0 || IsMaxMemoryUsageReached()) { | 1740 if (result < 0 || IsMaxMemoryUsageReached()) { |
| 1729 DVLOG(1) << __func__ << " result=" << result | 1741 DVLOG(1) << __func__ << " result=" << result |
| 1730 << " IsMaxMemoryUsageReached=" << IsMaxMemoryUsageReached(); | 1742 << " IsMaxMemoryUsageReached=" << IsMaxMemoryUsageReached(); |
| 1731 // Update the duration based on the highest elapsed time across all streams. | 1743 // Update the duration based on the highest elapsed time across all streams. |
| 1732 base::TimeDelta max_duration; | 1744 base::TimeDelta max_duration; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1837 | 1849 |
| 1838 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { | 1850 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { |
| 1839 DCHECK(task_runner_->BelongsToCurrentThread()); | 1851 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 1840 for (const auto& stream : streams_) { | 1852 for (const auto& stream : streams_) { |
| 1841 if (stream) | 1853 if (stream) |
| 1842 stream->SetLiveness(liveness); | 1854 stream->SetLiveness(liveness); |
| 1843 } | 1855 } |
| 1844 } | 1856 } |
| 1845 | 1857 |
| 1846 } // namespace media | 1858 } // namespace media |
| OLD | NEW |