1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // #define LOG_NDEBUG 0
18 #define LOG_TAG "WebmElement"
19
20 #include "EbmlUtil.h"
21 #include "WebmElement.h"
22 #include "WebmConstants.h"
23
24 #include <media/stagefright/foundation/ADebug.h>
25 #include <media/stagefright/foundation/ColorUtils.h>
26 #include <media/stagefright/MetaData.h>
27 #include <utils/Log.h>
28
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <sys/mman.h>
34
35 using namespace android;
36 using namespace webm;
37
38 namespace {
39
voidSize(int64_t totalSize)40 int64_t voidSize(int64_t totalSize) {
41 if (totalSize < 2) {
42 return -1;
43 }
44 if (totalSize < 9) {
45 return totalSize - 2;
46 }
47 return totalSize - 9;
48 }
49
childrenSum(const List<sp<WebmElement>> & children)50 uint64_t childrenSum(const List<sp<WebmElement> >& children) {
51 uint64_t total = 0;
52 for (List<sp<WebmElement> >::const_iterator it = children.begin();
53 it != children.end(); ++it) {
54 total += (*it)->totalSize();
55 }
56 return total;
57 }
58
populateCommonTrackEntries(int num,uint64_t uid,bool lacing,const char * lang,const char * codec,TrackTypes type,List<sp<WebmElement>> & ls)59 void populateCommonTrackEntries(
60 int num,
61 uint64_t uid,
62 bool lacing,
63 const char *lang,
64 const char *codec,
65 TrackTypes type,
66 List<sp<WebmElement> > &ls) {
67 ls.push_back(new WebmUnsigned(kMkvTrackNumber, num));
68 ls.push_back(new WebmUnsigned(kMkvTrackUid, uid));
69 ls.push_back(new WebmUnsigned(kMkvFlagLacing, lacing));
70 ls.push_back(new WebmString(kMkvLanguage, lang));
71 ls.push_back(new WebmString(kMkvCodecId, codec));
72 ls.push_back(new WebmUnsigned(kMkvTrackType, type));
73 }
74 }
75
76 namespace android {
77
WebmElement(uint64_t id,uint64_t size)78 WebmElement::WebmElement(uint64_t id, uint64_t size)
79 : mId(id), mSize(size) {
80 }
81
~WebmElement()82 WebmElement::~WebmElement() {
83 }
84
serializePayloadSize(uint8_t * buf)85 int WebmElement::serializePayloadSize(uint8_t *buf) {
86 return serializeCodedUnsigned(encodeUnsigned(mSize), buf);
87 }
88
serializeInto(uint8_t * buf)89 uint64_t WebmElement::serializeInto(uint8_t *buf) {
90 uint8_t *cur = buf;
91 int head = serializeCodedUnsigned(mId, cur);
92 cur += head;
93 int neck = serializePayloadSize(cur);
94 cur += neck;
95 serializePayload(cur);
96 cur += mSize;
97 return cur - buf;
98 }
99
totalSize()100 uint64_t WebmElement::totalSize() {
101 uint8_t buf[8];
102 //............... + sizeOf(encodeUnsigned(size))
103 return sizeOf(mId) + serializePayloadSize(buf) + mSize;
104 }
105
serialize(uint64_t & size)106 uint8_t *WebmElement::serialize(uint64_t& size) {
107 size = totalSize();
108 uint8_t *buf = new uint8_t[size];
109 serializeInto(buf);
110 return buf;
111 }
112
write(int fd,uint64_t & size)113 int WebmElement::write(int fd, uint64_t& size) {
114 uint8_t buf[8];
115 size = totalSize();
116 off64_t off = ::lseek64(fd, (size - 1), SEEK_CUR) - (size - 1);
117 ::write(fd, buf, 1); // extend file
118
119 off64_t curOff = off + size;
120 off64_t alignedOff = off & ~(::sysconf(_SC_PAGE_SIZE) - 1);
121 off64_t mapSize = curOff - alignedOff;
122 off64_t pageOff = off - alignedOff;
123 void *dst = ::mmap64(NULL, mapSize, PROT_WRITE, MAP_SHARED, fd, alignedOff);
124 if (dst == MAP_FAILED) {
125 ALOGE("mmap64 failed; errno = %d", errno);
126 ALOGE("fd %d; flags: %o", fd, ::fcntl(fd, F_GETFL, 0));
127 return errno;
128 } else {
129 serializeInto((uint8_t*) dst + pageOff);
130 ::msync(dst, mapSize, MS_SYNC);
131 return ::munmap(dst, mapSize);
132 }
133 }
134
135 //=================================================================================================
136
WebmUnsigned(uint64_t id,uint64_t value)137 WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value)
138 : WebmElement(id, sizeOf(value)), mValue(value) {
139 }
140
serializePayload(uint8_t * buf)141 void WebmUnsigned::serializePayload(uint8_t *buf) {
142 serializeCodedUnsigned(mValue, buf);
143 }
144
145 //=================================================================================================
146
WebmFloat(uint64_t id,double value)147 WebmFloat::WebmFloat(uint64_t id, double value)
148 : WebmElement(id, sizeof(double)), mValue(value) {
149 }
150
WebmFloat(uint64_t id,float value)151 WebmFloat::WebmFloat(uint64_t id, float value)
152 : WebmElement(id, sizeof(float)), mValue(value) {
153 }
154
serializePayload(uint8_t * buf)155 void WebmFloat::serializePayload(uint8_t *buf) {
156 uint64_t data;
157 if (mSize == sizeof(float)) {
158 float f = mValue;
159 data = *reinterpret_cast<const uint32_t*>(&f);
160 } else {
161 data = *reinterpret_cast<const uint64_t*>(&mValue);
162 }
163 for (int i = mSize - 1; i >= 0; --i) {
164 buf[i] = data & 0xff;
165 data >>= 8;
166 }
167 }
168
169 //=================================================================================================
170
WebmBinary(uint64_t id,const sp<ABuffer> & ref)171 WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &ref)
172 : WebmElement(id, ref->size()), mRef(ref) {
173 }
174
serializePayload(uint8_t * buf)175 void WebmBinary::serializePayload(uint8_t *buf) {
176 memcpy(buf, mRef->data(), mRef->size());
177 }
178
179 //=================================================================================================
180
WebmString(uint64_t id,const char * str)181 WebmString::WebmString(uint64_t id, const char *str)
182 : WebmElement(id, strlen(str)), mStr(str) {
183 }
184
serializePayload(uint8_t * buf)185 void WebmString::serializePayload(uint8_t *buf) {
186 memcpy(buf, mStr, strlen(mStr));
187 }
188
189 //=================================================================================================
190
WebmSimpleBlock(int trackNum,int16_t relTimecode,bool key,const sp<ABuffer> & orig)191 WebmSimpleBlock::WebmSimpleBlock(
192 int trackNum,
193 int16_t relTimecode,
194 bool key,
195 const sp<ABuffer>& orig)
196 // ............................ trackNum*1 + timecode*2 + flags*1
197 // ^^^
198 // Only the least significant byte of trackNum is encoded
199 : WebmElement(kMkvSimpleBlock, orig->size() + 4),
200 mTrackNum(trackNum),
201 mRelTimecode(relTimecode),
202 mKey(key),
203 mRef(orig) {
204 }
205
serializePayload(uint8_t * buf)206 void WebmSimpleBlock::serializePayload(uint8_t *buf) {
207 serializeCodedUnsigned(encodeUnsigned(mTrackNum), buf);
208 buf[1] = (mRelTimecode & 0xff00) >> 8;
209 buf[2] = mRelTimecode & 0xff;
210 buf[3] = mKey ? 0x80 : 0;
211 memcpy(buf + 4, mRef->data(), mSize - 4);
212 }
213
214 //=================================================================================================
215
EbmlVoid(uint64_t totalSize)216 EbmlVoid::EbmlVoid(uint64_t totalSize)
217 : WebmElement(kMkvVoid, voidSize(totalSize)),
218 mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) {
219 CHECK_GE(voidSize(totalSize), 0);
220 }
221
serializePayloadSize(uint8_t * buf)222 int EbmlVoid::serializePayloadSize(uint8_t *buf) {
223 return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf);
224 }
225
serializePayload(uint8_t * buf)226 void EbmlVoid::serializePayload(uint8_t *buf) {
227 ::memset(buf, 0, mSize);
228 return;
229 }
230
231 //=================================================================================================
232
WebmMaster(uint64_t id,const List<sp<WebmElement>> & children)233 WebmMaster::WebmMaster(uint64_t id, const List<sp<WebmElement> >& children)
234 : WebmElement(id, childrenSum(children)), mChildren(children) {
235 }
236
WebmMaster(uint64_t id)237 WebmMaster::WebmMaster(uint64_t id)
238 : WebmElement(id, 0) {
239 }
240
serializePayloadSize(uint8_t * buf)241 int WebmMaster::serializePayloadSize(uint8_t *buf) {
242 if (mSize == 0){
243 return serializeCodedUnsigned(kMkvUnknownLength, buf);
244 }
245 return WebmElement::serializePayloadSize(buf);
246 }
247
serializePayload(uint8_t * buf)248 void WebmMaster::serializePayload(uint8_t *buf) {
249 uint64_t off = 0;
250 for (List<sp<WebmElement> >::const_iterator it = mChildren.begin(); it != mChildren.end();
251 ++it) {
252 sp<WebmElement> child = (*it);
253 child->serializeInto(buf + off);
254 off += child->totalSize();
255 }
256 }
257
258 //=================================================================================================
259
CuePointEntry(uint64_t time,int track,uint64_t off)260 sp<WebmElement> WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) {
261 List<sp<WebmElement> > cuePointEntryFields;
262 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTrack, track));
263 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueClusterPosition, off));
264 WebmElement *cueTrackPositions = new WebmMaster(kMkvCueTrackPositions, cuePointEntryFields);
265
266 cuePointEntryFields.clear();
267 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTime, time));
268 cuePointEntryFields.push_back(cueTrackPositions);
269 return new WebmMaster(kMkvCuePoint, cuePointEntryFields);
270 }
271
SeekEntry(uint64_t id,uint64_t off)272 sp<WebmElement> WebmElement::SeekEntry(uint64_t id, uint64_t off) {
273 List<sp<WebmElement> > seekEntryFields;
274 seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id));
275 seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off));
276 return new WebmMaster(kMkvSeek, seekEntryFields);
277 }
278
EbmlHeader(int ver,int readVer,int maxIdLen,int maxSizeLen,int docVer,int docReadVer)279 sp<WebmElement> WebmElement::EbmlHeader(
280 int ver,
281 int readVer,
282 int maxIdLen,
283 int maxSizeLen,
284 int docVer,
285 int docReadVer) {
286 List<sp<WebmElement> > headerFields;
287 headerFields.push_back(new WebmUnsigned(kMkvEbmlVersion, ver));
288 headerFields.push_back(new WebmUnsigned(kMkvEbmlReadVersion, readVer));
289 headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxIdlength, maxIdLen));
290 headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxSizeLength, maxSizeLen));
291 headerFields.push_back(new WebmString(kMkvDocType, "webm"));
292 headerFields.push_back(new WebmUnsigned(kMkvDocTypeVersion, docVer));
293 headerFields.push_back(new WebmUnsigned(kMkvDocTypeReadVersion, docReadVer));
294 return new WebmMaster(kMkvEbml, headerFields);
295 }
296
SegmentInfo(uint64_t scale,double dur)297 sp<WebmElement> WebmElement::SegmentInfo(uint64_t scale, double dur) {
298 List<sp<WebmElement> > segmentInfo;
299 // place duration first; easier to patch
300 segmentInfo.push_back(new WebmFloat(kMkvSegmentDuration, dur));
301 segmentInfo.push_back(new WebmUnsigned(kMkvTimecodeScale, scale));
302 segmentInfo.push_back(new WebmString(kMkvMuxingApp, "android"));
303 segmentInfo.push_back(new WebmString(kMkvWritingApp, "android"));
304 return new WebmMaster(kMkvInfo, segmentInfo);
305 }
306
AudioTrackEntry(int chans,double rate,const sp<ABuffer> & buf,int bps,uint64_t uid,bool lacing,const char * lang)307 sp<WebmElement> WebmElement::AudioTrackEntry(
308 int chans,
309 double rate,
310 const sp<ABuffer> &buf,
311 int bps,
312 uint64_t uid,
313 bool lacing,
314 const char *lang) {
315 if (uid == 0) {
316 uid = kAudioTrackNum;
317 }
318
319 List<sp<WebmElement> > trackEntryFields;
320 populateCommonTrackEntries(
321 kAudioTrackNum,
322 uid,
323 lacing,
324 lang,
325 "A_VORBIS",
326 kAudioType,
327 trackEntryFields);
328
329 List<sp<WebmElement> > audioInfo;
330 audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans));
331 audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate));
332 if (bps) {
333 WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps);
334 audioInfo.push_back(bitDepth);
335 }
336
337 trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo));
338 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf));
339 return new WebmMaster(kMkvTrackEntry, trackEntryFields);
340 }
341
VideoTrackEntry(const char * codec,uint64_t width,uint64_t height,const sp<MetaData> & meta,uint64_t uid,bool lacing,const char * lang)342 sp<WebmElement> WebmElement::VideoTrackEntry(
343 const char *codec,
344 uint64_t width,
345 uint64_t height,
346 const sp<MetaData> &meta,
347 uint64_t uid,
348 bool lacing,
349 const char *lang) {
350 if (uid == 0) {
351 uid = kVideoTrackNum;
352 }
353
354 List<sp<WebmElement> > trackEntryFields;
355 populateCommonTrackEntries(
356 kVideoTrackNum,
357 uid,
358 lacing,
359 lang,
360 codec,
361 kVideoType,
362 trackEntryFields);
363
364 // CSD
365 uint32_t type;
366 const void *data;
367 size_t size;
368 if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) {
369 sp<ABuffer> buf = new ABuffer((void *)data, size); // note: buf does not own data
370 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf));
371 }
372
373 List<sp<WebmElement> > videoInfo;
374 videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width));
375 videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height));
376
377 // Color aspects
378 {
379 List<sp<WebmElement> > colorInfo;
380
381 ColorAspects aspects;
382 aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
383 aspects.mTransfer = ColorAspects::TransferUnspecified;
384 aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
385 aspects.mRange = ColorAspects::RangeUnspecified;
386 bool havePrimaries = meta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries);
387 bool haveTransfer = meta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer);
388 bool haveCoeffs = meta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs);
389 bool haveRange = meta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange);
390
391 int32_t primaries, transfer, coeffs;
392 bool fullRange;
393 ColorUtils::convertCodecColorAspectsToIsoAspects(
394 aspects, &primaries, &transfer, &coeffs, &fullRange);
395 if (havePrimaries) {
396 colorInfo.push_back(new WebmUnsigned(kMkvPrimaries, primaries));
397 }
398 if (haveTransfer) {
399 colorInfo.push_back(new WebmUnsigned(kMkvTransferCharacteristics, transfer));
400 }
401 if (haveCoeffs) {
402 colorInfo.push_back(new WebmUnsigned(kMkvMatrixCoefficients, coeffs));
403 }
404 if (haveRange) {
405 colorInfo.push_back(new WebmUnsigned(kMkvRange, fullRange ? 2 : 1));
406 }
407
408 // Also add HDR static info, some of which goes to MasteringMetadata element
409
410 const HDRStaticInfo *info;
411 uint32_t type;
412 const void *data;
413 size_t size;
414 if (meta->findData(kKeyHdrStaticInfo, &type, &data, &size)
415 && type == 'hdrS' && size == sizeof(*info)) {
416 info = (const HDRStaticInfo*)data;
417 if (info->mID == HDRStaticInfo::kType1) {
418 List<sp<WebmElement> > masteringInfo;
419
420 // convert HDRStaticInfo values to matroska equivalent values for each non-0 group
421 if (info->sType1.mMaxFrameAverageLightLevel) {
422 colorInfo.push_back(new WebmUnsigned(
423 kMkvMaxFALL, info->sType1.mMaxFrameAverageLightLevel));
424 }
425 if (info->sType1.mMaxContentLightLevel) {
426 colorInfo.push_back(new WebmUnsigned(
427 kMkvMaxCLL, info->sType1.mMaxContentLightLevel));
428 }
429 if (info->sType1.mMinDisplayLuminance) {
430 // HDRStaticInfo Type1 stores min luminance scaled 10000:1
431 masteringInfo.push_back(new WebmFloat(
432 kMkvLuminanceMin, info->sType1.mMinDisplayLuminance * 0.0001));
433 }
434 if (info->sType1.mMaxDisplayLuminance) {
435 masteringInfo.push_back(new WebmFloat(
436 kMkvLuminanceMax, (float)info->sType1.mMaxDisplayLuminance));
437 }
438 // HDRStaticInfo Type1 stores primaries scaled 50000:1
439 if (info->sType1.mW.x || info->sType1.mW.y) {
440 masteringInfo.push_back(new WebmFloat(
441 kMkvWhitePointChromaticityX, info->sType1.mW.x * 0.00002));
442 masteringInfo.push_back(new WebmFloat(
443 kMkvWhitePointChromaticityY, info->sType1.mW.y * 0.00002));
444 }
445 if (info->sType1.mR.x || info->sType1.mR.y || info->sType1.mG.x
446 || info->sType1.mG.y || info->sType1.mB.x || info->sType1.mB.y) {
447 masteringInfo.push_back(new WebmFloat(
448 kMkvPrimaryRChromaticityX, info->sType1.mR.x * 0.00002));
449 masteringInfo.push_back(new WebmFloat(
450 kMkvPrimaryRChromaticityY, info->sType1.mR.y * 0.00002));
451 masteringInfo.push_back(new WebmFloat(
452 kMkvPrimaryGChromaticityX, info->sType1.mG.x * 0.00002));
453 masteringInfo.push_back(new WebmFloat(
454 kMkvPrimaryGChromaticityY, info->sType1.mG.y * 0.00002));
455 masteringInfo.push_back(new WebmFloat(
456 kMkvPrimaryBChromaticityX, info->sType1.mB.x * 0.00002));
457 masteringInfo.push_back(new WebmFloat(
458 kMkvPrimaryBChromaticityY, info->sType1.mB.y * 0.00002));
459 }
460 if (masteringInfo.size()) {
461 colorInfo.push_back(new WebmMaster(kMkvMasteringMetadata, masteringInfo));
462 }
463 }
464 }
465 if (colorInfo.size()) {
466 videoInfo.push_back(new WebmMaster(kMkvColour, colorInfo));
467 }
468 }
469
470 trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo));
471 return new WebmMaster(kMkvTrackEntry, trackEntryFields);
472 }
473 } /* namespace android */
474