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