OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 // This file contains an implementation of a VP9 bitstream parser. | |
6 | |
7 #include "media/filters/vp9_parser.h" | |
8 | |
9 #include "base/logging.h" | |
10 | |
11 namespace { | |
12 | |
13 int GetMinLog2TileCols(int sb64_cols) { | |
Pawel Osciak
2015/07/30 08:27:37
static?
Also please document what this function is
kcwu
2015/07/31 08:51:51
IIUC, no need to add static qualifier if it is alr
| |
14 const int kMaxTileWidthB64 = 64; | |
15 int min_log2 = 0; | |
16 while ((kMaxTileWidthB64 << min_log2) < sb64_cols) | |
17 min_log2++; | |
18 return min_log2; | |
19 } | |
20 | |
21 int GetMaxLog2TileCols(int sb64_cols) { | |
Pawel Osciak
2015/07/30 08:27:37
static?
kcwu
2015/07/31 08:51:51
Acknowledged.
| |
22 const int kMinTileWidthB64 = 4; | |
23 int max_log2 = 1; | |
24 while ((sb64_cols >> max_log2) >= kMinTileWidthB64) | |
25 max_log2++; | |
26 return max_log2 - 1; | |
27 } | |
28 | |
29 } // namespace | |
30 | |
31 namespace media { | |
32 | |
33 Vp9Parser::Vp9Parser() : stream_(nullptr), size_(0) { | |
34 memset(&ref_slots_, 0, sizeof(ref_slots_)); | |
35 } | |
36 | |
37 uint8_t Vp9Parser::ReadProfile() { | |
38 uint8_t profile = 0; | |
39 | |
40 // LSB first. | |
41 profile |= reader_.ReadBit(); | |
42 profile |= reader_.ReadBit() << 1; | |
43 if (profile > 2) | |
44 profile |= reader_.ReadBit() << 2; | |
45 return profile; | |
46 } | |
47 | |
48 bool Vp9Parser::VerifySyncCode() { | |
49 const int kSyncCode = 0x498342; | |
50 if (reader_.ReadLiteral(8 * 3) != kSyncCode) { | |
Pawel Osciak
2015/07/30 08:27:37
General comment: ReadLiteral/ReadBit should have a
kcwu
2015/07/31 08:51:51
Per our chat. We agreed checking reader failure at
| |
51 DLOG(ERROR) << "Invalid frame sync code"; | |
52 return false; | |
Pawel Osciak
2015/07/30 08:27:38
DVLOG please
kcwu1
2015/07/30 08:55:49
Only this one or all in this class?
Pawel Osciak
2015/07/30 08:59:24
All please.
kcwu1
2015/07/31 04:36:01
Done.
| |
53 } | |
54 return true; | |
55 } | |
56 | |
57 bool Vp9Parser::ReadBitDepthColorSpaceSampling(Vp9FrameHeader* fhdr) { | |
58 if (fhdr->profile >= 2) { | |
59 if (reader_.ReadBit()) | |
60 fhdr->bit_depth = 12; | |
61 else | |
62 fhdr->bit_depth = 10; | |
63 } else { | |
64 fhdr->bit_depth = 8; | |
65 } | |
66 | |
67 fhdr->color_space = static_cast<Vp9ColorSpace>(reader_.ReadLiteral(3)); | |
68 if (fhdr->color_space != Vp9ColorSpace::SRGB) { | |
69 fhdr->yuv_range = reader_.ReadBit(); | |
70 if (fhdr->profile == 1 || fhdr->profile == 3) { | |
71 fhdr->subsampling_x = reader_.ReadBit(); | |
72 fhdr->subsampling_y = reader_.ReadBit(); | |
73 if (fhdr->subsampling_x == 1 && fhdr->subsampling_y == 1) { | |
74 DLOG(ERROR) << "4:2:0 color not supported in profile 1 or 3"; | |
75 return false; | |
76 } | |
77 bool reserved = reader_.ReadBit(); | |
78 if (reserved) { | |
79 DLOG(ERROR) << "reserved bit set"; | |
80 return false; | |
81 } | |
82 } else { | |
83 fhdr->subsampling_x = fhdr->subsampling_y = 1; | |
84 } | |
85 } else { | |
86 if (fhdr->profile == 1 || fhdr->profile == 3) { | |
87 fhdr->subsampling_x = fhdr->subsampling_y = 0; | |
88 | |
89 // this bit is not specified in spec?? | |
Pawel Osciak
2015/07/30 08:27:37
s/this/This/
s/??/?/
kcwu1
2015/07/30 08:55:49
just keep note. I expect this will be removed befo
| |
90 bool reserved = reader_.ReadBit(); | |
91 if (reserved) { | |
92 DLOG(ERROR) << "reserved bit set"; | |
93 return false; | |
94 } | |
95 } else { | |
96 DLOG(ERROR) << "4:4:4 color not supported in profile 0 or 2"; | |
97 return false; | |
98 } | |
99 } | |
100 | |
101 return true; | |
102 } | |
103 | |
104 void Vp9Parser::ReadFrameSize(Vp9FrameHeader* fhdr) { | |
105 fhdr->width = reader_.ReadLiteral(16) + 1; | |
106 fhdr->height = reader_.ReadLiteral(16) + 1; | |
107 } | |
108 | |
109 void Vp9Parser::ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr) { | |
110 for (int i = 0; i < kVp9RefsPerFrame; i++) { | |
Pawel Osciak
2015/07/30 08:27:37
s/int/size_t/
Please use size_t in general for it
kcwu1
2015/07/31 04:36:02
Done.
| |
111 if (reader_.ReadBit()) { | |
112 fhdr->width = ref_slots_[i].width; | |
113 fhdr->height = ref_slots_[i].height; | |
114 return; | |
115 } | |
116 } | |
117 | |
118 fhdr->width = reader_.ReadLiteral(16) + 1; | |
119 fhdr->height = reader_.ReadLiteral(16) + 1; | |
120 } | |
121 | |
122 void Vp9Parser::ReadDisplayFrameSize(Vp9FrameHeader* fhdr) { | |
123 if (reader_.ReadBit()) { | |
124 fhdr->display_width = reader_.ReadLiteral(16) + 1; | |
125 fhdr->display_height = reader_.ReadLiteral(16) + 1; | |
126 } else { | |
127 fhdr->display_width = fhdr->width; | |
128 fhdr->display_height = fhdr->height; | |
129 } | |
130 } | |
131 | |
132 Vp9InterpFilter Vp9Parser::ReadInterpFilter() { | |
133 if (reader_.ReadBit()) | |
134 return Vp9InterpFilter::INTERP_FILTER_SELECT; | |
135 | |
136 // The mapping table for next two bits. | |
137 Vp9InterpFilter table[] = { | |
Pawel Osciak
2015/07/30 08:27:37
const?
kcwu1
2015/07/31 04:36:01
Done.
| |
138 Vp9InterpFilter::EIGHTTAP_SMOOTH, Vp9InterpFilter::EIGHTTAP, | |
139 Vp9InterpFilter::EIGHTTAP_SHARP, Vp9InterpFilter::BILINEAR, | |
140 }; | |
141 return table[reader_.ReadLiteral(2)]; | |
Pawel Osciak
2015/07/30 08:27:38
Is this different from return ReadLiteral(2) + 1 ?
kcwu1
2015/07/30 08:55:49
No difference. I just followed libvpx and feel it
Pawel Osciak
2015/07/30 08:59:24
Acknowledged.
| |
142 } | |
143 | |
144 void Vp9Parser::ReadLoopFilter(Vp9LoopFilter* loop_filter) { | |
145 loop_filter->filter_level = reader_.ReadLiteral(6); | |
146 loop_filter->sharpness_level = reader_.ReadLiteral(3); | |
147 | |
148 loop_filter->mode_ref_delta_enabled = reader_.ReadBit(); | |
149 if (loop_filter->mode_ref_delta_enabled) { | |
150 loop_filter->mode_ref_delta_update = reader_.ReadBit(); | |
151 if (loop_filter->mode_ref_delta_update) { | |
152 for (int i = 0; i < Vp9LoopFilter::kNumRefDeltas; i++) { | |
153 loop_filter->update_ref_deltas[i] = reader_.ReadBit(); | |
154 if (loop_filter->update_ref_deltas[i]) | |
155 loop_filter->ref_deltas[i] = reader_.ReadSignedLiteral(6); | |
156 } | |
157 | |
158 for (int i = 0; i < Vp9LoopFilter::kNumModeDeltas; i++) { | |
159 loop_filter->update_mode_deltas[i] = reader_.ReadBit(); | |
160 if (loop_filter->update_mode_deltas[i]) | |
161 loop_filter->mode_deltas[i] = reader_.ReadLiteral(6); | |
162 } | |
163 } | |
164 } else { | |
165 loop_filter->mode_ref_delta_update = false; | |
166 } | |
167 } | |
168 | |
169 void Vp9Parser::ReadQuantization(Vp9QuantizationParams* quants) { | |
170 quants->base_qindex = reader_.ReadLiteral(8); | |
171 | |
172 if (reader_.ReadBit()) | |
173 quants->y_dc_delta = reader_.ReadSignedLiteral(4); | |
174 else | |
175 quants->y_dc_delta = 0; | |
176 | |
177 if (reader_.ReadBit()) | |
178 quants->uv_ac_delta = reader_.ReadSignedLiteral(4); | |
179 else | |
180 quants->uv_ac_delta = 0; | |
181 | |
182 if (reader_.ReadBit()) | |
183 quants->uv_dc_delta = reader_.ReadSignedLiteral(4); | |
184 else | |
185 quants->uv_dc_delta = 0; | |
186 } | |
187 | |
188 void Vp9Parser::ReadSegmentationMap(Vp9Segmentation* segment) { | |
189 for (int i = 0; i < Vp9Segmentation::kTreeProbs; i++) { | |
190 if (reader_.ReadBit()) | |
191 segment->tree_probs[i] = reader_.ReadLiteral(8); | |
192 else | |
193 segment->tree_probs[i] = kVp9MaxProb; | |
194 } | |
195 | |
196 for (int i = 0; i < Vp9Segmentation::kPredictionProbs; i++) | |
197 segment->pred_probs[i] = kVp9MaxProb; | |
198 if (reader_.ReadBit()) { | |
Pawel Osciak
2015/07/30 11:52:31
Please store this in frame header (as temporal_upd
kcwu1
2015/07/31 04:36:01
Done.
| |
199 for (int i = 0; i < Vp9Segmentation::kPredictionProbs; i++) { | |
200 if (reader_.ReadBit()) | |
201 segment->pred_probs[i] = reader_.ReadLiteral(8); | |
202 } | |
203 } | |
204 } | |
205 | |
206 void Vp9Parser::ReadSegmentationData(Vp9Segmentation* segment) { | |
207 segment->abs_delta = reader_.ReadBit(); | |
208 | |
209 const int kFeatureDataBits[] = {7, 6, 2, 0}; | |
210 const bool kFeatureDataSigned[] = {true, true, false, false}; | |
211 | |
212 for (int i = 0; i < Vp9Segmentation::kNumSegment; i++) { | |
213 for (int j = 0; j < Vp9Segmentation::kNumFeature; j++) { | |
214 int8_t data = 0; | |
215 segment->feature_enabled[i][j] = reader_.ReadBit(); | |
216 if (segment->feature_enabled[i][j]) { | |
217 data = reader_.ReadLiteral(kFeatureDataBits[j]); | |
218 if (kFeatureDataSigned[j]) | |
219 if (reader_.ReadBit()) | |
220 data = -data; | |
221 } | |
222 segment->feature_data[i][j] = data; | |
223 } | |
224 } | |
225 } | |
226 | |
227 void Vp9Parser::ReadSegmentation(Vp9Segmentation* segment) { | |
228 segment->enabled = reader_.ReadBit(); | |
229 | |
230 if (!segment->enabled) { | |
231 segment->update_map = false; | |
232 segment->update_data = false; | |
233 return; | |
234 } | |
235 | |
236 segment->update_map = reader_.ReadBit(); | |
237 if (segment->update_map) | |
238 ReadSegmentationMap(segment); | |
239 | |
240 segment->update_data = reader_.ReadBit(); | |
241 if (segment->update_data) | |
242 ReadSegmentationData(segment); | |
243 } | |
244 | |
245 void Vp9Parser::ReadTiles(Vp9FrameHeader* fhdr) { | |
246 int sb64_cols = (fhdr->width + 63) / 64; | |
247 | |
248 int min_log2_tile_cols = GetMinLog2TileCols(sb64_cols); | |
249 int max_log2_tile_cols = GetMaxLog2TileCols(sb64_cols); | |
250 | |
251 int max_ones = max_log2_tile_cols - min_log2_tile_cols; | |
252 fhdr->log2_tile_cols = min_log2_tile_cols; | |
253 while (max_ones-- && reader_.ReadBit()) | |
254 fhdr->log2_tile_cols++; | |
255 | |
256 if (reader_.ReadBit()) | |
257 fhdr->log2_tile_rows = reader_.ReadLiteral(2) - 1; | |
258 else | |
259 fhdr->log2_tile_rows = 0; | |
260 } | |
261 | |
262 bool Vp9Parser::ParseUncompressedHeader(Vp9FrameHeader* fhdr) { | |
263 reader_.Initialize(stream_, size_); | |
264 | |
265 // frame marker | |
266 if (reader_.ReadLiteral(2) != 0x2) | |
267 return false; | |
268 | |
269 fhdr->profile = ReadProfile(); | |
270 if (fhdr->profile >= kVp9MaxProfile) { | |
271 DLOG(ERROR) << "Unsupported bitstream profile"; | |
272 return false; | |
273 } | |
274 | |
275 fhdr->show_existing_frame = reader_.ReadBit(); | |
276 if (fhdr->show_existing_frame) { | |
277 fhdr->frame_to_show = reader_.ReadLiteral(3); | |
278 fhdr->loop_filter.filter_level = 0; | |
Pawel Osciak
2015/07/30 08:27:37
It seems that sometimes we initialize members to 0
kcwu1
2015/07/31 04:36:01
Done.
| |
279 fhdr->show_frame = true; | |
280 | |
281 fhdr->first_partition_size = 0; | |
282 fhdr->compressed_header = nullptr; | |
283 | |
284 if (reader_.IsOutOfBuffer()) { | |
Pawel Osciak
2015/07/30 08:27:37
This shouldn't be needed once we make ReadBit/Lite
kcwu1
2015/07/30 08:55:49
Since out of data is rare, I'd prefer checked only
Pawel Osciak
2015/07/30 08:59:24
We can't read beyond memory that we have available
kcwu1
2015/07/30 09:04:15
The reader returns 0 if out of buffer. The return
| |
285 DLOG(ERROR) << "parser reads beyond the end of buffer"; | |
286 return false; | |
287 } | |
288 return true; | |
289 } | |
290 | |
291 fhdr->frame_type = static_cast<Vp9FrameHeader::FrameType>(reader_.ReadBit()); | |
292 fhdr->show_frame = reader_.ReadBit(); | |
293 fhdr->error_resilient_mode = reader_.ReadBit(); | |
294 | |
295 if (fhdr->IsKeyframe()) { | |
296 if (!VerifySyncCode()) | |
297 return false; | |
298 | |
299 if (!ReadBitDepthColorSpaceSampling(fhdr)) | |
300 return false; | |
301 | |
302 memset(&ref_slots_, 0, sizeof(ref_slots_)); | |
303 for (int i = 0; i < kVp9RefFrames; i++) | |
304 fhdr->refresh_flag[i] = true; | |
305 | |
306 ReadFrameSize(fhdr); | |
307 ReadDisplayFrameSize(fhdr); | |
308 } else { | |
309 if (fhdr->show_frame) | |
310 fhdr->intra_only = false; | |
311 else | |
312 fhdr->intra_only = reader_.ReadBit(); | |
313 if (fhdr->error_resilient_mode) | |
Pawel Osciak
2015/07/30 08:27:37
Please add empty line above.
kcwu1
2015/07/31 04:36:01
Done.
| |
314 fhdr->reset_context = false; | |
315 else | |
316 fhdr->reset_context = reader_.ReadLiteral(2); | |
317 | |
318 if (fhdr->intra_only) { | |
319 if (!VerifySyncCode()) | |
320 return false; | |
321 | |
322 if (fhdr->profile > 0) { | |
323 if (!ReadBitDepthColorSpaceSampling(fhdr)) | |
324 return false; | |
325 } else { | |
326 fhdr->bit_depth = 8; | |
327 fhdr->color_space = Vp9ColorSpace::BT_601; | |
328 fhdr->subsampling_x = fhdr->subsampling_y = 1; | |
329 } | |
330 | |
331 for (int i = 0; i < kVp9RefFrames; i++) | |
332 fhdr->refresh_flag[i] = reader_.ReadBit(); | |
333 ReadFrameSize(fhdr); | |
334 ReadDisplayFrameSize(fhdr); | |
335 } else { | |
336 for (int i = 0; i < kVp9RefFrames; i++) | |
337 fhdr->refresh_flag[i] = reader_.ReadBit(); | |
338 | |
339 for (int i = 0; i < kVp9RefsPerFrame; i++) { | |
340 fhdr->frame_refs[i] = reader_.ReadLiteral(kVp9RefFramesLog2); | |
341 fhdr->ref_sign_biases[i] = reader_.ReadBit(); | |
342 } | |
343 | |
344 ReadFrameSizeFromRefs(fhdr); | |
345 ReadDisplayFrameSize(fhdr); | |
346 | |
347 fhdr->allow_high_precision_mv = reader_.ReadBit(); | |
348 fhdr->interp_filter = ReadInterpFilter(); | |
349 } | |
350 } | |
351 | |
352 if (fhdr->error_resilient_mode) { | |
353 fhdr->refresh_frame_context = false; | |
354 fhdr->frame_parallel_decoding_mode = true; | |
355 } else { | |
356 fhdr->refresh_frame_context = reader_.ReadBit(); | |
357 fhdr->frame_parallel_decoding_mode = reader_.ReadBit(); | |
358 } | |
359 | |
360 const int kFrameContextLog2 = 2; | |
Pawel Osciak
2015/07/30 08:27:37
Any reason to make this specifically into a consta
kcwu1
2015/07/30 08:55:49
Do you mean why not use literal 2 directly?
Pawel Osciak
2015/07/30 08:59:24
Yes.
kcwu1
2015/07/31 04:36:01
Done.
| |
361 fhdr->frame_context_idx = reader_.ReadLiteral(kFrameContextLog2); | |
362 | |
363 ReadLoopFilter(&fhdr->loop_filter); | |
364 ReadQuantization(&fhdr->quant_params); | |
365 ReadSegmentation(&fhdr->segment); | |
366 | |
367 ReadTiles(fhdr); | |
368 | |
369 fhdr->first_partition_size = reader_.ReadLiteral(16); | |
370 if (fhdr->first_partition_size == 0) { | |
371 DLOG(ERROR) << "invalid header size"; | |
372 return false; | |
373 } | |
374 fhdr->compressed_header = stream_ + reader_.GetBytesRead(); | |
Pawel Osciak
2015/07/30 11:52:31
Could we also store the size of uncompressed heade
kcwu1
2015/07/31 04:36:01
Done.
And remove |compressed_header| since it is r
| |
375 | |
376 if (reader_.IsOutOfBuffer()) { | |
377 DLOG(ERROR) << "parser reads beyond the end of buffer"; | |
378 return false; | |
379 } | |
380 | |
381 return true; | |
382 } | |
383 | |
384 void Vp9Parser::UpdateSlots(Vp9FrameHeader* fhdr) { | |
385 for (int i = 0; i < kVp9RefFrames; i++) { | |
386 if (fhdr->refresh_flag[i]) { | |
387 ref_slots_[i].used = true; | |
388 ref_slots_[i].width = fhdr->width; | |
389 ref_slots_[i].height = fhdr->height; | |
390 } | |
391 } | |
392 } | |
393 | |
394 bool Vp9Parser::ParseFrame(const uint8_t* ptr, | |
395 size_t frame_size, | |
396 Vp9FrameHeader* fhdr) { | |
397 stream_ = ptr; | |
398 size_ = frame_size; | |
399 memset(fhdr, 0, sizeof(*fhdr)); | |
400 | |
401 if (!ParseUncompressedHeader(fhdr)) | |
402 return false; | |
403 | |
404 UpdateSlots(fhdr); | |
405 | |
406 return true; | |
407 } | |
408 | |
409 } // namespace media | |
OLD | NEW |