Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/formats/mp2t/es_adapter_video.h" | |
| 6 | |
| 7 #include "media/base/buffers.h" | |
| 8 #include "media/base/stream_parser_buffer.h" | |
| 9 #include "media/base/video_decoder_config.h" | |
| 10 #include "media/formats/mp2t/mp2t_common.h" | |
| 11 | |
| 12 namespace media { | |
| 13 namespace mp2t { | |
| 14 | |
| 15 // Arbitrary decision about the frame duration when there is no previous | |
| 16 // hint about what could be the frame duration. | |
| 17 static base::TimeDelta kDefaultFrameDuration( | |
| 18 base::TimeDelta::FromMilliseconds(40)); | |
| 19 | |
| 20 // To calculate the frame duration, we make an assumption | |
| 21 // that the timestamp of the next frame in presentation order | |
| 22 // is no further than 5 frames away in decode order. | |
| 23 // TODO(damienv): the previous assumption should cover most of the practical | |
| 24 // cases. However, the right way to calculate the frame duration would be | |
| 25 // to emulate the H264 dpb bumping process. | |
| 26 static const size_t kHistorySize = 5; | |
| 27 | |
| 28 EsAdapterVideo::EsAdapterVideo( | |
| 29 const NewVideoConfigCB& new_video_config_cb, | |
| 30 const EmitBufferCB& emit_buffer_cb) | |
| 31 : new_video_config_cb_(new_video_config_cb), | |
| 32 emit_buffer_cb_(emit_buffer_cb), | |
| 33 has_valid_config_(false), | |
| 34 has_valid_frame_(false), | |
| 35 last_frame_duration_(kDefaultFrameDuration), | |
| 36 buffer_index_(0) { | |
| 37 } | |
| 38 | |
| 39 EsAdapterVideo::~EsAdapterVideo() { | |
| 40 } | |
| 41 | |
| 42 void EsAdapterVideo::Flush() { | |
| 43 ProcessPendingBuffers(true); | |
| 44 } | |
| 45 | |
| 46 void EsAdapterVideo::OnConfigChanged( | |
| 47 const VideoDecoderConfig& video_decoder_config) { | |
| 48 config_list_.push_back( | |
| 49 ConfigEntry(buffer_index_ + buffer_list_.size(), video_decoder_config)); | |
| 50 has_valid_config_ = true; | |
| 51 ProcessPendingBuffers(false); | |
| 52 } | |
| 53 | |
| 54 void EsAdapterVideo::OnNewBuffer( | |
| 55 const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { | |
| 56 // Discard the incoming frame: | |
| 57 // - if it is not associated with any config, | |
| 58 // - or if only non-key frames have been added to a new segment. | |
| 59 if (!has_valid_config_ || | |
| 60 (!has_valid_frame_ && !stream_parser_buffer->IsKeyframe())) { | |
| 61 if (discarded_frames_dts_.empty() || | |
| 62 discarded_frames_min_pts_ > stream_parser_buffer->timestamp()) { | |
| 63 discarded_frames_min_pts_ = stream_parser_buffer->timestamp(); | |
| 64 } | |
| 65 discarded_frames_dts_.push_back( | |
| 66 stream_parser_buffer->GetDecodeTimestamp()); | |
| 67 return; | |
| 68 } | |
| 69 | |
| 70 has_valid_frame_ = true; | |
| 71 | |
| 72 if (!discarded_frames_dts_.empty()) | |
| 73 ReplaceDiscardedFrames(stream_parser_buffer); | |
| 74 | |
| 75 buffer_list_.push_back(stream_parser_buffer); | |
| 76 ProcessPendingBuffers(false); | |
| 77 } | |
| 78 | |
| 79 void EsAdapterVideo::ProcessPendingBuffers(bool flush) { | |
| 80 DCHECK(has_valid_config_); | |
| 81 | |
| 82 while (!buffer_list_.empty() && | |
| 83 (flush || buffer_list_.size() > kHistorySize)) { | |
| 84 // Signal a config change, just before emitting the corresponding frame. | |
| 85 if (!config_list_.empty() && config_list_.front().first == buffer_index_) { | |
| 86 new_video_config_cb_.Run(config_list_.front().second); | |
| 87 config_list_.pop_front(); | |
| 88 } | |
| 89 | |
| 90 scoped_refptr<StreamParserBuffer> buffer = buffer_list_.front(); | |
| 91 buffer_list_.pop_front(); | |
| 92 buffer_index_++; | |
| 93 | |
| 94 if (buffer->duration() == kNoTimestamp()) { | |
| 95 base::TimeDelta next_frame_pts = GetNextFramePts(buffer->timestamp()); | |
| 96 if (next_frame_pts == kNoTimestamp()) { | |
| 97 // This can happen when emitting the very last buffer | |
| 98 // or if the stream do not meet the assumption behind |kHistorySize|. | |
| 99 buffer->set_duration(last_frame_duration_); | |
| 100 } else { | |
| 101 buffer->set_duration(next_frame_pts - buffer->timestamp()); | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 emitted_pts_.push_back(buffer->timestamp()); | |
| 106 if (emitted_pts_.size() > kHistorySize) | |
| 107 emitted_pts_.pop_front(); | |
| 108 | |
| 109 last_frame_duration_ = buffer->duration(); | |
| 110 emit_buffer_cb_.Run(buffer); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 base::TimeDelta EsAdapterVideo::GetNextFramePts(base::TimeDelta current_pts) { | |
| 115 base::TimeDelta next_pts = kNoTimestamp(); | |
| 116 | |
| 117 // Consider the timestamps of future frames (in decode order). | |
| 118 for (BufferQueue::const_iterator it = buffer_list_.begin(); | |
|
acolwell GONE FROM CHROMIUM
2014/07/09 20:01:10
nit: Might want to call out B-frames here since pe
damienv1
2014/07/10 16:00:53
Done.
| |
| 119 it != buffer_list_.end(); ++it) { | |
| 120 if ((*it)->timestamp() < current_pts) | |
| 121 continue; | |
| 122 if (next_pts == kNoTimestamp() || next_pts > (*it)->timestamp()) | |
| 123 next_pts = (*it)->timestamp(); | |
| 124 } | |
| 125 | |
| 126 // Consider the timestamps of previous frames (in decode order). | |
| 127 for (std::list<base::TimeDelta>::const_iterator it = emitted_pts_.begin(); | |
|
acolwell GONE FROM CHROMIUM
2014/07/09 20:01:10
This is for the duration of the last B frame in IP
damienv1
2014/07/10 16:00:53
Done.
| |
| 128 it != emitted_pts_.end(); ++it) { | |
| 129 if (*it < current_pts) | |
| 130 continue; | |
| 131 if (next_pts == kNoTimestamp() || next_pts > *it) | |
| 132 next_pts = *it; | |
| 133 } | |
| 134 | |
| 135 return next_pts; | |
| 136 } | |
| 137 | |
| 138 void EsAdapterVideo::ReplaceDiscardedFrames( | |
| 139 const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { | |
| 140 DCHECK(!discarded_frames_dts_.empty()); | |
| 141 DCHECK(stream_parser_buffer->IsKeyframe()); | |
| 142 | |
| 143 // PTS is interpolated between the min PTS of discarded frames | |
| 144 // and the PTS of the first valid buffer. | |
| 145 base::TimeDelta pts = discarded_frames_min_pts_; | |
| 146 base::TimeDelta pts_delta = | |
| 147 (stream_parser_buffer->timestamp() - pts) / discarded_frames_dts_.size(); | |
| 148 | |
| 149 while (!discarded_frames_dts_.empty()) { | |
| 150 scoped_refptr<StreamParserBuffer> frame = | |
| 151 StreamParserBuffer::CopyFrom( | |
| 152 stream_parser_buffer->data(), | |
| 153 stream_parser_buffer->data_size(), | |
| 154 stream_parser_buffer->IsKeyframe(), | |
| 155 stream_parser_buffer->type(), | |
| 156 stream_parser_buffer->track_id()); | |
| 157 frame->SetDecodeTimestamp(discarded_frames_dts_.front()); | |
| 158 frame->set_timestamp(pts); | |
| 159 frame->set_duration(pts_delta); | |
| 160 buffer_list_.push_back(frame); | |
| 161 pts += pts_delta; | |
| 162 discarded_frames_dts_.pop_front(); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 } // namespace mp2t | |
| 167 } // namespace media | |
| 168 | |
| OLD | NEW |