Shaka Packager SDK
webm_media_parser.cc
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 <packager/media/formats/webm/webm_media_parser.h>
6 
7 #include <string>
8 
9 #include <absl/log/check.h>
10 #include <absl/log/log.h>
11 
12 #include <packager/macros/logging.h>
13 #include <packager/media/base/buffer_writer.h>
14 #include <packager/media/base/timestamp.h>
15 #include <packager/media/formats/webm/webm_cluster_parser.h>
16 #include <packager/media/formats/webm/webm_constants.h>
17 #include <packager/media/formats/webm/webm_content_encodings.h>
18 #include <packager/media/formats/webm/webm_info_parser.h>
19 #include <packager/media/formats/webm/webm_tracks_parser.h>
20 
21 namespace shaka {
22 namespace media {
23 
24 WebMMediaParser::WebMMediaParser()
25  : state_(kWaitingForInit), unknown_segment_size_(false) {}
26 
27 WebMMediaParser::~WebMMediaParser() {}
28 
29 void WebMMediaParser::Init(const InitCB& init_cb,
30  const NewMediaSampleCB& new_media_sample_cb,
31  const NewTextSampleCB&,
32  KeySource* decryption_key_source) {
33  DCHECK_EQ(state_, kWaitingForInit);
34  DCHECK(!init_cb_);
35  DCHECK(init_cb);
36  DCHECK(new_media_sample_cb);
37 
38  ChangeState(kParsingHeaders);
39  init_cb_ = init_cb;
40  new_sample_cb_ = new_media_sample_cb;
41  decryption_key_source_ = decryption_key_source;
42  ignore_text_tracks_ = true;
43 }
44 
45 bool WebMMediaParser::Flush() {
46  DCHECK_NE(state_, kWaitingForInit);
47 
48  byte_queue_.Reset();
49  bool result = true;
50  if (cluster_parser_)
51  result = cluster_parser_->Flush();
52  if (state_ == kParsingClusters) {
53  ChangeState(kParsingHeaders);
54  }
55  return result;
56 }
57 
58 bool WebMMediaParser::Parse(const uint8_t* buf, int size) {
59  DCHECK_NE(state_, kWaitingForInit);
60 
61  if (state_ == kError)
62  return false;
63 
64  byte_queue_.Push(buf, size);
65 
66  int result = 0;
67  int bytes_parsed = 0;
68  const uint8_t* cur = NULL;
69  int cur_size = 0;
70 
71  byte_queue_.Peek(&cur, &cur_size);
72  while (cur_size > 0) {
73  State oldState = state_;
74  switch (state_) {
75  case kParsingHeaders:
76  result = ParseInfoAndTracks(cur, cur_size);
77  break;
78 
79  case kParsingClusters:
80  result = ParseCluster(cur, cur_size);
81  break;
82 
83  case kWaitingForInit:
84  case kError:
85  return false;
86  }
87 
88  if (result < 0) {
89  ChangeState(kError);
90  return false;
91  }
92 
93  if (state_ == oldState && result == 0)
94  break;
95 
96  DCHECK_GE(result, 0);
97  cur += result;
98  cur_size -= result;
99  bytes_parsed += result;
100  }
101 
102  byte_queue_.Pop(bytes_parsed);
103  return true;
104 }
105 
106 void WebMMediaParser::ChangeState(State new_state) {
107  DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
108  state_ = new_state;
109 }
110 
111 int WebMMediaParser::ParseInfoAndTracks(const uint8_t* data, int size) {
112  DVLOG(2) << "ParseInfoAndTracks()";
113  DCHECK(data);
114  DCHECK_GT(size, 0);
115 
116  const uint8_t* cur = data;
117  int cur_size = size;
118  int bytes_parsed = 0;
119 
120  int id;
121  int64_t element_size;
122  int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
123 
124  if (result <= 0)
125  return result;
126 
127  switch (id) {
128  case kWebMIdEBMLHeader:
129  case kWebMIdSeekHead:
130  case kWebMIdVoid:
131  case kWebMIdCRC32:
132  case kWebMIdCues:
133  case kWebMIdChapters:
134  case kWebMIdTags:
135  case kWebMIdAttachments:
136  // TODO: Implement support for chapters.
137  if (cur_size < (result + element_size)) {
138  // We don't have the whole element yet. Signal we need more data.
139  return 0;
140  }
141  // Skip the element.
142  return result + element_size;
143  break;
144  case kWebMIdCluster:
145  if (!cluster_parser_) {
146  LOG(ERROR) << "Found Cluster element before Info.";
147  return -1;
148  }
149  ChangeState(kParsingClusters);
150  return 0;
151  break;
152  case kWebMIdSegment:
153  // Segment of unknown size indicates live stream.
154  if (element_size == kWebMUnknownSize)
155  unknown_segment_size_ = true;
156  // Just consume the segment header.
157  return result;
158  break;
159  case kWebMIdInfo:
160  // We've found the element we are looking for.
161  break;
162  default: {
163  LOG(ERROR) << "Unexpected element ID 0x" << std::hex << id;
164  return -1;
165  }
166  }
167 
168  WebMInfoParser info_parser;
169  result = info_parser.Parse(cur, cur_size);
170 
171  if (result <= 0)
172  return result;
173 
174  cur += result;
175  cur_size -= result;
176  bytes_parsed += result;
177 
178  WebMTracksParser tracks_parser(ignore_text_tracks_);
179  result = tracks_parser.Parse(cur, cur_size);
180 
181  if (result <= 0)
182  return result;
183 
184  bytes_parsed += result;
185 
186  double timecode_scale_in_us = info_parser.timecode_scale() / 1000.0;
187  int64_t duration_in_us = info_parser.duration() * timecode_scale_in_us;
188 
189  std::shared_ptr<AudioStreamInfo> audio_stream_info =
190  tracks_parser.audio_stream_info();
191  if (audio_stream_info) {
192  audio_stream_info->set_duration(duration_in_us);
193  } else {
194  VLOG(1) << "No audio track info found.";
195  }
196 
197  std::shared_ptr<VideoStreamInfo> video_stream_info =
198  tracks_parser.video_stream_info();
199  if (video_stream_info) {
200  video_stream_info->set_duration(duration_in_us);
201  } else {
202  VLOG(1) << "No video track info found.";
203  }
204 
205  if (!FetchKeysIfNecessary(tracks_parser.audio_encryption_key_id(),
206  tracks_parser.video_encryption_key_id())) {
207  return -1;
208  }
209 
210  cluster_parser_.reset(new WebMClusterParser(
211  info_parser.timecode_scale(), audio_stream_info, video_stream_info,
212  tracks_parser.vp_config(),
213  tracks_parser.GetAudioDefaultDuration(timecode_scale_in_us),
214  tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us),
215  tracks_parser.text_tracks(), tracks_parser.ignored_tracks(),
216  tracks_parser.audio_encryption_key_id(),
217  tracks_parser.video_encryption_key_id(), new_sample_cb_, init_cb_,
218  decryption_key_source_));
219 
220  return bytes_parsed;
221 }
222 
223 int WebMMediaParser::ParseCluster(const uint8_t* data, int size) {
224  if (!cluster_parser_)
225  return -1;
226 
227  int bytes_parsed = cluster_parser_->Parse(data, size);
228  if (bytes_parsed < 0)
229  return bytes_parsed;
230 
231  bool cluster_ended = cluster_parser_->cluster_ended();
232  if (cluster_ended) {
233  ChangeState(kParsingHeaders);
234  }
235 
236  return bytes_parsed;
237 }
238 
239 bool WebMMediaParser::FetchKeysIfNecessary(
240  const std::string& audio_encryption_key_id,
241  const std::string& video_encryption_key_id) {
242  if (audio_encryption_key_id.empty() && video_encryption_key_id.empty())
243  return true;
244  // An error will be returned later if the samples need to be decrypted.
245  if (!decryption_key_source_)
246  return true;
247 
248  Status status;
249  if (!audio_encryption_key_id.empty()) {
250  status.Update(decryption_key_source_->FetchKeys(
251  EmeInitDataType::WEBM,
252  std::vector<uint8_t>(audio_encryption_key_id.begin(),
253  audio_encryption_key_id.end())));
254  }
255  if (!video_encryption_key_id.empty()) {
256  status.Update(decryption_key_source_->FetchKeys(
257  EmeInitDataType::WEBM,
258  std::vector<uint8_t>(video_encryption_key_id.begin(),
259  video_encryption_key_id.end())));
260  }
261  if (!status.ok()) {
262  LOG(ERROR) << "Error fetching decryption keys: " << status;
263  return false;
264  }
265  return true;
266 }
267 
268 } // namespace media
269 } // namespace shaka
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:52
std::function< bool(uint32_t track_id, std::shared_ptr< MediaSample > media_sample)> NewMediaSampleCB
Definition: media_parser.h:45
std::function< bool(uint32_t track_id, std::shared_ptr< TextSample > text_sample)> NewTextSampleCB
Definition: media_parser.h:54
std::function< void(const std::vector< std::shared_ptr< StreamInfo > > &stream_info)> InitCB
Definition: media_parser.h:36
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66