• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS.  All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "src/block_parser.h"
9 
10 #include <cassert>
11 #include <cstdint>
12 #include <numeric>
13 #include <type_traits>
14 #include <vector>
15 
16 #include "src/parser_utils.h"
17 #include "webm/element.h"
18 
19 namespace webm {
20 
21 namespace {
22 
23 // The ParseBasicBlockFlags functions parse extra flag bits into the block,
24 // depending on the type of block that is being parsed.
ParseBasicBlockFlags(std::uint8_t,Block *)25 void ParseBasicBlockFlags(std::uint8_t /* flags */, Block* /* block */) {
26   // Block has no extra flags that aren't already handled.
27 }
28 
ParseBasicBlockFlags(std::uint8_t flags,SimpleBlock * block)29 void ParseBasicBlockFlags(std::uint8_t flags, SimpleBlock* block) {
30   block->is_key_frame = (0x80 & flags) != 0;
31   block->is_discardable = (0x01 & flags) != 0;
32 }
33 
34 // The BasicBlockBegin functions call the Callback event handler and get the
35 // correct action for the parser, depending on the type of block that is being
36 // parsed.
BasicBlockBegin(const ElementMetadata & metadata,const Block & block,Callback * callback,Action * action)37 Status BasicBlockBegin(const ElementMetadata& metadata, const Block& block,
38                        Callback* callback, Action* action) {
39   return callback->OnBlockBegin(metadata, block, action);
40 }
41 
BasicBlockBegin(const ElementMetadata & metadata,const SimpleBlock & block,Callback * callback,Action * action)42 Status BasicBlockBegin(const ElementMetadata& metadata,
43                        const SimpleBlock& block, Callback* callback,
44                        Action* action) {
45   return callback->OnSimpleBlockBegin(metadata, block, action);
46 }
47 
48 // The BasicBlockEnd functions call the Callback event handler depending on the
49 // type of block that is being parsed.
BasicBlockEnd(const ElementMetadata & metadata,const Block & block,Callback * callback)50 Status BasicBlockEnd(const ElementMetadata& metadata, const Block& block,
51                      Callback* callback) {
52   return callback->OnBlockEnd(metadata, block);
53 }
54 
BasicBlockEnd(const ElementMetadata & metadata,const SimpleBlock & block,Callback * callback)55 Status BasicBlockEnd(const ElementMetadata& metadata, const SimpleBlock& block,
56                      Callback* callback) {
57   return callback->OnSimpleBlockEnd(metadata, block);
58 }
59 
60 }  // namespace
61 
62 template <typename T>
Init(const ElementMetadata & metadata,std::uint64_t max_size)63 Status BasicBlockParser<T>::Init(const ElementMetadata& metadata,
64                                  std::uint64_t max_size) {
65   assert(metadata.size == kUnknownElementSize || metadata.size <= max_size);
66 
67   if (metadata.size == kUnknownElementSize || metadata.size < 5) {
68     return Status(Status::kInvalidElementSize);
69   }
70 
71   *this = {};
72   frame_metadata_.parent_element = metadata;
73 
74   return Status(Status::kOkCompleted);
75 }
76 
77 template <typename T>
Feed(Callback * callback,Reader * reader,std::uint64_t * num_bytes_read)78 Status BasicBlockParser<T>::Feed(Callback* callback, Reader* reader,
79                                  std::uint64_t* num_bytes_read) {
80   assert(callback != nullptr);
81   assert(reader != nullptr);
82   assert(num_bytes_read != nullptr);
83 
84   *num_bytes_read = 0;
85 
86   Status status;
87   std::uint64_t local_num_bytes_read;
88 
89   while (true) {
90     switch (state_) {
91       case State::kReadingHeader: {
92         status = header_parser_.Feed(callback, reader, &local_num_bytes_read);
93         *num_bytes_read += local_num_bytes_read;
94         header_bytes_read_ += local_num_bytes_read;
95         if (!status.completed_ok()) {
96           return status;
97         }
98         value_.track_number = header_parser_.value().track_number;
99         value_.timecode = header_parser_.value().timecode;
100 
101         std::uint8_t flags = header_parser_.value().flags;
102         value_.is_visible = (0x08 & flags) == 0;
103         value_.lacing = static_cast<Lacing>(flags & 0x06);
104         ParseBasicBlockFlags(flags, &value_);
105 
106         if (value_.lacing == Lacing::kNone) {
107           value_.num_frames = 1;
108           state_ = State::kGettingAction;
109         } else {
110           state_ = State::kReadingLaceCount;
111         }
112         continue;
113       }
114 
115       case State::kReadingLaceCount: {
116         assert(lace_sizes_.empty());
117         std::uint8_t lace_count;
118         status = ReadByte(reader, &lace_count);
119         if (!status.completed_ok()) {
120           return status;
121         }
122         ++*num_bytes_read;
123         ++header_bytes_read_;
124         // Lace count is stored as (count - 1).
125         value_.num_frames = lace_count + 1;
126         state_ = State::kGettingAction;
127         continue;
128       }
129 
130       case State::kGettingAction: {
131         Action action = Action::kRead;
132         status = BasicBlockBegin(frame_metadata_.parent_element, value_,
133                                  callback, &action);
134         if (!status.completed_ok()) {
135           return status;
136         }
137         if (action == Action::kSkip) {
138           state_ = State::kSkipping;
139         } else if (value_.lacing == Lacing::kNone || value_.num_frames == 1) {
140           state_ = State::kValidatingSize;
141         } else if (value_.lacing == Lacing::kXiph) {
142           state_ = State::kReadingXiphLaceSizes;
143         } else if (value_.lacing == Lacing::kEbml) {
144           state_ = State::kReadingFirstEbmlLaceSize;
145         } else {
146           state_ = State::kCalculatingFixedLaceSizes;
147         }
148         continue;
149       }
150 
151       case State::kReadingXiphLaceSizes:
152         assert(value_.num_frames > 0);
153         while (static_cast<int>(lace_sizes_.size()) < value_.num_frames - 1) {
154           std::uint8_t byte;
155           do {
156             status = ReadByte(reader, &byte);
157             if (!status.completed_ok()) {
158               return status;
159             }
160             ++*num_bytes_read;
161             ++header_bytes_read_;
162             xiph_lace_size_ += byte;
163           } while (byte == 255);
164 
165           lace_sizes_.push_back(xiph_lace_size_);
166           xiph_lace_size_ = 0;
167         }
168         state_ = State::kValidatingSize;
169         continue;
170 
171       case State::kReadingFirstEbmlLaceSize:
172         assert(value_.num_frames > 0);
173         assert(lace_sizes_.empty());
174         status = uint_parser_.Feed(callback, reader, &local_num_bytes_read);
175         *num_bytes_read += local_num_bytes_read;
176         header_bytes_read_ += local_num_bytes_read;
177         if (!status.completed_ok()) {
178           return status;
179         }
180         lace_sizes_.push_back(uint_parser_.value());
181         uint_parser_ = {};
182         state_ = State::kReadingEbmlLaceSizes;
183         continue;
184 
185       case State::kReadingEbmlLaceSizes:
186         assert(value_.num_frames > 0);
187         assert(!lace_sizes_.empty());
188         while (static_cast<int>(lace_sizes_.size()) < value_.num_frames - 1) {
189           status = uint_parser_.Feed(callback, reader, &local_num_bytes_read);
190           *num_bytes_read += local_num_bytes_read;
191           header_bytes_read_ += local_num_bytes_read;
192           if (!status.completed_ok()) {
193             return status;
194           }
195           constexpr std::uint64_t one = 1;  // Prettier than a static_cast.
196           std::uint64_t offset =
197               (one << (uint_parser_.encoded_length() * 7 - 1)) - 1;
198           lace_sizes_.push_back(lace_sizes_.back() + uint_parser_.value() -
199                                 offset);
200           uint_parser_ = {};
201         }
202         state_ = State::kValidatingSize;
203         continue;
204 
205       case State::kCalculatingFixedLaceSizes: {
206         assert(value_.num_frames > 0);
207         assert(lace_sizes_.empty());
208         if (header_bytes_read_ >= frame_metadata_.parent_element.size) {
209           return Status(Status::kInvalidElementValue);
210         }
211         std::uint64_t laced_data_size =
212             frame_metadata_.parent_element.size - header_bytes_read_;
213         std::uint64_t frame_size = laced_data_size / value_.num_frames;
214         if (laced_data_size % value_.num_frames != 0) {
215           return Status(Status::kInvalidElementValue);
216         }
217         lace_sizes_.resize(value_.num_frames, frame_size);
218         frame_metadata_.position =
219             frame_metadata_.parent_element.position + header_bytes_read_;
220         frame_metadata_.size = frame_size;
221         state_ = State::kReadingFrames;
222         continue;
223       }
224 
225       case State::kValidatingSize: {
226         std::uint64_t sum = std::accumulate(
227             lace_sizes_.begin(), lace_sizes_.end(), header_bytes_read_);
228         if (sum >= frame_metadata_.parent_element.size) {
229           return Status(Status::kInvalidElementValue);
230         }
231         lace_sizes_.push_back(frame_metadata_.parent_element.size - sum);
232         frame_metadata_.position = frame_metadata_.parent_element.position +
233                                    frame_metadata_.parent_element.header_size +
234                                    header_bytes_read_;
235         frame_metadata_.size = lace_sizes_[0];
236         state_ = State::kReadingFrames;
237         continue;
238       }
239 
240       case State::kSkipping:
241         do {
242           // Skip the remaining part of the header and all of the frames.
243           status = reader->Skip(
244               frame_metadata_.parent_element.size - header_bytes_read_,
245               &local_num_bytes_read);
246           *num_bytes_read += local_num_bytes_read;
247           header_bytes_read_ += local_num_bytes_read;
248         } while (status.code == Status::kOkPartial);
249         return status;
250 
251       case State::kReadingFrames:
252         assert(value_.num_frames > 0);
253         assert(static_cast<int>(lace_sizes_.size()) == value_.num_frames);
254         for (; current_lace_ < lace_sizes_.size(); ++current_lace_) {
255           const std::uint64_t original = lace_sizes_[current_lace_];
256           status = callback->OnFrame(frame_metadata_, reader,
257                                      &lace_sizes_[current_lace_]);
258           *num_bytes_read += original - lace_sizes_[current_lace_];
259           if (!status.completed_ok()) {
260             return status;
261           }
262           assert(lace_sizes_[current_lace_] == 0);
263           if (current_lace_ + 1 < lace_sizes_.size()) {
264             frame_metadata_.position += frame_metadata_.size;
265             frame_metadata_.size = lace_sizes_[current_lace_ + 1];
266           }
267         }
268         state_ = State::kDone;
269         continue;
270 
271       case State::kDone:
272         return BasicBlockEnd(frame_metadata_.parent_element, value_, callback);
273     }
274   }
275 }
276 
277 template <typename T>
WasSkipped() const278 bool BasicBlockParser<T>::WasSkipped() const {
279   return state_ == State::kSkipping;
280 }
281 
282 template class BasicBlockParser<Block>;
283 template class BasicBlockParser<SimpleBlock>;
284 
285 }  // namespace webm
286