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 <media/stagefright/foundation/ColorUtils.h> 26 #include <media/stagefright/MetaData.h> 27 #include <utils/Log.h> 28 29 #include <string.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <sys/mman.h> 34 35 using namespace android; 36 using namespace webm; 37 38 namespace { 39 40 int64_t voidSize(int64_t totalSize) { 41 if (totalSize < 2) { 42 return -1; 43 } 44 if (totalSize < 9) { 45 return totalSize - 2; 46 } 47 return totalSize - 9; 48 } 49 50 uint64_t childrenSum(const List<sp<WebmElement> >& children) { 51 uint64_t total = 0; 52 for (List<sp<WebmElement> >::const_iterator it = children.begin(); 53 it != children.end(); ++it) { 54 total += (*it)->totalSize(); 55 } 56 return total; 57 } 58 59 void populateCommonTrackEntries( 60 int num, 61 uint64_t uid, 62 bool lacing, 63 const char *lang, 64 const char *codec, 65 TrackTypes type, 66 List<sp<WebmElement> > &ls) { 67 ls.push_back(new WebmUnsigned(kMkvTrackNumber, num)); 68 ls.push_back(new WebmUnsigned(kMkvTrackUid, uid)); 69 ls.push_back(new WebmUnsigned(kMkvFlagLacing, lacing)); 70 ls.push_back(new WebmString(kMkvLanguage, lang)); 71 ls.push_back(new WebmString(kMkvCodecId, codec)); 72 ls.push_back(new WebmUnsigned(kMkvTrackType, type)); 73 } 74 } 75 76 namespace android { 77 78 WebmElement::WebmElement(uint64_t id, uint64_t size) 79 : mId(id), mSize(size) { 80 } 81 82 WebmElement::~WebmElement() { 83 } 84 85 int WebmElement::serializePayloadSize(uint8_t *buf) { 86 return serializeCodedUnsigned(encodeUnsigned(mSize), buf); 87 } 88 89 uint64_t WebmElement::serializeInto(uint8_t *buf) { 90 uint8_t *cur = buf; 91 int head = serializeCodedUnsigned(mId, cur); 92 cur += head; 93 int neck = serializePayloadSize(cur); 94 cur += neck; 95 serializePayload(cur); 96 cur += mSize; 97 return cur - buf; 98 } 99 100 uint64_t WebmElement::totalSize() { 101 uint8_t buf[8]; 102 //............... + sizeOf(encodeUnsigned(size)) 103 return sizeOf(mId) + serializePayloadSize(buf) + mSize; 104 } 105 106 uint8_t *WebmElement::serialize(uint64_t& size) { 107 size = totalSize(); 108 uint8_t *buf = new uint8_t[size]; 109 serializeInto(buf); 110 return buf; 111 } 112 113 int WebmElement::write(int fd, uint64_t& size) { 114 uint8_t buf[8]; 115 size = totalSize(); 116 off64_t off = ::lseek64(fd, (size - 1), SEEK_CUR) - (size - 1); 117 ::write(fd, buf, 1); // extend file 118 119 off64_t curOff = off + size; 120 off64_t alignedOff = off & ~(::sysconf(_SC_PAGE_SIZE) - 1); 121 off64_t mapSize = curOff - alignedOff; 122 off64_t pageOff = off - alignedOff; 123 void *dst = ::mmap64(NULL, mapSize, PROT_WRITE, MAP_SHARED, fd, alignedOff); 124 if (dst == MAP_FAILED) { 125 ALOGE("mmap64 failed; errno = %d", errno); 126 ALOGE("fd %d; flags: %o", fd, ::fcntl(fd, F_GETFL, 0)); 127 return errno; 128 } else { 129 serializeInto((uint8_t*) dst + pageOff); 130 ::msync(dst, mapSize, MS_SYNC); 131 return ::munmap(dst, mapSize); 132 } 133 } 134 135 //================================================================================================= 136 137 WebmUnsigned::WebmUnsigned(uint64_t id, uint64_t value) 138 : WebmElement(id, sizeOf(value)), mValue(value) { 139 } 140 141 void WebmUnsigned::serializePayload(uint8_t *buf) { 142 serializeCodedUnsigned(mValue, buf); 143 } 144 145 //================================================================================================= 146 147 WebmFloat::WebmFloat(uint64_t id, double value) 148 : WebmElement(id, sizeof(double)), mValue(value) { 149 } 150 151 WebmFloat::WebmFloat(uint64_t id, float value) 152 : WebmElement(id, sizeof(float)), mValue(value) { 153 } 154 155 void WebmFloat::serializePayload(uint8_t *buf) { 156 uint64_t data; 157 if (mSize == sizeof(float)) { 158 float f = mValue; 159 data = *reinterpret_cast<const uint32_t*>(&f); 160 } else { 161 data = *reinterpret_cast<const uint64_t*>(&mValue); 162 } 163 for (int i = mSize - 1; i >= 0; --i) { 164 buf[i] = data & 0xff; 165 data >>= 8; 166 } 167 } 168 169 //================================================================================================= 170 171 WebmBinary::WebmBinary(uint64_t id, const sp<ABuffer> &ref) 172 : WebmElement(id, ref->size()), mRef(ref) { 173 } 174 175 void WebmBinary::serializePayload(uint8_t *buf) { 176 memcpy(buf, mRef->data(), mRef->size()); 177 } 178 179 //================================================================================================= 180 181 WebmString::WebmString(uint64_t id, const char *str) 182 : WebmElement(id, strlen(str)), mStr(str) { 183 } 184 185 void WebmString::serializePayload(uint8_t *buf) { 186 memcpy(buf, mStr, strlen(mStr)); 187 } 188 189 //================================================================================================= 190 191 WebmSimpleBlock::WebmSimpleBlock( 192 int trackNum, 193 int16_t relTimecode, 194 bool key, 195 const sp<ABuffer>& orig) 196 // ............................ trackNum*1 + timecode*2 + flags*1 197 // ^^^ 198 // Only the least significant byte of trackNum is encoded 199 : WebmElement(kMkvSimpleBlock, orig->size() + 4), 200 mTrackNum(trackNum), 201 mRelTimecode(relTimecode), 202 mKey(key), 203 mRef(orig) { 204 } 205 206 void WebmSimpleBlock::serializePayload(uint8_t *buf) { 207 serializeCodedUnsigned(encodeUnsigned(mTrackNum), buf); 208 buf[1] = (mRelTimecode & 0xff00) >> 8; 209 buf[2] = mRelTimecode & 0xff; 210 buf[3] = mKey ? 0x80 : 0; 211 memcpy(buf + 4, mRef->data(), mSize - 4); 212 } 213 214 //================================================================================================= 215 216 EbmlVoid::EbmlVoid(uint64_t totalSize) 217 : WebmElement(kMkvVoid, voidSize(totalSize)), 218 mSizeWidth(totalSize - sizeOf(kMkvVoid) - voidSize(totalSize)) { 219 CHECK_GE(voidSize(totalSize), 0); 220 } 221 222 int EbmlVoid::serializePayloadSize(uint8_t *buf) { 223 return serializeCodedUnsigned(encodeUnsigned(mSize, mSizeWidth), buf); 224 } 225 226 void EbmlVoid::serializePayload(uint8_t *buf) { 227 ::memset(buf, 0, mSize); 228 return; 229 } 230 231 //================================================================================================= 232 233 WebmMaster::WebmMaster(uint64_t id, const List<sp<WebmElement> >& children) 234 : WebmElement(id, childrenSum(children)), mChildren(children) { 235 } 236 237 WebmMaster::WebmMaster(uint64_t id) 238 : WebmElement(id, 0) { 239 } 240 241 int WebmMaster::serializePayloadSize(uint8_t *buf) { 242 if (mSize == 0){ 243 return serializeCodedUnsigned(kMkvUnknownLength, buf); 244 } 245 return WebmElement::serializePayloadSize(buf); 246 } 247 248 void WebmMaster::serializePayload(uint8_t *buf) { 249 uint64_t off = 0; 250 for (List<sp<WebmElement> >::const_iterator it = mChildren.begin(); it != mChildren.end(); 251 ++it) { 252 sp<WebmElement> child = (*it); 253 child->serializeInto(buf + off); 254 off += child->totalSize(); 255 } 256 } 257 258 //================================================================================================= 259 260 sp<WebmElement> WebmElement::CuePointEntry(uint64_t time, int track, uint64_t off) { 261 List<sp<WebmElement> > cuePointEntryFields; 262 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTrack, track)); 263 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueClusterPosition, off)); 264 WebmElement *cueTrackPositions = new WebmMaster(kMkvCueTrackPositions, cuePointEntryFields); 265 266 cuePointEntryFields.clear(); 267 cuePointEntryFields.push_back(new WebmUnsigned(kMkvCueTime, time)); 268 cuePointEntryFields.push_back(cueTrackPositions); 269 return new WebmMaster(kMkvCuePoint, cuePointEntryFields); 270 } 271 272 sp<WebmElement> WebmElement::SeekEntry(uint64_t id, uint64_t off) { 273 List<sp<WebmElement> > seekEntryFields; 274 seekEntryFields.push_back(new WebmUnsigned(kMkvSeekId, id)); 275 seekEntryFields.push_back(new WebmUnsigned(kMkvSeekPosition, off)); 276 return new WebmMaster(kMkvSeek, seekEntryFields); 277 } 278 279 sp<WebmElement> WebmElement::EbmlHeader( 280 int ver, 281 int readVer, 282 int maxIdLen, 283 int maxSizeLen, 284 int docVer, 285 int docReadVer) { 286 List<sp<WebmElement> > headerFields; 287 headerFields.push_back(new WebmUnsigned(kMkvEbmlVersion, ver)); 288 headerFields.push_back(new WebmUnsigned(kMkvEbmlReadVersion, readVer)); 289 headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxIdlength, maxIdLen)); 290 headerFields.push_back(new WebmUnsigned(kMkvEbmlMaxSizeLength, maxSizeLen)); 291 headerFields.push_back(new WebmString(kMkvDocType, "webm")); 292 headerFields.push_back(new WebmUnsigned(kMkvDocTypeVersion, docVer)); 293 headerFields.push_back(new WebmUnsigned(kMkvDocTypeReadVersion, docReadVer)); 294 return new WebmMaster(kMkvEbml, headerFields); 295 } 296 297 sp<WebmElement> WebmElement::SegmentInfo(uint64_t scale, double dur) { 298 List<sp<WebmElement> > segmentInfo; 299 // place duration first; easier to patch 300 segmentInfo.push_back(new WebmFloat(kMkvSegmentDuration, dur)); 301 segmentInfo.push_back(new WebmUnsigned(kMkvTimecodeScale, scale)); 302 segmentInfo.push_back(new WebmString(kMkvMuxingApp, "android")); 303 segmentInfo.push_back(new WebmString(kMkvWritingApp, "android")); 304 return new WebmMaster(kMkvInfo, segmentInfo); 305 } 306 307 sp<WebmElement> WebmElement::AudioTrackEntry( 308 int chans, 309 double rate, 310 const sp<ABuffer> &buf, 311 int bps, 312 uint64_t uid, 313 bool lacing, 314 const char *lang) { 315 if (uid == 0) { 316 uid = kAudioTrackNum; 317 } 318 319 List<sp<WebmElement> > trackEntryFields; 320 populateCommonTrackEntries( 321 kAudioTrackNum, 322 uid, 323 lacing, 324 lang, 325 "A_VORBIS", 326 kAudioType, 327 trackEntryFields); 328 329 List<sp<WebmElement> > audioInfo; 330 audioInfo.push_back(new WebmUnsigned(kMkvChannels, chans)); 331 audioInfo.push_back(new WebmFloat(kMkvSamplingFrequency, rate)); 332 if (bps) { 333 WebmElement *bitDepth = new WebmUnsigned(kMkvBitDepth, bps); 334 audioInfo.push_back(bitDepth); 335 } 336 337 trackEntryFields.push_back(new WebmMaster(kMkvAudio, audioInfo)); 338 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf)); 339 return new WebmMaster(kMkvTrackEntry, trackEntryFields); 340 } 341 342 sp<WebmElement> WebmElement::VideoTrackEntry( 343 const char *codec, 344 uint64_t width, 345 uint64_t height, 346 const sp<MetaData> &meta, 347 uint64_t uid, 348 bool lacing, 349 const char *lang) { 350 if (uid == 0) { 351 uid = kVideoTrackNum; 352 } 353 354 List<sp<WebmElement> > trackEntryFields; 355 populateCommonTrackEntries( 356 kVideoTrackNum, 357 uid, 358 lacing, 359 lang, 360 codec, 361 kVideoType, 362 trackEntryFields); 363 364 // CSD 365 uint32_t type; 366 const void *data; 367 size_t size; 368 if (meta->findData(kKeyVp9CodecPrivate, &type, &data, &size)) { 369 sp<ABuffer> buf = new ABuffer((void *)data, size); // note: buf does not own data 370 trackEntryFields.push_back(new WebmBinary(kMkvCodecPrivate, buf)); 371 } 372 373 List<sp<WebmElement> > videoInfo; 374 videoInfo.push_back(new WebmUnsigned(kMkvPixelWidth, width)); 375 videoInfo.push_back(new WebmUnsigned(kMkvPixelHeight, height)); 376 377 // Color aspects 378 { 379 List<sp<WebmElement> > colorInfo; 380 381 ColorAspects aspects; 382 aspects.mPrimaries = ColorAspects::PrimariesUnspecified; 383 aspects.mTransfer = ColorAspects::TransferUnspecified; 384 aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified; 385 aspects.mRange = ColorAspects::RangeUnspecified; 386 bool havePrimaries = meta->findInt32(kKeyColorPrimaries, (int32_t*)&aspects.mPrimaries); 387 bool haveTransfer = meta->findInt32(kKeyTransferFunction, (int32_t*)&aspects.mTransfer); 388 bool haveCoeffs = meta->findInt32(kKeyColorMatrix, (int32_t*)&aspects.mMatrixCoeffs); 389 bool haveRange = meta->findInt32(kKeyColorRange, (int32_t*)&aspects.mRange); 390 391 int32_t primaries, transfer, coeffs; 392 bool fullRange; 393 ColorUtils::convertCodecColorAspectsToIsoAspects( 394 aspects, &primaries, &transfer, &coeffs, &fullRange); 395 if (havePrimaries) { 396 colorInfo.push_back(new WebmUnsigned(kMkvPrimaries, primaries)); 397 } 398 if (haveTransfer) { 399 colorInfo.push_back(new WebmUnsigned(kMkvTransferCharacteristics, transfer)); 400 } 401 if (haveCoeffs) { 402 colorInfo.push_back(new WebmUnsigned(kMkvMatrixCoefficients, coeffs)); 403 } 404 if (haveRange) { 405 colorInfo.push_back(new WebmUnsigned(kMkvRange, fullRange ? 2 : 1)); 406 } 407 408 // Also add HDR static info, some of which goes to MasteringMetadata element 409 410 const HDRStaticInfo *info; 411 uint32_t type; 412 const void *data; 413 size_t size; 414 if (meta->findData(kKeyHdrStaticInfo, &type, &data, &size) 415 && type == 'hdrS' && size == sizeof(*info)) { 416 info = (const HDRStaticInfo*)data; 417 if (info->mID == HDRStaticInfo::kType1) { 418 List<sp<WebmElement> > masteringInfo; 419 420 // convert HDRStaticInfo values to matroska equivalent values for each non-0 group 421 if (info->sType1.mMaxFrameAverageLightLevel) { 422 colorInfo.push_back(new WebmUnsigned( 423 kMkvMaxFALL, info->sType1.mMaxFrameAverageLightLevel)); 424 } 425 if (info->sType1.mMaxContentLightLevel) { 426 colorInfo.push_back(new WebmUnsigned( 427 kMkvMaxCLL, info->sType1.mMaxContentLightLevel)); 428 } 429 if (info->sType1.mMinDisplayLuminance) { 430 // HDRStaticInfo Type1 stores min luminance scaled 10000:1 431 masteringInfo.push_back(new WebmFloat( 432 kMkvLuminanceMin, info->sType1.mMinDisplayLuminance * 0.0001)); 433 } 434 if (info->sType1.mMaxDisplayLuminance) { 435 masteringInfo.push_back(new WebmFloat( 436 kMkvLuminanceMax, (float)info->sType1.mMaxDisplayLuminance)); 437 } 438 // HDRStaticInfo Type1 stores primaries scaled 50000:1 439 if (info->sType1.mW.x || info->sType1.mW.y) { 440 masteringInfo.push_back(new WebmFloat( 441 kMkvWhitePointChromaticityX, info->sType1.mW.x * 0.00002)); 442 masteringInfo.push_back(new WebmFloat( 443 kMkvWhitePointChromaticityY, info->sType1.mW.y * 0.00002)); 444 } 445 if (info->sType1.mR.x || info->sType1.mR.y || info->sType1.mG.x 446 || info->sType1.mG.y || info->sType1.mB.x || info->sType1.mB.y) { 447 masteringInfo.push_back(new WebmFloat( 448 kMkvPrimaryRChromaticityX, info->sType1.mR.x * 0.00002)); 449 masteringInfo.push_back(new WebmFloat( 450 kMkvPrimaryRChromaticityY, info->sType1.mR.y * 0.00002)); 451 masteringInfo.push_back(new WebmFloat( 452 kMkvPrimaryGChromaticityX, info->sType1.mG.x * 0.00002)); 453 masteringInfo.push_back(new WebmFloat( 454 kMkvPrimaryGChromaticityY, info->sType1.mG.y * 0.00002)); 455 masteringInfo.push_back(new WebmFloat( 456 kMkvPrimaryBChromaticityX, info->sType1.mB.x * 0.00002)); 457 masteringInfo.push_back(new WebmFloat( 458 kMkvPrimaryBChromaticityY, info->sType1.mB.y * 0.00002)); 459 } 460 if (masteringInfo.size()) { 461 colorInfo.push_back(new WebmMaster(kMkvMasteringMetadata, masteringInfo)); 462 } 463 } 464 } 465 if (colorInfo.size()) { 466 videoInfo.push_back(new WebmMaster(kMkvColour, colorInfo)); 467 } 468 } 469 470 trackEntryFields.push_back(new WebmMaster(kMkvVideo, videoInfo)); 471 return new WebmMaster(kMkvTrackEntry, trackEntryFields); 472 } 473 } /* namespace android */ 474