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