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 <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
21namespace shaka {
22namespace media {
23
24WebMMediaParser::WebMMediaParser()
25 : state_(kWaitingForInit), unknown_segment_size_(false) {}
26
27WebMMediaParser::~WebMMediaParser() {}
28
29void 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
45bool 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
58bool 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
106void WebMMediaParser::ChangeState(State new_state) {
107 DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
108 state_ = new_state;
109}
110
111int 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
223int 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
239bool 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
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.