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