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