stagefright: add HEVC support to MediaRecorder

Bug: 22879917
Change-Id: I6c97b051467de44c506a8ff021321e5953a63fc3
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 2a17696..ff0e52e 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -37,7 +37,8 @@
 const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = {
     {"h263", VIDEO_ENCODER_H263},
     {"h264", VIDEO_ENCODER_H264},
-    {"m4v",  VIDEO_ENCODER_MPEG_4_SP}
+    {"m4v",  VIDEO_ENCODER_MPEG_4_SP},
+    {"hevc", VIDEO_ENCODER_HEVC}
 };
 
 const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 73d07a0..405df06 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1226,6 +1226,7 @@
             (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 :
              mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 :
              mVideoEncoder == VIDEO_ENCODER_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 :
+             mVideoEncoder == VIDEO_ENCODER_HEVC ? MEDIA_MIMETYPE_VIDEO_HEVC :
              mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""),
             false /* decoder */, true /* hwCodec */, &codecs);
 
@@ -1515,6 +1516,10 @@
             format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8);
             break;
 
+        case VIDEO_ENCODER_HEVC:
+            format->setString("mime", MEDIA_MIMETYPE_VIDEO_HEVC);
+            break;
+
         default:
             CHECK(!"Should not be here, unsupported video encoding.");
             break;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index cb5cfe3..29f305c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3530,8 +3530,8 @@
         hevcType.eProfile = static_cast<OMX_VIDEO_HEVCPROFILETYPE>(profile);
         hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
     }
-
-    // TODO: Need OMX structure definition for setting iFrameInterval
+    // TODO: finer control?
+    hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate);
 
     err = mOMX->setParameter(
             mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 9a4a350..14a0c66 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -24,6 +24,7 @@
         FLACExtractor.cpp                 \
         FrameRenderTracker.cpp            \
         HTTPBase.cpp                      \
+        HevcUtils.cpp                     \
         JPEGSource.cpp                    \
         MP3Extractor.cpp                  \
         MPEG2TSWriter.cpp                 \
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
new file mode 100644
index 0000000..087c903
--- /dev/null
+++ b/media/libstagefright/HevcUtils.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2015 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 "HevcUtils"
+
+#include <cstring>
+
+#include "include/HevcUtils.h"
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static const uint8_t kHevcNalUnitTypes[5] = {
+    kHevcNalUnitTypeVps,
+    kHevcNalUnitTypeSps,
+    kHevcNalUnitTypePps,
+    kHevcNalUnitTypePrefixSei,
+    kHevcNalUnitTypeSuffixSei,
+};
+
+HevcParameterSets::HevcParameterSets() {
+}
+
+status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) {
+    uint8_t nalUnitType = (data[0] >> 1) & 0x3f;
+    status_t err = OK;
+    switch (nalUnitType) {
+        case 32:  // VPS
+            err = parseVps(data + 2, size - 2);
+            break;
+        case 33:  // SPS
+            err = parseSps(data + 2, size - 2);
+            break;
+        case 34:  // PPS
+            err = parsePps(data + 2, size - 2);
+            break;
+        case 39:  // Prefix SEI
+        case 40:  // Suffix SEI
+            // Ignore
+            break;
+        default:
+            ALOGE("Unrecognized NAL unit type.");
+            return ERROR_MALFORMED;
+    }
+
+    if (err != OK) {
+        return err;
+    }
+
+    sp<ABuffer> buffer = ABuffer::CreateAsCopy(data, size);
+    buffer->setInt32Data(nalUnitType);
+    mNalUnits.push(buffer);
+    return OK;
+}
+
+template <typename T>
+static bool findParam(uint32_t key, T *param,
+        KeyedVector<uint32_t, uint64_t> &params) {
+    CHECK(param);
+    if (params.indexOfKey(key) < 0) {
+        return false;
+    }
+    *param = (T) params[key];
+    return true;
+}
+
+bool HevcParameterSets::findParam8(uint32_t key, uint8_t *param) {
+    return findParam(key, param, mParams);
+}
+
+bool HevcParameterSets::findParam16(uint32_t key, uint16_t *param) {
+    return findParam(key, param, mParams);
+}
+
+bool HevcParameterSets::findParam32(uint32_t key, uint32_t *param) {
+    return findParam(key, param, mParams);
+}
+
+bool HevcParameterSets::findParam64(uint32_t key, uint64_t *param) {
+    return findParam(key, param, mParams);
+}
+
+size_t HevcParameterSets::getNumNalUnitsOfType(uint8_t type) {
+    size_t num = 0;
+    for (size_t i = 0; i < mNalUnits.size(); ++i) {
+        if (getType(i) == type) {
+            ++num;
+        }
+    }
+    return num;
+}
+
+uint8_t HevcParameterSets::getType(size_t index) {
+    CHECK_LT(index, mNalUnits.size());
+    return mNalUnits[index]->int32Data();
+}
+
+size_t HevcParameterSets::getSize(size_t index) {
+    CHECK_LT(index, mNalUnits.size());
+    return mNalUnits[index]->size();
+}
+
+bool HevcParameterSets::write(size_t index, uint8_t* dest, size_t size) {
+    CHECK_LT(index, mNalUnits.size());
+    const sp<ABuffer>& nalUnit = mNalUnits[index];
+    if (size < nalUnit->size()) {
+        ALOGE("dest buffer size too small: %zu vs. %zu to be written",
+                size, nalUnit->size());
+        return false;
+    }
+    memcpy(dest, nalUnit->data(), nalUnit->size());
+    return true;
+}
+
+status_t HevcParameterSets::parseVps(const uint8_t* data, size_t size) {
+    // See Rec. ITU-T H.265 v3 (04/2015) Chapter 7.3.2.1 for reference
+    NALBitReader reader(data, size);
+    // Skip vps_video_parameter_set_id
+    reader.skipBits(4);
+    // Skip vps_base_layer_internal_flag
+    reader.skipBits(1);
+    // Skip vps_base_layer_available_flag
+    reader.skipBits(1);
+    // Skip vps_max_layers_minus_1
+    reader.skipBits(6);
+    // Skip vps_temporal_id_nesting_flags
+    reader.skipBits(1);
+    // Skip reserved
+    reader.skipBits(16);
+
+    mParams.add(kGeneralProfileSpace, reader.getBits(2));
+    mParams.add(kGeneralTierFlag, reader.getBits(1));
+    mParams.add(kGeneralProfileIdc, reader.getBits(5));
+    mParams.add(kGeneralProfileCompatibilityFlags, reader.getBits(32));
+    mParams.add(
+            kGeneralConstraintIndicatorFlags,
+            ((uint64_t)reader.getBits(16) << 32) | reader.getBits(32));
+    mParams.add(kGeneralLevelIdc, reader.getBits(8));
+    // 96 bits total for general profile.
+
+    return OK;
+}
+
+status_t HevcParameterSets::parseSps(const uint8_t* data, size_t size) {
+    // See Rec. ITU-T H.265 v3 (04/2015) Chapter 7.3.2.2 for reference
+    NALBitReader reader(data, size);
+    // Skip sps_video_parameter_set_id
+    reader.skipBits(4);
+    uint8_t maxSubLayersMinus1 = reader.getBits(3);
+    // Skip sps_temporal_id_nesting_flag;
+    reader.skipBits(1);
+    // Skip general profile
+    reader.skipBits(96);
+    if (maxSubLayersMinus1 > 0) {
+        bool subLayerProfilePresentFlag[8];
+        bool subLayerLevelPresentFlag[8];
+        for (int i = 0; i < maxSubLayersMinus1; ++i) {
+            subLayerProfilePresentFlag[i] = reader.getBits(1);
+            subLayerLevelPresentFlag[i] = reader.getBits(1);
+        }
+        // Skip reserved
+        reader.skipBits(2 * (8 - maxSubLayersMinus1));
+        for (int i = 0; i < maxSubLayersMinus1; ++i) {
+            if (subLayerProfilePresentFlag[i]) {
+                // Skip profile
+                reader.skipBits(88);
+            }
+            if (subLayerLevelPresentFlag[i]) {
+                // Skip sub_layer_level_idc[i]
+                reader.skipBits(8);
+            }
+        }
+    }
+    // Skip sps_seq_parameter_set_id
+    parseUE(&reader);
+    uint8_t chromaFormatIdc = parseUE(&reader);
+    mParams.add(kChromaFormatIdc, chromaFormatIdc);
+    if (chromaFormatIdc == 3) {
+        // Skip separate_colour_plane_flag
+        reader.skipBits(1);
+    }
+    // Skip pic_width_in_luma_samples
+    parseUE(&reader);
+    // Skip pic_height_in_luma_samples
+    parseUE(&reader);
+    if (reader.getBits(1) /* i.e. conformance_window_flag */) {
+        // Skip conf_win_left_offset
+        parseUE(&reader);
+        // Skip conf_win_right_offset
+        parseUE(&reader);
+        // Skip conf_win_top_offset
+        parseUE(&reader);
+        // Skip conf_win_bottom_offset
+        parseUE(&reader);
+    }
+    mParams.add(kBitDepthLumaMinus8, parseUE(&reader));
+    mParams.add(kBitDepthChromaMinus8, parseUE(&reader));
+
+    return OK;
+}
+
+status_t HevcParameterSets::parsePps(
+        const uint8_t* data __unused, size_t size __unused) {
+    return OK;
+}
+
+status_t HevcParameterSets::makeHvcc(uint8_t *hvcc, size_t *hvccSize,
+        size_t nalSizeLength) {
+    if (hvcc == NULL || hvccSize == NULL
+            || (nalSizeLength != 4 && nalSizeLength != 2)) {
+        return BAD_VALUE;
+    }
+    // ISO 14496-15: HEVC file format
+    size_t size = 23;  // 23 bytes in the header
+    size_t numOfArrays = 0;
+    const size_t numNalUnits = getNumNalUnits();
+    for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) {
+        uint8_t type = kHevcNalUnitTypes[i];
+        size_t numNalus = getNumNalUnitsOfType(type);
+        if (numNalus == 0) {
+            continue;
+        }
+        ++numOfArrays;
+        size += 3;
+        for (size_t j = 0; j < numNalUnits; ++j) {
+            if (getType(j) != type) {
+                continue;
+            }
+            size += 2 + getSize(j);
+        }
+    }
+    uint8_t generalProfileSpace, generalTierFlag, generalProfileIdc;
+    if (!findParam8(kGeneralProfileSpace, &generalProfileSpace)
+            || !findParam8(kGeneralTierFlag, &generalTierFlag)
+            || !findParam8(kGeneralProfileIdc, &generalProfileIdc)) {
+        return ERROR_MALFORMED;
+    }
+    uint32_t compatibilityFlags;
+    uint64_t constraintIdcFlags;
+    if (!findParam32(kGeneralProfileCompatibilityFlags, &compatibilityFlags)
+            || !findParam64(kGeneralConstraintIndicatorFlags, &constraintIdcFlags)) {
+        return ERROR_MALFORMED;
+    }
+    uint8_t generalLevelIdc;
+    if (!findParam8(kGeneralLevelIdc, &generalLevelIdc)) {
+        return ERROR_MALFORMED;
+    }
+    uint8_t chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8;
+    if (!findParam8(kChromaFormatIdc, &chromaFormatIdc)
+            || !findParam8(kBitDepthLumaMinus8, &bitDepthLumaMinus8)
+            || !findParam8(kBitDepthChromaMinus8, &bitDepthChromaMinus8)) {
+        return ERROR_MALFORMED;
+    }
+    if (size > *hvccSize) {
+        return NO_MEMORY;
+    }
+    *hvccSize = size;
+
+    uint8_t *header = hvcc;
+    header[0] = 1;
+    header[1] = (kGeneralProfileSpace << 6) | (kGeneralTierFlag << 5) | kGeneralProfileIdc;
+    header[2] = (compatibilityFlags >> 24) & 0xff;
+    header[3] = (compatibilityFlags >> 16) & 0xff;
+    header[4] = (compatibilityFlags >> 8) & 0xff;
+    header[5] = compatibilityFlags & 0xff;
+    header[6] = (constraintIdcFlags >> 40) & 0xff;
+    header[7] = (constraintIdcFlags >> 32) & 0xff;
+    header[8] = (constraintIdcFlags >> 24) & 0xff;
+    header[9] = (constraintIdcFlags >> 16) & 0xff;
+    header[10] = (constraintIdcFlags >> 8) & 0xff;
+    header[11] = constraintIdcFlags & 0xff;
+    header[12] = generalLevelIdc;
+    // FIXME: parse min_spatial_segmentation_idc.
+    header[13] = 0xf0;
+    header[14] = 0;
+    // FIXME: derive parallelismType properly.
+    header[15] = 0xfc;
+    header[16] = 0xfc | chromaFormatIdc;
+    header[17] = 0xf8 | bitDepthLumaMinus8;
+    header[18] = 0xf8 | bitDepthChromaMinus8;
+    // FIXME: derive avgFrameRate
+    header[19] = 0;
+    header[20] = 0;
+    // constantFrameRate, numTemporalLayers, temporalIdNested all set to 0.
+    header[21] = nalSizeLength - 1;
+    header[22] = numOfArrays;
+    header += 23;
+    for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) {
+        uint8_t type = kHevcNalUnitTypes[i];
+        size_t numNalus = getNumNalUnitsOfType(type);
+        if (numNalus == 0) {
+            continue;
+        }
+        // array_completeness set to 0.
+        header[0] = type;
+        header[1] = (numNalus >> 8) & 0xff;
+        header[2] = numNalus & 0xff;
+        header += 3;
+        for (size_t j = 0; j < numNalUnits; ++j) {
+            if (getType(j) != type) {
+                continue;
+            }
+            header[0] = (getSize(j) >> 8) & 0xff;
+            header[1] = getSize(j) & 0xff;
+            if (!write(j, header + 2, size - (header - (uint8_t *)hvcc))) {
+                return NO_MEMORY;
+            }
+            header += (2 + getSize(j));
+        }
+    }
+    CHECK_EQ(header - size, hvcc);
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index ea4a7ac..a5d9484 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,7 +41,7 @@
 #include <cutils/properties.h>
 
 #include "include/ESDS.h"
-
+#include "include/HevcUtils.h"
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -70,6 +70,18 @@
 #endif
 static const char kMetaKey_CaptureFps[] = "com.android.capture.fps";
 
+static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
+    kHevcNalUnitTypeVps,
+    kHevcNalUnitTypeSps,
+    kHevcNalUnitTypePps,
+};
+static const uint8_t kHevcNalUnitTypes[5] = {
+    kHevcNalUnitTypeVps,
+    kHevcNalUnitTypeSps,
+    kHevcNalUnitTypePps,
+    kHevcNalUnitTypePrefixSei,
+    kHevcNalUnitTypeSuffixSei,
+};
 /* uncomment to include model and build in meta */
 //#define SHOW_MODEL_BUILD 1
 
@@ -89,6 +101,7 @@
     void writeTrackHeader(bool use32BitOffset = true);
     void bufferChunk(int64_t timestampUs);
     bool isAvc() const { return mIsAvc; }
+    bool isHevc() const { return mIsHevc; }
     bool isAudio() const { return mIsAudio; }
     bool isMPEG4() const { return mIsMPEG4; }
     void addChunkOffset(off64_t offset);
@@ -234,6 +247,7 @@
     volatile bool mResumed;
     volatile bool mStarted;
     bool mIsAvc;
+    bool mIsHevc;
     bool mIsAudio;
     bool mIsMPEG4;
     int32_t mTrackId;
@@ -299,10 +313,17 @@
     const uint8_t *parseParamSet(
         const uint8_t *data, size_t length, int type, size_t *paramSetLen);
 
+    status_t copyCodecSpecificData(const uint8_t *data, size_t size, size_t minLength = 0);
+
     status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
     status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
     status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
 
+    status_t makeHEVCCodecSpecificData(const uint8_t *data, size_t size);
+    status_t copyHEVCCodecSpecificData(const uint8_t *data, size_t size);
+    status_t parseHEVCCodecSpecificData(
+            const uint8_t *data, size_t size, HevcParameterSets &paramSets);
+
     // Track authoring progress status
     void trackProgressStatus(int64_t timeUs, status_t err = OK);
     void initTrackingProgressStatus(MetaData *params);
@@ -340,6 +361,7 @@
     void writeD263Box();
     void writePaspBox();
     void writeAvccBox();
+    void writeHvccBox();
     void writeUrlBox();
     void writeDrefBox();
     void writeDinfBox();
@@ -463,6 +485,8 @@
             return "s263";
         } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
             return "avc1";
+        } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
+            return "hvc1";
         }
     } else {
         ALOGE("Track (%s) other than video or audio is not supported", mime);
@@ -1465,6 +1489,7 @@
     const char *mime;
     mMeta->findCString(kKeyMIMEType, &mime);
     mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+    mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
     mIsAudio = !strncasecmp(mime, "audio/", 6);
     mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
                !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
@@ -1560,31 +1585,26 @@
     const char *mime;
     CHECK(mMeta->findCString(kKeyMIMEType, &mime));
 
+    uint32_t type;
+    const void *data = NULL;
+    size_t size = 0;
     if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-        uint32_t type;
-        const void *data;
-        size_t size;
-        if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
-            mCodecSpecificData = malloc(size);
-            mCodecSpecificDataSize = size;
-            memcpy(mCodecSpecificData, data, size);
-            mGotAllCodecSpecificData = true;
-        }
+        mMeta->findData(kKeyAVCC, &type, &data, &size);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+        mMeta->findData(kKeyHVCC, &type, &data, &size);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
-        uint32_t type;
-        const void *data;
-        size_t size;
         if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
             ESDS esds(data, size);
-            if (esds.getCodecSpecificInfo(&data, &size) == OK) {
-                mCodecSpecificData = malloc(size);
-                mCodecSpecificDataSize = size;
-                memcpy(mCodecSpecificData, data, size);
-                mGotAllCodecSpecificData = true;
+            if (esds.getCodecSpecificInfo(&data, &size) != OK) {
+                data = NULL;
+                size = 0;
             }
         }
     }
+    if (data != NULL && copyCodecSpecificData((uint8_t *)data, size) == OK) {
+        mGotAllCodecSpecificData = true;
+    }
 }
 
 MPEG4Writer::Track::~Track() {
@@ -1661,7 +1681,7 @@
     while (!chunk->mSamples.empty()) {
         List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
 
-        off64_t offset = chunk->mTrack->isAvc()
+        off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHevc())
                                 ? addLengthPrefixedSample_l(*it)
                                 : addSample_l(*it);
 
@@ -1968,13 +1988,30 @@
 
     // 2 bytes for each of the parameter set length field
     // plus the 7 bytes for the header
-    if (size < 4 + 7) {
+    return copyCodecSpecificData(data, size, 4 + 7);
+}
+
+status_t MPEG4Writer::Track::copyHEVCCodecSpecificData(
+        const uint8_t *data, size_t size) {
+    ALOGV("copyHEVCCodecSpecificData");
+
+    // Min length of HEVC CSD is 23. (ISO/IEC 14496-15:2014 Chapter 8.3.3.1.2)
+    return copyCodecSpecificData(data, size, 23);
+}
+
+status_t MPEG4Writer::Track::copyCodecSpecificData(
+        const uint8_t *data, size_t size, size_t minLength) {
+    if (size < minLength) {
         ALOGE("Codec specific data length too short: %zu", size);
         return ERROR_MALFORMED;
     }
 
-    mCodecSpecificDataSize = size;
     mCodecSpecificData = malloc(size);
+    if (mCodecSpecificData == NULL) {
+        ALOGE("Failed allocating codec specific data");
+        return NO_MEMORY;
+    }
+    mCodecSpecificDataSize = size;
     memcpy(mCodecSpecificData, data, size);
     return OK;
 }
@@ -2097,6 +2134,11 @@
     // ISO 14496-15: AVC file format
     mCodecSpecificDataSize += 7;  // 7 more bytes in the header
     mCodecSpecificData = malloc(mCodecSpecificDataSize);
+    if (mCodecSpecificData == NULL) {
+        mCodecSpecificDataSize = 0;
+        ALOGE("Failed allocating codec specific data");
+        return NO_MEMORY;
+    }
     uint8_t *header = (uint8_t *)mCodecSpecificData;
     header[0] = 1;                     // version
     header[1] = mProfileIdc;           // profile indication
@@ -2145,6 +2187,96 @@
     return OK;
 }
 
+
+status_t MPEG4Writer::Track::parseHEVCCodecSpecificData(
+        const uint8_t *data, size_t size, HevcParameterSets &paramSets) {
+
+    ALOGV("parseHEVCCodecSpecificData");
+    const uint8_t *tmp = data;
+    const uint8_t *nextStartCode = data;
+    size_t bytesLeft = size;
+    while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
+        nextStartCode = findNextStartCode(tmp + 4, bytesLeft - 4);
+        if (nextStartCode == NULL) {
+            return ERROR_MALFORMED;
+        }
+        status_t err = paramSets.addNalUnit(tmp + 4, (nextStartCode - tmp) - 4);
+        if (err != OK) {
+            return ERROR_MALFORMED;
+        }
+
+        // Move on to find the next parameter set
+        bytesLeft -= nextStartCode - tmp;
+        tmp = nextStartCode;
+    }
+
+    size_t csdSize = 23;
+    const size_t numNalUnits = paramSets.getNumNalUnits();
+    for (size_t i = 0; i < ARRAY_SIZE(kMandatoryHevcNalUnitTypes); ++i) {
+        int type = kMandatoryHevcNalUnitTypes[i];
+        size_t numParamSets = paramSets.getNumNalUnitsOfType(type);
+        if (numParamSets == 0) {
+            ALOGE("Cound not find NAL unit of type %d", type);
+            return ERROR_MALFORMED;
+        }
+    }
+    for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) {
+        int type = kHevcNalUnitTypes[i];
+        size_t numParamSets = paramSets.getNumNalUnitsOfType(type);
+        if (numParamSets > 0xffff) {
+            ALOGE("Too many seq parameter sets (%zu) found", numParamSets);
+            return ERROR_MALFORMED;
+        }
+        csdSize += 3;
+        for (size_t j = 0; j < numNalUnits; ++j) {
+            if (paramSets.getType(j) != type) {
+                continue;
+            }
+            csdSize += 2 + paramSets.getSize(j);
+        }
+    }
+    mCodecSpecificDataSize = csdSize;
+    return OK;
+}
+
+status_t MPEG4Writer::Track::makeHEVCCodecSpecificData(
+        const uint8_t *data, size_t size) {
+
+    if (mCodecSpecificData != NULL) {
+        ALOGE("Already have codec specific data");
+        return ERROR_MALFORMED;
+    }
+
+    if (size < 4) {
+        ALOGE("Codec specific data length too short: %zu", size);
+        return ERROR_MALFORMED;
+    }
+
+    // Data is in the form of HEVCCodecSpecificData
+    if (memcmp("\x00\x00\x00\x01", data, 4)) {
+        return copyHEVCCodecSpecificData(data, size);
+    }
+
+    HevcParameterSets paramSets;
+    if (parseHEVCCodecSpecificData(data, size, paramSets) != OK) {
+        return ERROR_MALFORMED;
+    }
+
+    mCodecSpecificData = malloc(mCodecSpecificDataSize);
+    if (mCodecSpecificData == NULL) {
+        mCodecSpecificDataSize = 0;
+        ALOGE("Failed allocating codec specific data");
+        return NO_MEMORY;
+    }
+    status_t err = paramSets.makeHvcc((uint8_t *)mCodecSpecificData,
+            &mCodecSpecificDataSize, mOwner->useNalLengthFour() ? 5 : 2);
+    if (err != OK) {
+        return err;
+    }
+
+    return OK;
+}
+
 /*
  * Updates the drift time from the audio track so that
  * the video track can get the updated drift time information
@@ -2228,13 +2360,15 @@
                             + buffer->range_offset(),
                         buffer->range_length());
                 CHECK_EQ((status_t)OK, err);
-            } else if (mIsMPEG4) {
-                mCodecSpecificDataSize = buffer->range_length();
-                mCodecSpecificData = malloc(mCodecSpecificDataSize);
-                memcpy(mCodecSpecificData,
+            } else if (mIsHevc) {
+                status_t err = makeHEVCCodecSpecificData(
                         (const uint8_t *)buffer->data()
                             + buffer->range_offset(),
-                       buffer->range_length());
+                        buffer->range_length());
+                CHECK_EQ((status_t)OK, err);
+            } else if (mIsMPEG4) {
+                copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
+                        buffer->range_length());
             }
 
             buffer->release();
@@ -2254,10 +2388,10 @@
         buffer->release();
         buffer = NULL;
 
-        if (mIsAvc) StripStartcode(copy);
+        if (mIsAvc || mIsHevc) StripStartcode(copy);
 
         size_t sampleSize = copy->range_length();
-        if (mIsAvc) {
+        if (mIsAvc || mIsHevc) {
             if (mOwner->useNalLengthFour()) {
                 sampleSize += 4;
             } else {
@@ -2457,7 +2591,7 @@
             trackProgressStatus(timestampUs);
         }
         if (!hasMultipleTracks) {
-            off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
+            off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addLengthPrefixedSample_l(copy)
                                  : mOwner->addSample_l(copy);
 
             uint32_t count = (mOwner->use32BitFileOffset()
@@ -2709,7 +2843,8 @@
     CHECK(mMeta->findCString(kKeyMIMEType, &mime));
     if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) ||
         !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
-        !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
+        !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
         if (!mCodecSpecificData ||
             mCodecSpecificDataSize <= 0) {
             ALOGE("Missing codec specific data");
@@ -2815,6 +2950,8 @@
         writeD263Box();
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
         writeAvccBox();
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
+        writeHvccBox();
     }
 
     writePaspBox();
@@ -3070,6 +3207,20 @@
     mOwner->endBox();  // avcC
 }
 
+
+void MPEG4Writer::Track::writeHvccBox() {
+    CHECK(mCodecSpecificData);
+    CHECK_GE(mCodecSpecificDataSize, 5);
+
+    // Patch avcc's lengthSize field to match the number
+    // of bytes we use to indicate the size of a nal unit.
+    uint8_t *ptr = (uint8_t *)mCodecSpecificData;
+    ptr[21] = (ptr[21] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
+    mOwner->beginBox("hvcC");
+    mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+    mOwner->endBox();  // hvcC
+}
+
 void MPEG4Writer::Track::writeD263Box() {
     mOwner->beginBox("d263");
     mOwner->writeInt32(0);  // vendor
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2a8b635..13ad03a 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -22,6 +22,7 @@
 #include <sys/stat.h>
 
 #include "include/ESDS.h"
+#include "include/HevcUtils.h"
 
 #include <arpa/inet.h>
 #include <cutils/properties.h>
@@ -575,6 +576,41 @@
 
 }
 
+static size_t reassembleHVCC(const sp<ABuffer> &csd0, uint8_t *hvcc, size_t hvccSize, size_t nalSizeLength) {
+    HevcParameterSets paramSets;
+    uint8_t* data = csd0->data();
+    if (csd0->size() < 4) {
+        ALOGE("csd0 too small");
+        return 0;
+    }
+    if (memcmp(data, "\x00\x00\x00\x01", 4) != 0) {
+        ALOGE("csd0 doesn't start with a start code");
+        return 0;
+    }
+    size_t prevNalOffset = 4;
+    status_t err = OK;
+    for (size_t i = 1; i < csd0->size() - 4; ++i) {
+        if (memcmp(&data[i], "\x00\x00\x00\x01", 4) != 0) {
+            continue;
+        }
+        err = paramSets.addNalUnit(&data[prevNalOffset], i - prevNalOffset);
+        if (err != OK) {
+            return 0;
+        }
+        prevNalOffset = i + 4;
+    }
+    err = paramSets.addNalUnit(&data[prevNalOffset], csd0->size() - prevNalOffset);
+    if (err != OK) {
+        return 0;
+    }
+    size_t size = hvccSize;
+    err = paramSets.makeHvcc(hvcc, &size, nalSizeLength);
+    if (err != OK) {
+        return 0;
+    }
+    return size;
+}
+
 void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
     AString mime;
     if (msg->findString("mime", &mime)) {
@@ -693,6 +729,10 @@
             // for transporting the CSD to muxers.
             reassembleESDS(csd0, esds);
             meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
+        } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) {
+            uint8_t hvcc[1024]; // that oughta be enough, right?
+            size_t outsize = reassembleHVCC(csd0, hvcc, 1024, 4);
+            meta->setData(kKeyHVCC, kKeyHVCC, hvcc, outsize);
         }
     }
 
diff --git a/media/libstagefright/include/HevcUtils.h b/media/libstagefright/include/HevcUtils.h
new file mode 100644
index 0000000..0d7bb2f
--- /dev/null
+++ b/media/libstagefright/include/HevcUtils.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef HEVC_UTILS_H_
+
+#define HEVC_UTILS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+enum {
+    kHevcNalUnitTypeVps = 32,
+    kHevcNalUnitTypeSps = 33,
+    kHevcNalUnitTypePps = 34,
+    kHevcNalUnitTypePrefixSei = 39,
+    kHevcNalUnitTypeSuffixSei = 40,
+};
+
+enum {
+    // uint8_t
+    kGeneralProfileSpace,
+    // uint8_t
+    kGeneralTierFlag,
+    // uint8_t
+    kGeneralProfileIdc,
+    // uint32_t
+    kGeneralProfileCompatibilityFlags,
+    // uint64_t
+    kGeneralConstraintIndicatorFlags,
+    // uint8_t
+    kGeneralLevelIdc,
+    // uint8_t
+    kChromaFormatIdc,
+    // uint8_t
+    kBitDepthLumaMinus8,
+    // uint8_t
+    kBitDepthChromaMinus8,
+};
+
+class HevcParameterSets {
+public:
+    HevcParameterSets();
+
+    status_t addNalUnit(const uint8_t* data, size_t size);
+
+    bool findParam8(uint32_t key, uint8_t *param);
+    bool findParam16(uint32_t key, uint16_t *param);
+    bool findParam32(uint32_t key, uint32_t *param);
+    bool findParam64(uint32_t key, uint64_t *param);
+
+    inline size_t getNumNalUnits() { return mNalUnits.size(); }
+    size_t getNumNalUnitsOfType(uint8_t type);
+    uint8_t getType(size_t index);
+    size_t getSize(size_t index);
+    // Note that this method does not write the start code.
+    bool write(size_t index, uint8_t* dest, size_t size);
+    status_t makeHvcc(uint8_t *hvcc, size_t *hvccSize, size_t nalSizeLength);
+
+private:
+    status_t parseVps(const uint8_t* data, size_t size);
+    status_t parseSps(const uint8_t* data, size_t size);
+    status_t parsePps(const uint8_t* data, size_t size);
+
+    KeyedVector<uint32_t, uint64_t> mParams;
+    Vector<sp<ABuffer>> mNalUnits;
+
+    DISALLOW_EVIL_CONSTRUCTORS(HevcParameterSets);
+};
+
+}  // namespace android
+
+#endif  // HEVC_UTILS_H_