• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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