diff options
Diffstat (limited to 'media/libstagefright/webm/WebmElement.cpp')
-rw-r--r-- | media/libstagefright/webm/WebmElement.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/media/libstagefright/webm/WebmElement.cpp b/media/libstagefright/webm/WebmElement.cpp new file mode 100644 index 0000000..a008cab --- /dev/null +++ b/media/libstagefright/webm/WebmElement.cpp @@ -0,0 +1,367 @@ +/* + * 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 <media/stagefright/foundation/ADebug.h> +#include <utils/Log.h> + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> + +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<sp<WebmElement> >& children) { + uint64_t total = 0; + for (List<sp<WebmElement> >::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<sp<WebmElement> > &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<const uint32_t*>(&f); + } else { + data = *reinterpret_cast<const uint64_t*>(&mValue); + } + for (int i = mSize - 1; i >= 0; --i) { + buf[i] = data & 0xff; + data >>= 8; + } +} + +//================================================================================================= + +WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &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<ABuffer>& 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<sp<WebmElement> >& 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<sp<WebmElement> >::const_iterator it = mChildren.begin(); it != mChildren.end(); + ++it) { + sp<WebmElement> child = (*it); + child->serializeInto(buf + off); + off += child->totalSize(); + } +} + +//================================================================================================= + +sp<WebmElement> WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) { + List<sp<WebmElement> > 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> WebmElement::SeekEntry(uint64_t id, uint64_t off) { + List<sp<WebmElement> > seekEntryFields; + seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id)); + seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off)); + return new WebmMaster(kMkvSeek, seekEntryFields); +} + +sp<WebmElement> WebmElement::EbmlHeader( + int ver, + int readVer, + int maxIdLen, + int maxSizeLen, + int docVer, + int docReadVer) { + List<sp<WebmElement> > 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> WebmElement::SegmentInfo(uint64_t scale, double dur) { + List<sp<WebmElement> > 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> WebmElement::AudioTrackEntry( + int chans, + double rate, + const sp<ABuffer> &buf, + int bps, + uint64_t uid, + bool lacing, + const char *lang) { + if (uid == 0) { + uid = kAudioTrackNum; + } + + List<sp<WebmElement> > trackEntryFields; + populateCommonTrackEntries( + kAudioTrackNum, + uid, + lacing, + lang, + "A_VORBIS", + kAudioType, + trackEntryFields); + + List<sp<WebmElement> > 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> WebmElement::VideoTrackEntry( + uint64_t width, + uint64_t height, + uint64_t uid, + bool lacing, + const char *lang) { + if (uid == 0) { + uid = kVideoTrackNum; + } + + List<sp<WebmElement> > trackEntryFields; + populateCommonTrackEntries( + kVideoTrackNum, + uid, + lacing, + lang, + "V_VP8", + kVideoType, + trackEntryFields); + + List<sp<WebmElement> > 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 */ |