/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "WebmElement" #include "EbmlUtil.h" #include "WebmElement.h" #include "WebmConstants.h" #include #include #include #include #include #include #include using namespace android; using namespace webm; namespace { int64_t voidSize(int64_t totalSize) { if (totalSize < 2) { return -1; } if (totalSize < 9) { return totalSize - 2; } return totalSize - 9; } uint64_t childrenSum(const List >& children) { uint64_t total = 0; for (List >::const_iterator it = children.begin(); it != children.end(); ++it) { total += (*it)->totalSize(); } return total; } void populateCommonTrackEntries( int num, uint64_t uid, bool lacing, const char *lang, const char *codec, TrackTypes type, List > &ls) { ls.push_back(new WebmUnsigned(kMkvTrackNumber, num)); ls.push_back(new WebmUnsigned(kMkvTrackUid, uid)); ls.push_back(new WebmUnsigned(kMkvFlagLacing, lacing)); ls.push_back(new WebmString(kMkvLanguage, lang)); ls.push_back(new WebmString(kMkvCodecId, codec)); ls.push_back(new WebmUnsigned(kMkvTrackType, type)); } } namespace android { WebmElement::WebmElement(uint64_t id, uint64_t size) : mId(id), mSize(size) { } WebmElement::~WebmElement() { } int WebmElement::serializePayloadSize(uint8_t *buf) { return serializeCodedUnsigned(encodeUnsigned(mSize), buf); } uint64_t WebmElement::serializeInto(uint8_t *buf) { uint8_t *cur = buf; int head = serializeCodedUnsigned(mId, cur); cur += head; int neck = serializePayloadSize(cur); cur += neck; serializePayload(cur); cur += mSize; return cur - buf; } uint64_t WebmElement::totalSize() { uint8_t buf[8]; //............... + sizeOf(encodeUnsigned(size)) return sizeOf(mId) + serializePayloadSize(buf) + mSize; } uint8_t *WebmElement::serialize(uint64_t& size) { size = totalSize(); uint8_t *buf = new uint8_t[size]; serializeInto(buf); return buf; } int WebmElement::write(int fd, uint64_t& size) { uint8_t buf[8]; size = totalSize(); off64_t off = ::lseek64(fd, (size - 1), SEEK_CUR) - (size - 1); ::write(fd, buf, 1); // extend file off64_t curOff = off + size; off64_t alignedOff = off & ~(::sysconf(_SC_PAGE_SIZE) - 1); off64_t mapSize = curOff - alignedOff; off64_t pageOff = off - alignedOff; void *dst = ::mmap64(NULL, mapSize, PROT_WRITE, MAP_SHARED, fd, alignedOff); if (dst == MAP_FAILED) { ALOGE("mmap64 failed; errno = %d", errno); ALOGE("fd %d; flags: %o", fd, ::fcntl(fd, F_GETFL, 0)); return errno; } else { serializeInto((uint8_t*) dst + pageOff); ::msync(dst, mapSize, MS_SYNC); return ::munmap(dst, mapSize); } } //================================================================================================= WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value) : WebmElement(id, sizeOf(value)), mValue(value) { } void WebmUnsigned::serializePayload(uint8_t *buf) { serializeCodedUnsigned(mValue, buf); } //================================================================================================= WebmFloat::WebmFloat(uint64_t id, double value) : WebmElement(id, sizeof(double)), mValue(value) { } WebmFloat::WebmFloat(uint64_t id, float value) : WebmElement(id, sizeof(float)), mValue(value) { } void WebmFloat::serializePayload(uint8_t *buf) { uint64_t data; if (mSize == sizeof(float)) { float f = mValue; data = *reinterpret_cast(&f); } else { data = *reinterpret_cast(&mValue); } for (int i = mSize - 1; i >= 0; --i) { buf[i] = data & 0xff; data >>= 8; } } //================================================================================================= WebmBinary::WebmBinary(uint64_t id, const sp &ref) : WebmElement(id, ref->size()), mRef(ref) { } void WebmBinary::serializePayload(uint8_t *buf) { memcpy(buf, mRef->data(), mRef->size()); } //================================================================================================= WebmString::WebmString(uint64_t id, const char *str) : WebmElement(id, strlen(str)), mStr(str) { } void WebmString::serializePayload(uint8_t *buf) { memcpy(buf, mStr, strlen(mStr)); } //================================================================================================= WebmSimpleBlock::WebmSimpleBlock( int trackNum, int16_t relTimecode, bool key, const sp& orig) // ............................ trackNum*1 + timecode*2 + flags*1 // ^^^ // Only the least significant byte of trackNum is encoded : WebmElement(kMkvSimpleBlock, orig->size() + 4), mTrackNum(trackNum), mRelTimecode(relTimecode), mKey(key), mRef(orig) { } void WebmSimpleBlock::serializePayload(uint8_t *buf) { serializeCodedUnsigned(encodeUnsigned(mTrackNum), buf); buf[1] = (mRelTimecode & 0xff00) >> 8; buf[2] = mRelTimecode & 0xff; buf[3] = mKey ? 0x80 : 0; memcpy(buf + 4, mRef->data(), mSize - 4); } //================================================================================================= EbmlVoid::EbmlVoid(uint64_t totalSize) : WebmElement(kMkvVoid, voidSize(totalSize)), mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) { CHECK_GE(voidSize(totalSize), 0); } int EbmlVoid::serializePayloadSize(uint8_t *buf) { return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf); } void EbmlVoid::serializePayload(uint8_t *buf) { ::memset(buf, 0, mSize); return; } //================================================================================================= WebmMaster::WebmMaster(uint64_t id, const List >& children) : WebmElement(id, childrenSum(children)), mChildren(children) { } WebmMaster::WebmMaster(uint64_t id) : WebmElement(id, 0) { } int WebmMaster::serializePayloadSize(uint8_t *buf) { if (mSize == 0){ return serializeCodedUnsigned(kMkvUnknownLength, buf); } return WebmElement::serializePayloadSize(buf); } void WebmMaster::serializePayload(uint8_t *buf) { uint64_t off = 0; for (List >::const_iterator it = mChildren.begin(); it != mChildren.end(); ++it) { sp child = (*it); child->serializeInto(buf + off); off += child->totalSize(); } } //================================================================================================= sp WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) { List > cuePointEntryFields; cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTrack, track)); cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueClusterPosition, off)); WebmElement *cueTrackPositions = new WebmMaster(kMkvCueTrackPositions, cuePointEntryFields); cuePointEntryFields.clear(); cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTime, time)); cuePointEntryFields.push_back(cueTrackPositions); return new WebmMaster(kMkvCuePoint, cuePointEntryFields); } sp WebmElement::SeekEntry(uint64_t id, uint64_t off) { List > seekEntryFields; seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id)); seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off)); return new WebmMaster(kMkvSeek, seekEntryFields); } sp WebmElement::EbmlHeader( int ver, int readVer, int maxIdLen, int maxSizeLen, int docVer, int docReadVer) { List > headerFields; headerFields.push_back(new WebmUnsigned(kMkvEbmlVersion, ver)); headerFields.push_back(new WebmUnsigned(kMkvEbmlReadVersion, readVer)); headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxIdlength, maxIdLen)); headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxSizeLength, maxSizeLen)); headerFields.push_back(new WebmString(kMkvDocType, "webm")); headerFields.push_back(new WebmUnsigned(kMkvDocTypeVersion, docVer)); headerFields.push_back(new WebmUnsigned(kMkvDocTypeReadVersion, docReadVer)); return new WebmMaster(kMkvEbml, headerFields); } sp WebmElement::SegmentInfo(uint64_t scale, double dur) { List > segmentInfo; // place duration first; easier to patch segmentInfo.push_back(new WebmFloat(kMkvSegmentDuration, dur)); segmentInfo.push_back(new WebmUnsigned(kMkvTimecodeScale, scale)); segmentInfo.push_back(new WebmString(kMkvMuxingApp, "android")); segmentInfo.push_back(new WebmString(kMkvWritingApp, "android")); return new WebmMaster(kMkvInfo, segmentInfo); } sp WebmElement::AudioTrackEntry( int chans, double rate, const sp &buf, int bps, uint64_t uid, bool lacing, const char *lang) { if (uid == 0) { uid = kAudioTrackNum; } List > trackEntryFields; populateCommonTrackEntries( kAudioTrackNum, uid, lacing, lang, "A_VORBIS", kAudioType, trackEntryFields); List > audioInfo; audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans)); audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate)); if (bps) { WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps); audioInfo.push_back(bitDepth); } trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo)); trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf)); return new WebmMaster(kMkvTrackEntry, trackEntryFields); } sp WebmElement::VideoTrackEntry( uint64_t width, uint64_t height, uint64_t uid, bool lacing, const char *lang) { if (uid == 0) { uid = kVideoTrackNum; } List > trackEntryFields; populateCommonTrackEntries( kVideoTrackNum, uid, lacing, lang, "V_VP8", kVideoType, trackEntryFields); List > videoInfo; videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width)); videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height)); trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo)); return new WebmMaster(kMkvTrackEntry, trackEntryFields); } } /* namespace android */