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 |