Home | History | Annotate | Download | only in webm
      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