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

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

Issue 1954633002: MEDIA_LOG for large encoded timestamp gaps in decoder stream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: New strategy, only check gaps after meeting initial (offset) expectations. Created 4 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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/decoder_stream.h" 5 #include "media/filters/decoder_stream.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h" 10 #include "base/callback_helpers.h"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h" 13 #include "base/single_thread_task_runner.h"
14 #include "base/trace_event/trace_event.h" 14 #include "base/trace_event/trace_event.h"
15 #include "media/base/bind_to_current_loop.h" 15 #include "media/base/bind_to_current_loop.h"
16 #include "media/base/cdm_context.h" 16 #include "media/base/cdm_context.h"
17 #include "media/base/decoder_buffer.h" 17 #include "media/base/decoder_buffer.h"
18 #include "media/base/limits.h" 18 #include "media/base/limits.h"
19 #include "media/base/media_log.h" 19 #include "media/base/media_log.h"
20 #include "media/base/timestamp_constants.h" 20 #include "media/base/timestamp_constants.h"
21 #include "media/base/video_decoder.h" 21 #include "media/base/video_decoder.h"
22 #include "media/base/video_frame.h" 22 #include "media/base/video_frame.h"
23 #include "media/filters/decrypting_demuxer_stream.h" 23 #include "media/filters/decrypting_demuxer_stream.h"
24 24
25 namespace media { 25 namespace media {
26 26
27 // Defines how many milliseconds of DecoderBuffer timestamp gap will be allowed
28 // before warning the user. See CheckForTimestampGap(). Value of 50 chosen, as
29 // this is low enough to catch issues early, but high enough to avoid noise for
30 // containers like WebM that default to low granularity timestamp precision.
31 const int kGapWarningThresholdMsec = 50;
32
33 // Limits the number of adjustments to |audio_ts_offset_| in order to reach a
34 // stable state where gaps between encoded timestamps match decoded output
35 // intervals. See CheckForTimestampGap().
36 const int kLimitTriesForStableTiming = 5;
37
38 // Limits the milliseconds of difference between expected and actual timestamps
39 // gaps to consider timestamp expectations "stable". 1 chosen because some
40 // containers (WebM) default to millisecond timestamp precision. See
41 // CheckForTimestampGap().
42 const int kStableTimeGapThrsholdMsec = 1;
43
27 // TODO(rileya): Devise a better way of specifying trace/UMA/etc strings for 44 // TODO(rileya): Devise a better way of specifying trace/UMA/etc strings for
28 // templated classes such as this. 45 // templated classes such as this.
29 template <DemuxerStream::Type StreamType> 46 template <DemuxerStream::Type StreamType>
30 static const char* GetTraceString(); 47 static const char* GetTraceString();
31 48
32 #define FUNCTION_DVLOG(level) \ 49 #define FUNCTION_DVLOG(level) \
33 DVLOG(level) << __FUNCTION__ << "<" << GetStreamTypeString() << ">" 50 DVLOG(level) << __FUNCTION__ << "<" << GetStreamTypeString() << ">"
34 51
35 template <> 52 template <>
36 const char* GetTraceString<DemuxerStream::VIDEO>() { 53 const char* GetTraceString<DemuxerStream::VIDEO>() {
37 return "DecoderStream<VIDEO>::Decode"; 54 return "DecoderStream<VIDEO>::Decode";
38 } 55 }
39 56
40 template <> 57 template <>
41 const char* GetTraceString<DemuxerStream::AUDIO>() { 58 const char* GetTraceString<DemuxerStream::AUDIO>() {
42 return "DecoderStream<AUDIO>::Decode"; 59 return "DecoderStream<AUDIO>::Decode";
43 } 60 }
44 61
45 template <DemuxerStream::Type StreamType> 62 template <DemuxerStream::Type StreamType>
46 DecoderStream<StreamType>::DecoderStream( 63 DecoderStream<StreamType>::DecoderStream(
47 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 64 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
48 ScopedVector<Decoder> decoders, 65 ScopedVector<Decoder> decoders,
49 const scoped_refptr<MediaLog>& media_log) 66 const scoped_refptr<MediaLog>& media_log)
50 : task_runner_(task_runner), 67 : task_runner_(task_runner),
51 media_log_(media_log), 68 media_log_(media_log),
52 state_(STATE_UNINITIALIZED), 69 state_(STATE_UNINITIALIZED),
53 stream_(NULL), 70 stream_(NULL),
71 audio_base_ts_(kNoTimestamp()),
72 stable_audio_times_(false),
73 num_unstable_audio_tries_(0),
74 drift_warning_threshold_msec_(kGapWarningThresholdMsec),
54 decoder_selector_(new DecoderSelector<StreamType>(task_runner, 75 decoder_selector_(new DecoderSelector<StreamType>(task_runner,
55 std::move(decoders), 76 std::move(decoders),
56 media_log)), 77 media_log)),
57 decoded_frames_since_fallback_(0), 78 decoded_frames_since_fallback_(0),
58 active_splice_(false), 79 active_splice_(false),
59 decoding_eos_(false), 80 decoding_eos_(false),
60 pending_decode_requests_(0), 81 pending_decode_requests_(0),
61 duration_tracker_(8), 82 duration_tracker_(8),
62 received_config_change_during_reinit_(false), 83 received_config_change_during_reinit_(false),
63 pending_demuxer_read_(false), 84 pending_demuxer_read_(false),
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 179
159 reset_cb_ = closure; 180 reset_cb_ = closure;
160 181
161 if (!read_cb_.is_null()) { 182 if (!read_cb_.is_null()) {
162 task_runner_->PostTask(FROM_HERE, 183 task_runner_->PostTask(FROM_HERE,
163 base::Bind(base::ResetAndReturn(&read_cb_), ABORTED, 184 base::Bind(base::ResetAndReturn(&read_cb_), ABORTED,
164 scoped_refptr<Output>())); 185 scoped_refptr<Output>()));
165 } 186 }
166 187
167 ready_outputs_.clear(); 188 ready_outputs_.clear();
189 audio_base_ts_ = kNoTimestamp();
190 audio_ts_offset_ = base::TimeDelta();
191 audio_output_ts_helper_.reset();
192 num_unstable_audio_tries_ = 0;
193 stable_audio_times_ = false;
194 drift_warning_threshold_msec_ = kGapWarningThresholdMsec;
168 195
169 // It's possible to have received a DECODE_ERROR and entered STATE_ERROR right 196 // It's possible to have received a DECODE_ERROR and entered STATE_ERROR right
170 // before a Reset() is executed. If we are still waiting for a demuxer read, 197 // before a Reset() is executed. If we are still waiting for a demuxer read,
171 // OnBufferReady() will handle the reset callback. 198 // OnBufferReady() will handle the reset callback.
172 // See crbug.com/597605 and crbug.com/607454. 199 // See crbug.com/597605 and crbug.com/607454.
173 if (state_ == STATE_ERROR && !pending_demuxer_read_) { 200 if (state_ == STATE_ERROR && !pending_demuxer_read_) {
174 task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_)); 201 task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_));
175 return; 202 return;
176 } 203 }
177 204
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 368
342 template <DemuxerStream::Type StreamType> 369 template <DemuxerStream::Type StreamType>
343 void DecoderStream<StreamType>::DecodeInternal( 370 void DecoderStream<StreamType>::DecodeInternal(
344 const scoped_refptr<DecoderBuffer>& buffer) { 371 const scoped_refptr<DecoderBuffer>& buffer) {
345 FUNCTION_DVLOG(2); 372 FUNCTION_DVLOG(2);
346 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; 373 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_;
347 DCHECK_LT(pending_decode_requests_, GetMaxDecodeRequests()); 374 DCHECK_LT(pending_decode_requests_, GetMaxDecodeRequests());
348 DCHECK(reset_cb_.is_null()); 375 DCHECK(reset_cb_.is_null());
349 DCHECK(buffer.get()); 376 DCHECK(buffer.get());
350 377
378 CheckForTimestampGap(buffer);
379
351 int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size(); 380 int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size();
352 381
353 TRACE_EVENT_ASYNC_BEGIN2( 382 TRACE_EVENT_ASYNC_BEGIN2(
354 "media", GetTraceString<StreamType>(), this, "key frame", 383 "media", GetTraceString<StreamType>(), this, "key frame",
355 !buffer->end_of_stream() && buffer->is_key_frame(), "timestamp (ms)", 384 !buffer->end_of_stream() && buffer->is_key_frame(), "timestamp (ms)",
356 !buffer->end_of_stream() ? buffer->timestamp().InMilliseconds() : 0); 385 !buffer->end_of_stream() ? buffer->timestamp().InMilliseconds() : 0);
357 386
358 if (buffer->end_of_stream()) 387 if (buffer->end_of_stream())
359 decoding_eos_ = true; 388 decoding_eos_ = true;
360 else if (buffer->duration() != kNoTimestamp()) 389 else if (buffer->duration() != kNoTimestamp())
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 if (state_ == STATE_ERROR) { 498 if (state_ == STATE_ERROR) {
470 DCHECK(read_cb_.is_null()); 499 DCHECK(read_cb_.is_null());
471 return; 500 return;
472 } 501 }
473 502
474 // Drop decoding result if Reset() was called during decoding. 503 // Drop decoding result if Reset() was called during decoding.
475 // The resetting process will be handled when the decoder is reset. 504 // The resetting process will be handled when the decoder is reset.
476 if (!reset_cb_.is_null()) 505 if (!reset_cb_.is_null())
477 return; 506 return;
478 507
508 RecordOutputDuration(output);
509
479 ++decoded_frames_since_fallback_; 510 ++decoded_frames_since_fallback_;
480 511
481 // |decoder_| sucessfully decoded a frame. No need to keep buffers for a 512 // |decoder_| sucessfully decoded a frame. No need to keep buffers for a
482 // fallback decoder. 513 // fallback decoder.
483 // Note: |fallback_buffers_| might still have buffers, and we will keep 514 // Note: |fallback_buffers_| might still have buffers, and we will keep
484 // reading from there before requesting new buffers from |stream_|. 515 // reading from there before requesting new buffers from |stream_|.
485 pending_buffers_.clear(); 516 pending_buffers_.clear();
486 517
487 if (!read_cb_.is_null()) { 518 if (!read_cb_.is_null()) {
488 // If |ready_outputs_| was non-empty, the read would have already been 519 // If |ready_outputs_| was non-empty, the read would have already been
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
765 state_ = STATE_NORMAL; 796 state_ = STATE_NORMAL;
766 active_splice_ = false; 797 active_splice_ = false;
767 base::ResetAndReturn(&reset_cb_).Run(); 798 base::ResetAndReturn(&reset_cb_).Run();
768 return; 799 return;
769 } 800 }
770 801
771 // The resetting process will be continued in OnDecoderReinitialized(). 802 // The resetting process will be continued in OnDecoderReinitialized().
772 ReinitializeDecoder(); 803 ReinitializeDecoder();
773 } 804 }
774 805
806 template <>
807 void DecoderStream<DemuxerStream::VIDEO>::CheckForTimestampGap(
808 const scoped_refptr<DecoderBuffer>& buffer) {}
809
810 template <>
811 void DecoderStream<DemuxerStream::VIDEO>::RecordOutputDuration(
812 const scoped_refptr<Output>& output) {}
813
814 template <>
815 void DecoderStream<DemuxerStream::AUDIO>::CheckForTimestampGap(
816 const scoped_refptr<DecoderBuffer>& buffer) {
817 if (buffer->end_of_stream())
818 return;
819 DCHECK_NE(kNoTimestamp(), buffer->timestamp());
820
821 // Don't continue checking timestamps if we've exhausted tries to reach stable
822 // state. This suggests the media's encoded timestamps are way off.
823 if (num_unstable_audio_tries_ > kLimitTriesForStableTiming)
824 return;
825
826 // Keep resetting encode base ts until we start getting decode output. Some
827 // codecs/containers (e.g. chained Ogg) will take several encoded buffers
828 // before producing the first decoded output.
829 if (!audio_output_ts_helper_) {
830 audio_base_ts_ = buffer->timestamp();
831 DVLOG(3) << __FUNCTION__
832 << " setting audio_base:" << audio_base_ts_.InMicroseconds();
833 // Wait for decoded output to form expectations to verify timestamps.
834 return;
835 }
836
837 base::TimeDelta expected_ts =
838 audio_output_ts_helper_->GetTimestamp() + audio_ts_offset_;
839 base::TimeDelta ts_delta = buffer->timestamp() - expected_ts;
840
841 // Reconciling encoded buffer timestamps with decoded output often requires
DaleCurtis 2016/05/17 21:32:54 Notably this also hides errors at the beginning of
chcunningham 2016/05/18 00:18:00 Will try to do what we discussed, where we expect
842 // adjusting expectations by some offset. This accounts for varied (and at
843 // this point unknown) handling of front trimming and codec delay. Codec delay
844 // and skip trimming may or may not be accounted for in the encoded timestamps
845 // depending on the codec (e.g. MP3 vs Opus) and demuxers used (e.g. FFmpeg
846 // vs MSE stream parsers).
847 if (!stable_audio_times_) {
848 if (std::abs(ts_delta.InMilliseconds()) < kStableTimeGapThrsholdMsec) {
849 stable_audio_times_ = true;
850 DVLOG(3) << __FUNCTION__
851 << " stabilized! tries:" << num_unstable_audio_tries_
852 << " offset:" << audio_ts_offset_.InMicroseconds();
853 } else {
854 base::TimeDelta orig_offset = audio_ts_offset_;
855 audio_ts_offset_ += ts_delta;
DaleCurtis 2016/05/17 21:32:54 Seems like you should just set the base timestamp
chcunningham 2016/05/18 00:18:00 Done.
856 DVLOG(3) << __FUNCTION__
857 << " NOT stabilized. tries:" << num_unstable_audio_tries_
858 << " offset was:" << orig_offset.InMicroseconds()
859 << " now:" << audio_ts_offset_.InMicroseconds();
860 num_unstable_audio_tries_++;
861
862 // Let developers know if their files timestamps are way off from
863 if (num_unstable_audio_tries_ > kLimitTriesForStableTiming)
DaleCurtis 2016/05/17 21:32:54 Needs {}
chcunningham 2016/05/18 00:18:00 Done.
864 MEDIA_LOG(ERROR, media_log_)
865 << "Failed to reconcile encoded audio times with decoded output.";
866 }
867
868 // Don't bother with further checking until we reach stable state.
869 return;
870 }
871
872 if (std::abs(ts_delta.InMilliseconds()) > drift_warning_threshold_msec_) {
873 MEDIA_LOG(ERROR, media_log_)
874 << " Large timestamp gap detected; may cause AV sync to drift."
875 << " time:" << buffer->timestamp()
876 << " expected:" << expected_ts.InMicroseconds()
877 << " delta:" << ts_delta.InMicroseconds();
878 // Increase threshold to avoid log spam but, let us know if gap widens.
879 drift_warning_threshold_msec_ = std::abs(ts_delta.InMilliseconds());
880 }
881 DVLOG(3) << __FUNCTION__ << " delta:" << ts_delta.InMicroseconds()
882 << " expected_ts:" << expected_ts.InMicroseconds()
883 << " actual_ts:" << buffer->timestamp().InMicroseconds()
884 << " audio_ts_offset:" << audio_ts_offset_.InMicroseconds()
885 << " base_ts:" << audio_base_ts_.InMicroseconds();
886 }
887
888 template <>
889 void DecoderStream<DemuxerStream::AUDIO>::RecordOutputDuration(
890 const scoped_refptr<Output>& output) {
891 if (output.get()) {
892 AudioBuffer* audio_buffer = static_cast<AudioBuffer*>(output.get());
893
894 if (!audio_output_ts_helper_) {
895 DCHECK_NE(audio_base_ts_, kNoTimestamp());
896 audio_output_ts_helper_.reset(
897 new AudioTimestampHelper(audio_buffer->sample_rate()));
898 audio_output_ts_helper_->SetBaseTimestamp(audio_base_ts_);
899 }
900
901 DVLOG(3) << __FUNCTION__ << " " << audio_buffer->frame_count() << " frames";
902 audio_output_ts_helper_->AddFrames(audio_buffer->frame_count());
903 }
904 }
905
775 template class DecoderStream<DemuxerStream::VIDEO>; 906 template class DecoderStream<DemuxerStream::VIDEO>;
776 template class DecoderStream<DemuxerStream::AUDIO>; 907 template class DecoderStream<DemuxerStream::AUDIO>;
777 908
778 } // namespace media 909 } // namespace media
OLDNEW
« media/filters/decoder_stream.h ('K') | « media/filters/decoder_stream.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698