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 <utils/Log.h>
26 
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/mman.h>
32 
33 using namespace android;
34 using namespace webm;
35 
36 namespace {
37 
voidSize(int64_t totalSize)38 int64_t voidSize(int64_t totalSize) {
39     if (totalSize < 2) {
40         return -1;
41     }
42     if (totalSize < 9) {
43         return totalSize - 2;
44     }
45     return totalSize - 9;
46 }
47 
childrenSum(const List<sp<WebmElement>> & children)48 uint64_t childrenSum(const List<sp<WebmElement> >& children) {
49     uint64_t total = 0;
50     for (List<sp<WebmElement> >::const_iterator it = children.begin();
51             it != children.end(); ++it) {
52         total += (*it)->totalSize();
53     }
54     return total;
55 }
56 
populateCommonTrackEntries(int num,uint64_t uid,bool lacing,const char * lang,const char * codec,TrackTypes type,List<sp<WebmElement>> & ls)57 void populateCommonTrackEntries(
58         int num,
59         uint64_t uid,
60         bool lacing,
61         const char *lang,
62         const char *codec,
63         TrackTypes type,
64         List<sp<WebmElement> > &ls) {
65     ls.push_back(new WebmUnsigned(kMkvTrackNumber, num));
66     ls.push_back(new WebmUnsigned(kMkvTrackUid, uid));
67     ls.push_back(new WebmUnsigned(kMkvFlagLacing, lacing));
68     ls.push_back(new WebmString(kMkvLanguage, lang));
69     ls.push_back(new WebmString(kMkvCodecId, codec));
70     ls.push_back(new WebmUnsigned(kMkvTrackType, type));
71 }
72 }
73 
74 namespace android {
75 
WebmElement(uint64_t id,uint64_t size)76 WebmElement::WebmElement(uint64_t id, uint64_t size)
77     : mId(id), mSize(size) {
78 }
79 
~WebmElement()80 WebmElement::~WebmElement() {
81 }
82 
serializePayloadSize(uint8_t * buf)83 int WebmElement::serializePayloadSize(uint8_t *buf) {
84     return serializeCodedUnsigned(encodeUnsigned(mSize), buf);
85 }
86 
serializeInto(uint8_t * buf)87 uint64_t WebmElement::serializeInto(uint8_t *buf) {
88     uint8_t *cur = buf;
89     int head = serializeCodedUnsigned(mId, cur);
90     cur += head;
91     int neck = serializePayloadSize(cur);
92     cur += neck;
93     serializePayload(cur);
94     cur += mSize;
95     return cur - buf;
96 }
97 
totalSize()98 uint64_t WebmElement::totalSize() {
99     uint8_t buf[8];
100     //............... + sizeOf(encodeUnsigned(size))
101     return sizeOf(mId) + serializePayloadSize(buf) + mSize;
102 }
103 
serialize(uint64_t & size)104 uint8_t *WebmElement::serialize(uint64_t& size) {
105     size = totalSize();
106     uint8_t *buf = new uint8_t[size];
107     serializeInto(buf);
108     return buf;
109 }
110 
write(int fd,uint64_t & size)111 int WebmElement::write(int fd, uint64_t& size) {
112     uint8_t buf[8];
113     size = totalSize();
114     off64_t off = ::lseek64(fd, (size - 1), SEEK_CUR) - (size - 1);
115     ::write(fd, buf, 1); // extend file
116 
117     off64_t curOff = off + size;
118     off64_t alignedOff = off & ~(::sysconf(_SC_PAGE_SIZE) - 1);
119     off64_t mapSize = curOff - alignedOff;
120     off64_t pageOff = off - alignedOff;
121     void *dst = ::mmap64(NULL, mapSize, PROT_WRITE, MAP_SHARED, fd, alignedOff);
122     if (dst == MAP_FAILED) {
123         ALOGE("mmap64 failed; errno = %d", errno);
124         ALOGE("fd %d; flags: %o", fd, ::fcntl(fd, F_GETFL, 0));
125         return errno;
126     } else {
127         serializeInto((uint8_t*) dst + pageOff);
128         ::msync(dst, mapSize, MS_SYNC);
129         return ::munmap(dst, mapSize);
130     }
131 }
132 
133 //=================================================================================================
134 
WebmUnsigned(uint64_t id,uint64_t value)135 WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value)
136     : WebmElement(id, sizeOf(value)), mValue(value) {
137 }
138 
serializePayload(uint8_t * buf)139 void WebmUnsigned::serializePayload(uint8_t *buf) {
140     serializeCodedUnsigned(mValue, buf);
141 }
142 
143 //=================================================================================================
144 
WebmFloat(uint64_t id,double value)145 WebmFloat::WebmFloat(uint64_t id, double value)
146     : WebmElement(id, sizeof(double)), mValue(value) {
147 }
148 
WebmFloat(uint64_t id,float value)149 WebmFloat::WebmFloat(uint64_t id, float value)
150     : WebmElement(id, sizeof(float)), mValue(value) {
151 }
152 
serializePayload(uint8_t * buf)153 void WebmFloat::serializePayload(uint8_t *buf) {
154     uint64_t data;
155     if (mSize == sizeof(float)) {
156         float f = mValue;
157         data = *reinterpret_cast<const uint32_t*>(&f);
158     } else {
159         data = *reinterpret_cast<const uint64_t*>(&mValue);
160     }
161     for (int i = mSize - 1; i >= 0; --i) {
162         buf[i] = data & 0xff;
163         data >>= 8;
164     }
165 }
166 
167 //=================================================================================================
168 
WebmBinary(uint64_t id,const sp<ABuffer> & ref)169 WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &ref)
170     : WebmElement(id, ref->size()), mRef(ref) {
171 }
172 
serializePayload(uint8_t * buf)173 void WebmBinary::serializePayload(uint8_t *buf) {
174     memcpy(buf, mRef->data(), mRef->size());
175 }
176 
177 //=================================================================================================
178 
WebmString(uint64_t id,const char * str)179 WebmString::WebmString(uint64_t id, const char *str)
180     : WebmElement(id, strlen(str)), mStr(str) {
181 }
182 
serializePayload(uint8_t * buf)183 void WebmString::serializePayload(uint8_t *buf) {
184     memcpy(buf, mStr, strlen(mStr));
185 }
186 
187 //=================================================================================================
188 
WebmSimpleBlock(int trackNum,int16_t relTimecode,bool key,const sp<ABuffer> & orig)189 WebmSimpleBlock::WebmSimpleBlock(
190         int trackNum,
191         int16_t relTimecode,
192         bool key,
193         const sp<ABuffer>& orig)
194     // ............................ trackNum*1 + timecode*2 + flags*1
195     //                                ^^^
196     // Only the least significant byte of trackNum is encoded
197     : WebmElement(kMkvSimpleBlock, orig->size() + 4),
198       mTrackNum(trackNum),
199       mRelTimecode(relTimecode),
200       mKey(key),
201       mRef(orig) {
202 }
203 
serializePayload(uint8_t * buf)204 void WebmSimpleBlock::serializePayload(uint8_t *buf) {
205     serializeCodedUnsigned(encodeUnsigned(mTrackNum), buf);
206     buf[1] = (mRelTimecode & 0xff00) >> 8;
207     buf[2] = mRelTimecode & 0xff;
208     buf[3] = mKey ? 0x80 : 0;
209     memcpy(buf + 4, mRef->data(), mSize - 4);
210 }
211 
212 //=================================================================================================
213 
EbmlVoid(uint64_t totalSize)214 EbmlVoid::EbmlVoid(uint64_t totalSize)
215     : WebmElement(kMkvVoid, voidSize(totalSize)),
216       mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) {
217     CHECK_GE(voidSize(totalSize), 0);
218 }
219 
serializePayloadSize(uint8_t * buf)220 int EbmlVoid::serializePayloadSize(uint8_t *buf) {
221     return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf);
222 }
223 
serializePayload(uint8_t * buf)224 void EbmlVoid::serializePayload(uint8_t *buf) {
225     ::memset(buf, 0, mSize);
226     return;
227 }
228 
229 //=================================================================================================
230 
WebmMaster(uint64_t id,const List<sp<WebmElement>> & children)231 WebmMaster::WebmMaster(uint64_t id, const List<sp<WebmElement> >& children)
232     : WebmElement(id, childrenSum(children)), mChildren(children) {
233 }
234 
WebmMaster(uint64_t id)235 WebmMaster::WebmMaster(uint64_t id)
236     : WebmElement(id, 0) {
237 }
238 
serializePayloadSize(uint8_t * buf)239 int WebmMaster::serializePayloadSize(uint8_t *buf) {
240     if (mSize == 0){
241         return serializeCodedUnsigned(kMkvUnknownLength, buf);
242     }
243     return WebmElement::serializePayloadSize(buf);
244 }
245 
serializePayload(uint8_t * buf)246 void WebmMaster::serializePayload(uint8_t *buf) {
247     uint64_t off = 0;
248     for (List<sp<WebmElement> >::const_iterator it = mChildren.begin(); it != mChildren.end();
249             ++it) {
250         sp<WebmElement> child = (*it);
251         child->serializeInto(buf + off);
252         off += child->totalSize();
253     }
254 }
255 
256 //=================================================================================================
257 
CuePointEntry(uint64_t time,int track,uint64_t off)258 sp<WebmElement> WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) {
259     List<sp<WebmElement> > cuePointEntryFields;
260     cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTrack, track));
261     cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueClusterPosition, off));
262     WebmElement *cueTrackPositions = new WebmMaster(kMkvCueTrackPositions, cuePointEntryFields);
263 
264     cuePointEntryFields.clear();
265     cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTime, time));
266     cuePointEntryFields.push_back(cueTrackPositions);
267     return new WebmMaster(kMkvCuePoint, cuePointEntryFields);
268 }
269 
SeekEntry(uint64_t id,uint64_t off)270 sp<WebmElement> WebmElement::SeekEntry(uint64_t id, uint64_t off) {
271     List<sp<WebmElement> > seekEntryFields;
272     seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id));
273     seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off));
274     return new WebmMaster(kMkvSeek, seekEntryFields);
275 }
276 
EbmlHeader(int ver,int readVer,int maxIdLen,int maxSizeLen,int docVer,int docReadVer)277 sp<WebmElement> WebmElement::EbmlHeader(
278         int ver,
279         int readVer,
280         int maxIdLen,
281         int maxSizeLen,
282         int docVer,
283         int docReadVer) {
284     List<sp<WebmElement> > headerFields;
285     headerFields.push_back(new WebmUnsigned(kMkvEbmlVersion, ver));
286     headerFields.push_back(new WebmUnsigned(kMkvEbmlReadVersion, readVer));
287     headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxIdlength, maxIdLen));
288     headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxSizeLength, maxSizeLen));
289     headerFields.push_back(new WebmString(kMkvDocType, "webm"));
290     headerFields.push_back(new WebmUnsigned(kMkvDocTypeVersion, docVer));
291     headerFields.push_back(new WebmUnsigned(kMkvDocTypeReadVersion, docReadVer));
292     return new WebmMaster(kMkvEbml, headerFields);
293 }
294 
SegmentInfo(uint64_t scale,double dur)295 sp<WebmElement> WebmElement::SegmentInfo(uint64_t scale, double dur) {
296     List<sp<WebmElement> > segmentInfo;
297     // place duration first; easier to patch
298     segmentInfo.push_back(new WebmFloat(kMkvSegmentDuration, dur));
299     segmentInfo.push_back(new WebmUnsigned(kMkvTimecodeScale, scale));
300     segmentInfo.push_back(new WebmString(kMkvMuxingApp, "android"));
301     segmentInfo.push_back(new WebmString(kMkvWritingApp, "android"));
302     return new WebmMaster(kMkvInfo, segmentInfo);
303 }
304 
AudioTrackEntry(int chans,double rate,const sp<ABuffer> & buf,int bps,uint64_t uid,bool lacing,const char * lang)305 sp<WebmElement> WebmElement::AudioTrackEntry(
306         int chans,
307         double rate,
308         const sp<ABuffer> &buf,
309         int bps,
310         uint64_t uid,
311         bool lacing,
312         const char *lang) {
313     if (uid == 0) {
314         uid = kAudioTrackNum;
315     }
316 
317     List<sp<WebmElement> > trackEntryFields;
318     populateCommonTrackEntries(
319             kAudioTrackNum,
320             uid,
321             lacing,
322             lang,
323             "A_VORBIS",
324             kAudioType,
325             trackEntryFields);
326 
327     List<sp<WebmElement> > audioInfo;
328     audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans));
329     audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate));
330     if (bps) {
331         WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps);
332         audioInfo.push_back(bitDepth);
333     }
334 
335     trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo));
336     trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf));
337     return new WebmMaster(kMkvTrackEntry, trackEntryFields);
338 }
339 
VideoTrackEntry(uint64_t width,uint64_t height,uint64_t uid,bool lacing,const char * lang)340 sp<WebmElement> WebmElement::VideoTrackEntry(
341         uint64_t width,
342         uint64_t height,
343         uint64_t uid,
344         bool lacing,
345         const char *lang) {
346     if (uid == 0) {
347         uid = kVideoTrackNum;
348     }
349 
350     List<sp<WebmElement> > trackEntryFields;
351     populateCommonTrackEntries(
352             kVideoTrackNum,
353             uid,
354             lacing,
355             lang,
356             "V_VP8",
357             kVideoType,
358             trackEntryFields);
359 
360     List<sp<WebmElement> > videoInfo;
361     videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width));
362     videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height));
363 
364     trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo));
365     return new WebmMaster(kMkvTrackEntry, trackEntryFields);
366 }
367 } /* namespace android */
368