WebmElement.cpp revision b4a7a2df4c28c3f32b5d877b54831d2cc5d78f81
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
33using namespace android;
34using namespace webm;
35
36namespace {
37
38int64_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
48uint64_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
57void 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
74namespace android {
75
76WebmElement::WebmElement(uint64_t id, uint64_t size)
77    : mId(id), mSize(size) {
78}
79
80WebmElement::~WebmElement() {
81}
82
83int WebmElement::serializePayloadSize(uint8_t *buf) {
84    return serializeCodedUnsigned(encodeUnsigned(mSize), buf);
85}
86
87uint64_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
98uint64_t WebmElement::totalSize() {
99    uint8_t buf[8];
100    //............... + sizeOf(encodeUnsigned(size))
101    return sizeOf(mId) + serializePayloadSize(buf) + mSize;
102}
103
104uint8_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
111int 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
135WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value)
136    : WebmElement(id, sizeOf(value)), mValue(value) {
137}
138
139void WebmUnsigned::serializePayload(uint8_t *buf) {
140    serializeCodedUnsigned(mValue, buf);
141}
142
143//=================================================================================================
144
145WebmFloat::WebmFloat(uint64_t id, double value)
146    : WebmElement(id, sizeof(double)), mValue(value) {
147}
148
149WebmFloat::WebmFloat(uint64_t id, float value)
150    : WebmElement(id, sizeof(float)), mValue(value) {
151}
152
153void 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
169WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &ref)
170    : WebmElement(id, ref->size()), mRef(ref) {
171}
172
173void WebmBinary::serializePayload(uint8_t *buf) {
174    memcpy(buf, mRef->data(), mRef->size());
175}
176
177//=================================================================================================
178
179WebmString::WebmString(uint64_t id, const char *str)
180    : WebmElement(id, strlen(str)), mStr(str) {
181}
182
183void WebmString::serializePayload(uint8_t *buf) {
184    memcpy(buf, mStr, strlen(mStr));
185}
186
187//=================================================================================================
188
189WebmSimpleBlock::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
204void 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
214EbmlVoid::EbmlVoid(uint64_t totalSize)
215    : WebmElement(kMkvVoid, voidSize(totalSize)),
216      mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) {
217    CHECK_GE(voidSize(totalSize), 0);
218}
219
220int EbmlVoid::serializePayloadSize(uint8_t *buf) {
221    return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf);
222}
223
224void EbmlVoid::serializePayload(uint8_t *buf) {
225    ::memset(buf, 0, mSize);
226    return;
227}
228
229//=================================================================================================
230
231WebmMaster::WebmMaster(uint64_t id, const List<sp<WebmElement> >& children)
232    : WebmElement(id, childrenSum(children)), mChildren(children) {
233}
234
235WebmMaster::WebmMaster(uint64_t id)
236    : WebmElement(id, 0) {
237}
238
239int WebmMaster::serializePayloadSize(uint8_t *buf) {
240    if (mSize == 0){
241        return serializeCodedUnsigned(kMkvUnknownLength, buf);
242    }
243    return WebmElement::serializePayloadSize(buf);
244}
245
246void 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
258sp<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
270sp<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
277sp<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
295sp<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
305sp<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
340sp<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