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