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(const char * codec,int chans,double rate,const sp<ABuffer> & buf,int bps,uint64_t uid,bool lacing,const char * lang)307 sp<WebmElement> WebmElement::AudioTrackEntry(
308 const char *codec,
309 int chans,
310 double rate,
311 const sp<ABuffer> &buf,
312 int bps,
313 uint64_t uid,
314 bool lacing,
315 const char *lang) {
316 if (uid == 0) {
317 uid = kAudioTrackNum;
318 }
319
320 List<sp<WebmElement> > trackEntryFields;
321 populateCommonTrackEntries(
322 kAudioTrackNum,
323 uid,
324 lacing,
325 lang,
326 codec,
327 kAudioType,
328 trackEntryFields);
329
330 List<sp<WebmElement> > audioInfo;
331 audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans));
332 audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate));
333 if (bps) {
334 WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps);
335 audioInfo.push_back(bitDepth);
336 }
337
338 trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo));
339 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf));
340 return new WebmMaster(kMkvTrackEntry, trackEntryFields);
341 }
342
VideoTrackEntry(const char * codec,uint64_t width,uint64_t height,const sp<MetaData> & meta,uint64_t uid,bool lacing,const char * lang)343 sp<WebmElement> WebmElement::VideoTrackEntry(
344 const char *codec,
345 uint64_t width,
346 uint64_t height,
347 const sp<MetaData> &meta,
348 uint64_t uid,
349 bool lacing,
350 const char *lang) {
351 if (uid == 0) {
352 uid = kVideoTrackNum;
353 }
354
355 List<sp<WebmElement> > trackEntryFields;
356 populateCommonTrackEntries(
357 kVideoTrackNum,
358 uid,
359 lacing,
360 lang,
361 codec,
362 kVideoType,
363 trackEntryFields);
364
365 // CSD
366 uint32_t type;
367 const void *data;
368 size_t size;
369 if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) {
370 sp<ABuffer> buf = new ABuffer((void *)data, size); // note: buf does not own data
371 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf));
372 }
373
374 List<sp<WebmElement> > videoInfo;
375 videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width));
376 videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height));
377
378 // Color aspects
379 {
380 List<sp<WebmElement> > colorInfo;
381
382 ColorAspects aspects;
383 aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
384 aspects.mTransfer = ColorAspects::TransferUnspecified;
385 aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
386 aspects.mRange = ColorAspects::RangeUnspecified;
387 bool havePrimaries = meta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries);
388 bool haveTransfer = meta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer);
389 bool haveCoeffs = meta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs);
390 bool haveRange = meta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange);
391
392 int32_t primaries, transfer, coeffs;
393 bool fullRange;
394 ColorUtils::convertCodecColorAspectsToIsoAspects(
395 aspects, &primaries, &transfer, &coeffs, &fullRange);
396 if (havePrimaries) {
397 colorInfo.push_back(new WebmUnsigned(kMkvPrimaries, primaries));
398 }
399 if (haveTransfer) {
400 colorInfo.push_back(new WebmUnsigned(kMkvTransferCharacteristics, transfer));
401 }
402 if (haveCoeffs) {
403 colorInfo.push_back(new WebmUnsigned(kMkvMatrixCoefficients, coeffs));
404 }
405 if (haveRange) {
406 colorInfo.push_back(new WebmUnsigned(kMkvRange, fullRange ? 2 : 1));
407 }
408
409 // Also add HDR static info, some of which goes to MasteringMetadata element
410
411 const HDRStaticInfo *info;
412 uint32_t type;
413 const void *data;
414 size_t size;
415 if (meta->findData(kKeyHdrStaticInfo, &type, &data, &size)
416 && type == 'hdrS' && size == sizeof(*info)) {
417 info = (const HDRStaticInfo*)data;
418 if (info->mID == HDRStaticInfo::kType1) {
419 List<sp<WebmElement> > masteringInfo;
420
421 // convert HDRStaticInfo values to matroska equivalent values for each non-0 group
422 if (info->sType1.mMaxFrameAverageLightLevel) {
423 colorInfo.push_back(new WebmUnsigned(
424 kMkvMaxFALL, info->sType1.mMaxFrameAverageLightLevel));
425 }
426 if (info->sType1.mMaxContentLightLevel) {
427 colorInfo.push_back(new WebmUnsigned(
428 kMkvMaxCLL, info->sType1.mMaxContentLightLevel));
429 }
430 if (info->sType1.mMinDisplayLuminance) {
431 // HDRStaticInfo Type1 stores min luminance scaled 10000:1
432 masteringInfo.push_back(new WebmFloat(
433 kMkvLuminanceMin, info->sType1.mMinDisplayLuminance * 0.0001));
434 }
435 if (info->sType1.mMaxDisplayLuminance) {
436 masteringInfo.push_back(new WebmFloat(
437 kMkvLuminanceMax, (float)info->sType1.mMaxDisplayLuminance));
438 }
439 // HDRStaticInfo Type1 stores primaries scaled 50000:1
440 if (info->sType1.mW.x || info->sType1.mW.y) {
441 masteringInfo.push_back(new WebmFloat(
442 kMkvWhitePointChromaticityX, info->sType1.mW.x * 0.00002));
443 masteringInfo.push_back(new WebmFloat(
444 kMkvWhitePointChromaticityY, info->sType1.mW.y * 0.00002));
445 }
446 if (info->sType1.mR.x || info->sType1.mR.y || info->sType1.mG.x
447 || info->sType1.mG.y || info->sType1.mB.x || info->sType1.mB.y) {
448 masteringInfo.push_back(new WebmFloat(
449 kMkvPrimaryRChromaticityX, info->sType1.mR.x * 0.00002));
450 masteringInfo.push_back(new WebmFloat(
451 kMkvPrimaryRChromaticityY, info->sType1.mR.y * 0.00002));
452 masteringInfo.push_back(new WebmFloat(
453 kMkvPrimaryGChromaticityX, info->sType1.mG.x * 0.00002));
454 masteringInfo.push_back(new WebmFloat(
455 kMkvPrimaryGChromaticityY, info->sType1.mG.y * 0.00002));
456 masteringInfo.push_back(new WebmFloat(
457 kMkvPrimaryBChromaticityX, info->sType1.mB.x * 0.00002));
458 masteringInfo.push_back(new WebmFloat(
459 kMkvPrimaryBChromaticityY, info->sType1.mB.y * 0.00002));
460 }
461 if (masteringInfo.size()) {
462 colorInfo.push_back(new WebmMaster(kMkvMasteringMetadata, masteringInfo));
463 }
464 }
465 }
466 if (colorInfo.size()) {
467 videoInfo.push_back(new WebmMaster(kMkvColour, colorInfo));
468 }
469 }
470
471 trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo));
472 return new WebmMaster(kMkvTrackEntry, trackEntryFields);
473 }
474 } /* namespace android */
475