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 const int kDefaultFrameDurationMs = 40; |
| 18 |
| 19 // To calculate the frame duration, we make an assumption |
| 20 // that the timestamp of the next frame in presentation order |
| 21 // is no further than 5 frames away in decode order. |
| 22 // TODO(damienv): the previous assumption should cover most of the practical |
| 23 // cases. However, the right way to calculate the frame duration would be |
| 24 // to emulate the H264 dpb bumping process. |
| 25 static const size_t kHistorySize = 5; |
| 26 |
| 27 EsAdapterVideo::EsAdapterVideo( |
| 28 const NewVideoConfigCB& new_video_config_cb, |
| 29 const EmitBufferCB& emit_buffer_cb) |
| 30 : new_video_config_cb_(new_video_config_cb), |
| 31 emit_buffer_cb_(emit_buffer_cb), |
| 32 has_valid_config_(false), |
| 33 has_valid_frame_(false), |
| 34 last_frame_duration_( |
| 35 base::TimeDelta::FromMilliseconds(kDefaultFrameDurationMs)), |
| 36 buffer_index_(0) { |
| 37 } |
| 38 |
| 39 EsAdapterVideo::~EsAdapterVideo() { |
| 40 } |
| 41 |
| 42 void EsAdapterVideo::Flush() { |
| 43 ProcessPendingBuffers(true); |
| 44 } |
| 45 |
| 46 void EsAdapterVideo::Reset() { |
| 47 has_valid_config_ = false; |
| 48 has_valid_frame_ = false; |
| 49 |
| 50 last_frame_duration_ = |
| 51 base::TimeDelta::FromMilliseconds(kDefaultFrameDurationMs); |
| 52 |
| 53 config_list_.clear(); |
| 54 buffer_index_ = 0; |
| 55 buffer_list_.clear(); |
| 56 emitted_pts_.clear(); |
| 57 |
| 58 discarded_frames_min_pts_ = base::TimeDelta(); |
| 59 discarded_frames_dts_.clear(); |
| 60 } |
| 61 |
| 62 void EsAdapterVideo::OnConfigChanged( |
| 63 const VideoDecoderConfig& video_decoder_config) { |
| 64 config_list_.push_back( |
| 65 ConfigEntry(buffer_index_ + buffer_list_.size(), video_decoder_config)); |
| 66 has_valid_config_ = true; |
| 67 ProcessPendingBuffers(false); |
| 68 } |
| 69 |
| 70 void EsAdapterVideo::OnNewBuffer( |
| 71 const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { |
| 72 // Discard the incoming frame: |
| 73 // - if it is not associated with any config, |
| 74 // - or if only non-key frames have been added to a new segment. |
| 75 if (!has_valid_config_ || |
| 76 (!has_valid_frame_ && !stream_parser_buffer->IsKeyframe())) { |
| 77 if (discarded_frames_dts_.empty() || |
| 78 discarded_frames_min_pts_ > stream_parser_buffer->timestamp()) { |
| 79 discarded_frames_min_pts_ = stream_parser_buffer->timestamp(); |
| 80 } |
| 81 discarded_frames_dts_.push_back( |
| 82 stream_parser_buffer->GetDecodeTimestamp()); |
| 83 return; |
| 84 } |
| 85 |
| 86 has_valid_frame_ = true; |
| 87 |
| 88 if (!discarded_frames_dts_.empty()) |
| 89 ReplaceDiscardedFrames(stream_parser_buffer); |
| 90 |
| 91 buffer_list_.push_back(stream_parser_buffer); |
| 92 ProcessPendingBuffers(false); |
| 93 } |
| 94 |
| 95 void EsAdapterVideo::ProcessPendingBuffers(bool flush) { |
| 96 DCHECK(has_valid_config_); |
| 97 |
| 98 while (!buffer_list_.empty() && |
| 99 (flush || buffer_list_.size() > kHistorySize)) { |
| 100 // Signal a config change, just before emitting the corresponding frame. |
| 101 if (!config_list_.empty() && config_list_.front().first == buffer_index_) { |
| 102 new_video_config_cb_.Run(config_list_.front().second); |
| 103 config_list_.pop_front(); |
| 104 } |
| 105 |
| 106 scoped_refptr<StreamParserBuffer> buffer = buffer_list_.front(); |
| 107 buffer_list_.pop_front(); |
| 108 buffer_index_++; |
| 109 |
| 110 if (buffer->duration() == kNoTimestamp()) { |
| 111 base::TimeDelta next_frame_pts = GetNextFramePts(buffer->timestamp()); |
| 112 if (next_frame_pts == kNoTimestamp()) { |
| 113 // This can happen when emitting the very last buffer |
| 114 // or if the stream do not meet the assumption behind |kHistorySize|. |
| 115 DVLOG(LOG_LEVEL_ES) << "Using last frame duration: " |
| 116 << last_frame_duration_.InMilliseconds(); |
| 117 buffer->set_duration(last_frame_duration_); |
| 118 } else { |
| 119 base::TimeDelta duration = next_frame_pts - buffer->timestamp(); |
| 120 DVLOG(LOG_LEVEL_ES) << "Frame duration: " << duration.InMilliseconds(); |
| 121 buffer->set_duration(duration); |
| 122 } |
| 123 } |
| 124 |
| 125 emitted_pts_.push_back(buffer->timestamp()); |
| 126 if (emitted_pts_.size() > kHistorySize) |
| 127 emitted_pts_.pop_front(); |
| 128 |
| 129 last_frame_duration_ = buffer->duration(); |
| 130 emit_buffer_cb_.Run(buffer); |
| 131 } |
| 132 } |
| 133 |
| 134 base::TimeDelta EsAdapterVideo::GetNextFramePts(base::TimeDelta current_pts) { |
| 135 base::TimeDelta next_pts = kNoTimestamp(); |
| 136 |
| 137 // Consider the timestamps of future frames (in decode order). |
| 138 // Note: the next frame is not enough when the GOP includes some B frames. |
| 139 for (BufferQueue::const_iterator it = buffer_list_.begin(); |
| 140 it != buffer_list_.end(); ++it) { |
| 141 if ((*it)->timestamp() < current_pts) |
| 142 continue; |
| 143 if (next_pts == kNoTimestamp() || next_pts > (*it)->timestamp()) |
| 144 next_pts = (*it)->timestamp(); |
| 145 } |
| 146 |
| 147 // Consider the timestamps of previous frames (in decode order). |
| 148 // In a simple GOP structure with B frames, the frame next to the last B |
| 149 // frame (in presentation order) is located before in decode order. |
| 150 for (std::list<base::TimeDelta>::const_iterator it = emitted_pts_.begin(); |
| 151 it != emitted_pts_.end(); ++it) { |
| 152 if (*it < current_pts) |
| 153 continue; |
| 154 if (next_pts == kNoTimestamp() || next_pts > *it) |
| 155 next_pts = *it; |
| 156 } |
| 157 |
| 158 return next_pts; |
| 159 } |
| 160 |
| 161 void EsAdapterVideo::ReplaceDiscardedFrames( |
| 162 const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { |
| 163 DCHECK(!discarded_frames_dts_.empty()); |
| 164 DCHECK(stream_parser_buffer->IsKeyframe()); |
| 165 |
| 166 // PTS is interpolated between the min PTS of discarded frames |
| 167 // and the PTS of the first valid buffer. |
| 168 base::TimeDelta pts = discarded_frames_min_pts_; |
| 169 base::TimeDelta pts_delta = |
| 170 (stream_parser_buffer->timestamp() - pts) / discarded_frames_dts_.size(); |
| 171 |
| 172 while (!discarded_frames_dts_.empty()) { |
| 173 scoped_refptr<StreamParserBuffer> frame = |
| 174 StreamParserBuffer::CopyFrom( |
| 175 stream_parser_buffer->data(), |
| 176 stream_parser_buffer->data_size(), |
| 177 stream_parser_buffer->IsKeyframe(), |
| 178 stream_parser_buffer->type(), |
| 179 stream_parser_buffer->track_id()); |
| 180 frame->SetDecodeTimestamp(discarded_frames_dts_.front()); |
| 181 frame->set_timestamp(pts); |
| 182 frame->set_duration(pts_delta); |
| 183 buffer_list_.push_back(frame); |
| 184 pts += pts_delta; |
| 185 discarded_frames_dts_.pop_front(); |
| 186 } |
| 187 } |
| 188 |
| 189 } // namespace mp2t |
| 190 } // namespace media |
OLD | NEW |