DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
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/timestamp.h"
13 #include "packager/media/formats/webm/webm_cluster_parser.h"
14 #include "packager/media/formats/webm/webm_constants.h"
15 #include "packager/media/formats/webm/webm_content_encodings.h"
16 #include "packager/media/formats/webm/webm_info_parser.h"
17 #include "packager/media/formats/webm/webm_tracks_parser.h"
18 
19 namespace edash_packager {
20 namespace media {
21 
22 WebMMediaParser::WebMMediaParser()
23  : state_(kWaitingForInit), unknown_segment_size_(false) {}
24 
25 WebMMediaParser::~WebMMediaParser() {}
26 
27 void WebMMediaParser::Init(const InitCB& init_cb,
28  const NewSampleCB& new_sample_cb,
29  KeySource* decryption_key_source) {
30  DCHECK_EQ(state_, kWaitingForInit);
31  DCHECK(init_cb_.is_null());
32  DCHECK(!init_cb.is_null());
33  DCHECK(!new_sample_cb.is_null());
34 
35  ChangeState(kParsingHeaders);
36  init_cb_ = init_cb;
37  new_sample_cb_ = new_sample_cb;
38  decryption_key_source_ = decryption_key_source;
39  ignore_text_tracks_ = true;
40 }
41 
43  DCHECK_NE(state_, kWaitingForInit);
44 
45  byte_queue_.Reset();
46  if (cluster_parser_)
47  cluster_parser_->Flush();
48  if (state_ == kParsingClusters) {
49  ChangeState(kParsingHeaders);
50  }
51 }
52 
53 bool WebMMediaParser::Parse(const uint8_t* buf, int size) {
54  DCHECK_NE(state_, kWaitingForInit);
55 
56  if (state_ == kError)
57  return false;
58 
59  byte_queue_.Push(buf, size);
60 
61  int result = 0;
62  int bytes_parsed = 0;
63  const uint8_t* cur = NULL;
64  int cur_size = 0;
65 
66  byte_queue_.Peek(&cur, &cur_size);
67  while (cur_size > 0) {
68  State oldState = state_;
69  switch (state_) {
70  case kParsingHeaders:
71  result = ParseInfoAndTracks(cur, cur_size);
72  break;
73 
74  case kParsingClusters:
75  result = ParseCluster(cur, cur_size);
76  break;
77 
78  case kWaitingForInit:
79  case kError:
80  return false;
81  }
82 
83  if (result < 0) {
84  ChangeState(kError);
85  return false;
86  }
87 
88  if (state_ == oldState && result == 0)
89  break;
90 
91  DCHECK_GE(result, 0);
92  cur += result;
93  cur_size -= result;
94  bytes_parsed += result;
95  }
96 
97  byte_queue_.Pop(bytes_parsed);
98  return true;
99 }
100 
101 void WebMMediaParser::ChangeState(State new_state) {
102  DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
103  state_ = new_state;
104 }
105 
106 int WebMMediaParser::ParseInfoAndTracks(const uint8_t* data, int size) {
107  DVLOG(2) << "ParseInfoAndTracks()";
108  DCHECK(data);
109  DCHECK_GT(size, 0);
110 
111  const uint8_t* cur = data;
112  int cur_size = size;
113  int bytes_parsed = 0;
114 
115  int id;
116  int64_t element_size;
117  int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
118 
119  if (result <= 0)
120  return result;
121 
122  switch (id) {
123  case kWebMIdEBMLHeader:
124  case kWebMIdSeekHead:
125  case kWebMIdVoid:
126  case kWebMIdCRC32:
127  case kWebMIdCues:
128  case kWebMIdChapters:
129  case kWebMIdTags:
130  case kWebMIdAttachments:
131  // TODO: Implement support for chapters.
132  if (cur_size < (result + element_size)) {
133  // We don't have the whole element yet. Signal we need more data.
134  return 0;
135  }
136  // Skip the element.
137  return result + element_size;
138  break;
139  case kWebMIdCluster:
140  if (!cluster_parser_) {
141  LOG(ERROR) << "Found Cluster element before Info.";
142  return -1;
143  }
144  ChangeState(kParsingClusters);
145  return 0;
146  break;
147  case kWebMIdSegment:
148  // Segment of unknown size indicates live stream.
149  if (element_size == kWebMUnknownSize)
150  unknown_segment_size_ = true;
151  // Just consume the segment header.
152  return result;
153  break;
154  case kWebMIdInfo:
155  // We've found the element we are looking for.
156  break;
157  default: {
158  LOG(ERROR) << "Unexpected element ID 0x" << std::hex << id;
159  return -1;
160  }
161  }
162 
163  WebMInfoParser info_parser;
164  result = info_parser.Parse(cur, cur_size);
165 
166  if (result <= 0)
167  return result;
168 
169  cur += result;
170  cur_size -= result;
171  bytes_parsed += result;
172 
173  WebMTracksParser tracks_parser(ignore_text_tracks_);
174  result = tracks_parser.Parse(cur, cur_size);
175 
176  if (result <= 0)
177  return result;
178 
179  bytes_parsed += result;
180 
181  double timecode_scale_in_us = info_parser.timecode_scale() / 1000.0;
182  int64_t duration_in_us = info_parser.duration() * timecode_scale_in_us;
183 
184  std::vector<scoped_refptr<StreamInfo>> streams;
185  AudioCodec audio_codec = kCodecOpus;
186  if (tracks_parser.audio_stream_info()) {
187  streams.push_back(tracks_parser.audio_stream_info());
188  streams.back()->set_duration(duration_in_us);
189  if (streams.back()->is_encrypted())
190  OnEncryptedMediaInitData(tracks_parser.audio_encryption_key_id());
191  audio_codec = tracks_parser.audio_stream_info()->codec();
192  } else {
193  VLOG(1) << "No audio track info found.";
194  }
195 
196  if (tracks_parser.video_stream_info()) {
197  streams.push_back(tracks_parser.video_stream_info());
198  streams.back()->set_duration(duration_in_us);
199  if (streams.back()->is_encrypted())
200  OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id());
201  } else {
202  VLOG(1) << "No video track info found.";
203  }
204 
205  init_cb_.Run(streams);
206 
207  cluster_parser_.reset(new WebMClusterParser(
208  info_parser.timecode_scale(), tracks_parser.audio_track_num(),
209  tracks_parser.GetAudioDefaultDuration(timecode_scale_in_us),
210  tracks_parser.video_track_num(),
211  tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us),
212  tracks_parser.text_tracks(), tracks_parser.ignored_tracks(),
213  tracks_parser.audio_encryption_key_id(),
214  tracks_parser.video_encryption_key_id(), audio_codec, new_sample_cb_));
215 
216  return bytes_parsed;
217 }
218 
219 int WebMMediaParser::ParseCluster(const uint8_t* data, int size) {
220  if (!cluster_parser_)
221  return -1;
222 
223  int bytes_parsed = cluster_parser_->Parse(data, size);
224  if (bytes_parsed < 0)
225  return bytes_parsed;
226 
227  bool cluster_ended = cluster_parser_->cluster_ended();
228  if (cluster_ended) {
229  ChangeState(kParsingHeaders);
230  }
231 
232  return bytes_parsed;
233 }
234 
235 void WebMMediaParser::OnEncryptedMediaInitData(const std::string& key_id) {
236  NOTIMPLEMENTED() << "WebM decryption is not implemented yet.";
237 }
238 
239 } // namespace media
240 } // namespace edash_packager
bool Parse(const uint8_t *buf, int size) override
void Init(const InitCB &init_cb, const NewSampleCB &new_sample_cb, KeySource *decryption_key_source) override
StreamParser implementation.
void Push(const uint8_t *data, int size)
Append new bytes to the end of the queue.
Definition: byte_queue.cc:29
base::Callback< bool(uint32_t track_id, const scoped_refptr< MediaSample > &media_sample)> NewSampleCB
Definition: media_parser.h:43
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:29
void Reset()
Reset the queue to the empty state.
Definition: byte_queue.cc:24
void Peek(const uint8_t **data, int *size) const
Definition: byte_queue.cc:63