https://zh.osdn.net/projects/android-x86/scm/git/frameworks-av/commits/9aa87d4ef502c9700a31fe46dc6e1d6f99cf4e5e
frameworks-av: Commit
frameworks/av
Commit MetaInfo
| 修订版 | 9aa87d4ef502c9700a31fe46dc6e1d6f99cf4e5e (tree) |
|---|---|
| 时间 | 2016-01-22 05:47:05 |
| 作者 | Wonsik Kim <wonsik@goog...> |
| Commiter | Wonsik Kim |
Log Message
stagefright: add HEVC support to MediaRecorder
Bug: 22879917
Change-Id: I6c97b051467de44c506a8ff021321e5953a63fc3
更改概述
- modified:
include/media/mediarecorder.h (diff) - modified:
media/libmedia/MediaProfiles.cpp (diff) - modified:
media/libmediaplayerservice/StagefrightRecorder.cpp (diff) - modified:
media/libstagefright/ACodec.cpp (diff) - modified:
media/libstagefright/Android.mk (diff) - add:
media/libstagefright/HevcUtils.cpp (diff) - modified:
media/libstagefright/MPEG4Writer.cpp (diff) - modified:
media/libstagefright/Utils.cpp (diff) - add:
media/libstagefright/include/HevcUtils.h (diff)
差异
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
| @@ -95,6 +95,7 @@ enum video_encoder { | ||
| 95 | 95 | VIDEO_ENCODER_H264 = 2, |
| 96 | 96 | VIDEO_ENCODER_MPEG_4_SP = 3, |
| 97 | 97 | VIDEO_ENCODER_VP8 = 4, |
| 98 | + VIDEO_ENCODER_HEVC = 5, | |
| 98 | 99 | |
| 99 | 100 | VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type |
| 100 | 101 | }; |
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
| @@ -37,7 +37,8 @@ MediaProfiles *MediaProfiles::sInstance = NULL; | ||
| 37 | 37 | const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = { |
| 38 | 38 | {"h263", VIDEO_ENCODER_H263}, |
| 39 | 39 | {"h264", VIDEO_ENCODER_H264}, |
| 40 | - {"m4v", VIDEO_ENCODER_MPEG_4_SP} | |
| 40 | + {"m4v", VIDEO_ENCODER_MPEG_4_SP}, | |
| 41 | + {"hevc", VIDEO_ENCODER_HEVC} | |
| 41 | 42 | }; |
| 42 | 43 | |
| 43 | 44 | const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { |
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
| @@ -1226,6 +1226,7 @@ status_t StagefrightRecorder::checkVideoEncoderCapabilities() { | ||
| 1226 | 1226 | (mVideoEncoder == VIDEO_ENCODER_H263 ? MEDIA_MIMETYPE_VIDEO_H263 : |
| 1227 | 1227 | mVideoEncoder == VIDEO_ENCODER_MPEG_4_SP ? MEDIA_MIMETYPE_VIDEO_MPEG4 : |
| 1228 | 1228 | mVideoEncoder == VIDEO_ENCODER_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : |
| 1229 | + mVideoEncoder == VIDEO_ENCODER_HEVC ? MEDIA_MIMETYPE_VIDEO_HEVC : | |
| 1229 | 1230 | mVideoEncoder == VIDEO_ENCODER_H264 ? MEDIA_MIMETYPE_VIDEO_AVC : ""), |
| 1230 | 1231 | false /* decoder */, true /* hwCodec */, &codecs); |
| 1231 | 1232 |
| @@ -1515,6 +1516,10 @@ status_t StagefrightRecorder::setupVideoEncoder( | ||
| 1515 | 1516 | format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8); |
| 1516 | 1517 | break; |
| 1517 | 1518 | |
| 1519 | + case VIDEO_ENCODER_HEVC: | |
| 1520 | + format->setString("mime", MEDIA_MIMETYPE_VIDEO_HEVC); | |
| 1521 | + break; | |
| 1522 | + | |
| 1518 | 1523 | default: |
| 1519 | 1524 | CHECK(!"Should not be here, unsupported video encoding."); |
| 1520 | 1525 | break; |
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
| @@ -3530,8 +3530,8 @@ status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) { | ||
| 3530 | 3530 | hevcType.eProfile = static_cast<OMX_VIDEO_HEVCPROFILETYPE>(profile); |
| 3531 | 3531 | hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level); |
| 3532 | 3532 | } |
| 3533 | - | |
| 3534 | - // TODO: Need OMX structure definition for setting iFrameInterval | |
| 3533 | + // TODO: finer control? | |
| 3534 | + hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate); | |
| 3535 | 3535 | |
| 3536 | 3536 | err = mOMX->setParameter( |
| 3537 | 3537 | mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType)); |
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
| @@ -24,6 +24,7 @@ LOCAL_SRC_FILES:= \ | ||
| 24 | 24 | FLACExtractor.cpp \ |
| 25 | 25 | FrameRenderTracker.cpp \ |
| 26 | 26 | HTTPBase.cpp \ |
| 27 | + HevcUtils.cpp \ | |
| 27 | 28 | JPEGSource.cpp \ |
| 28 | 29 | MP3Extractor.cpp \ |
| 29 | 30 | MPEG2TSWriter.cpp \ |
--- /dev/null
+++ b/media/libstagefright/HevcUtils.cpp
| @@ -0,0 +1,337 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2015 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 "HevcUtils" | |
| 19 | + | |
| 20 | +#include <cstring> | |
| 21 | + | |
| 22 | +#include "include/HevcUtils.h" | |
| 23 | +#include "include/avc_utils.h" | |
| 24 | + | |
| 25 | +#include <media/stagefright/foundation/ABitReader.h> | |
| 26 | +#include <media/stagefright/foundation/ABuffer.h> | |
| 27 | +#include <media/stagefright/foundation/ADebug.h> | |
| 28 | +#include <media/stagefright/foundation/AMessage.h> | |
| 29 | +#include <media/stagefright/MediaErrors.h> | |
| 30 | +#include <media/stagefright/Utils.h> | |
| 31 | + | |
| 32 | +namespace android { | |
| 33 | + | |
| 34 | +static const uint8_t kHevcNalUnitTypes[5] = { | |
| 35 | + kHevcNalUnitTypeVps, | |
| 36 | + kHevcNalUnitTypeSps, | |
| 37 | + kHevcNalUnitTypePps, | |
| 38 | + kHevcNalUnitTypePrefixSei, | |
| 39 | + kHevcNalUnitTypeSuffixSei, | |
| 40 | +}; | |
| 41 | + | |
| 42 | +HevcParameterSets::HevcParameterSets() { | |
| 43 | +} | |
| 44 | + | |
| 45 | +status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) { | |
| 46 | + uint8_t nalUnitType = (data[0] >> 1) & 0x3f; | |
| 47 | + status_t err = OK; | |
| 48 | + switch (nalUnitType) { | |
| 49 | + case 32: // VPS | |
| 50 | + err = parseVps(data + 2, size - 2); | |
| 51 | + break; | |
| 52 | + case 33: // SPS | |
| 53 | + err = parseSps(data + 2, size - 2); | |
| 54 | + break; | |
| 55 | + case 34: // PPS | |
| 56 | + err = parsePps(data + 2, size - 2); | |
| 57 | + break; | |
| 58 | + case 39: // Prefix SEI | |
| 59 | + case 40: // Suffix SEI | |
| 60 | + // Ignore | |
| 61 | + break; | |
| 62 | + default: | |
| 63 | + ALOGE("Unrecognized NAL unit type."); | |
| 64 | + return ERROR_MALFORMED; | |
| 65 | + } | |
| 66 | + | |
| 67 | + if (err != OK) { | |
| 68 | + return err; | |
| 69 | + } | |
| 70 | + | |
| 71 | + sp<ABuffer> buffer = ABuffer::CreateAsCopy(data, size); | |
| 72 | + buffer->setInt32Data(nalUnitType); | |
| 73 | + mNalUnits.push(buffer); | |
| 74 | + return OK; | |
| 75 | +} | |
| 76 | + | |
| 77 | +template <typename T> | |
| 78 | +static bool findParam(uint32_t key, T *param, | |
| 79 | + KeyedVector<uint32_t, uint64_t> ¶ms) { | |
| 80 | + CHECK(param); | |
| 81 | + if (params.indexOfKey(key) < 0) { | |
| 82 | + return false; | |
| 83 | + } | |
| 84 | + *param = (T) params[key]; | |
| 85 | + return true; | |
| 86 | +} | |
| 87 | + | |
| 88 | +bool HevcParameterSets::findParam8(uint32_t key, uint8_t *param) { | |
| 89 | + return findParam(key, param, mParams); | |
| 90 | +} | |
| 91 | + | |
| 92 | +bool HevcParameterSets::findParam16(uint32_t key, uint16_t *param) { | |
| 93 | + return findParam(key, param, mParams); | |
| 94 | +} | |
| 95 | + | |
| 96 | +bool HevcParameterSets::findParam32(uint32_t key, uint32_t *param) { | |
| 97 | + return findParam(key, param, mParams); | |
| 98 | +} | |
| 99 | + | |
| 100 | +bool HevcParameterSets::findParam64(uint32_t key, uint64_t *param) { | |
| 101 | + return findParam(key, param, mParams); | |
| 102 | +} | |
| 103 | + | |
| 104 | +size_t HevcParameterSets::getNumNalUnitsOfType(uint8_t type) { | |
| 105 | + size_t num = 0; | |
| 106 | + for (size_t i = 0; i < mNalUnits.size(); ++i) { | |
| 107 | + if (getType(i) == type) { | |
| 108 | + ++num; | |
| 109 | + } | |
| 110 | + } | |
| 111 | + return num; | |
| 112 | +} | |
| 113 | + | |
| 114 | +uint8_t HevcParameterSets::getType(size_t index) { | |
| 115 | + CHECK_LT(index, mNalUnits.size()); | |
| 116 | + return mNalUnits[index]->int32Data(); | |
| 117 | +} | |
| 118 | + | |
| 119 | +size_t HevcParameterSets::getSize(size_t index) { | |
| 120 | + CHECK_LT(index, mNalUnits.size()); | |
| 121 | + return mNalUnits[index]->size(); | |
| 122 | +} | |
| 123 | + | |
| 124 | +bool HevcParameterSets::write(size_t index, uint8_t* dest, size_t size) { | |
| 125 | + CHECK_LT(index, mNalUnits.size()); | |
| 126 | + const sp<ABuffer>& nalUnit = mNalUnits[index]; | |
| 127 | + if (size < nalUnit->size()) { | |
| 128 | + ALOGE("dest buffer size too small: %zu vs. %zu to be written", | |
| 129 | + size, nalUnit->size()); | |
| 130 | + return false; | |
| 131 | + } | |
| 132 | + memcpy(dest, nalUnit->data(), nalUnit->size()); | |
| 133 | + return true; | |
| 134 | +} | |
| 135 | + | |
| 136 | +status_t HevcParameterSets::parseVps(const uint8_t* data, size_t size) { | |
| 137 | + // See Rec. ITU-T H.265 v3 (04/2015) Chapter 7.3.2.1 for reference | |
| 138 | + NALBitReader reader(data, size); | |
| 139 | + // Skip vps_video_parameter_set_id | |
| 140 | + reader.skipBits(4); | |
| 141 | + // Skip vps_base_layer_internal_flag | |
| 142 | + reader.skipBits(1); | |
| 143 | + // Skip vps_base_layer_available_flag | |
| 144 | + reader.skipBits(1); | |
| 145 | + // Skip vps_max_layers_minus_1 | |
| 146 | + reader.skipBits(6); | |
| 147 | + // Skip vps_temporal_id_nesting_flags | |
| 148 | + reader.skipBits(1); | |
| 149 | + // Skip reserved | |
| 150 | + reader.skipBits(16); | |
| 151 | + | |
| 152 | + mParams.add(kGeneralProfileSpace, reader.getBits(2)); | |
| 153 | + mParams.add(kGeneralTierFlag, reader.getBits(1)); | |
| 154 | + mParams.add(kGeneralProfileIdc, reader.getBits(5)); | |
| 155 | + mParams.add(kGeneralProfileCompatibilityFlags, reader.getBits(32)); | |
| 156 | + mParams.add( | |
| 157 | + kGeneralConstraintIndicatorFlags, | |
| 158 | + ((uint64_t)reader.getBits(16) << 32) | reader.getBits(32)); | |
| 159 | + mParams.add(kGeneralLevelIdc, reader.getBits(8)); | |
| 160 | + // 96 bits total for general profile. | |
| 161 | + | |
| 162 | + return OK; | |
| 163 | +} | |
| 164 | + | |
| 165 | +status_t HevcParameterSets::parseSps(const uint8_t* data, size_t size) { | |
| 166 | + // See Rec. ITU-T H.265 v3 (04/2015) Chapter 7.3.2.2 for reference | |
| 167 | + NALBitReader reader(data, size); | |
| 168 | + // Skip sps_video_parameter_set_id | |
| 169 | + reader.skipBits(4); | |
| 170 | + uint8_t maxSubLayersMinus1 = reader.getBits(3); | |
| 171 | + // Skip sps_temporal_id_nesting_flag; | |
| 172 | + reader.skipBits(1); | |
| 173 | + // Skip general profile | |
| 174 | + reader.skipBits(96); | |
| 175 | + if (maxSubLayersMinus1 > 0) { | |
| 176 | + bool subLayerProfilePresentFlag[8]; | |
| 177 | + bool subLayerLevelPresentFlag[8]; | |
| 178 | + for (int i = 0; i < maxSubLayersMinus1; ++i) { | |
| 179 | + subLayerProfilePresentFlag[i] = reader.getBits(1); | |
| 180 | + subLayerLevelPresentFlag[i] = reader.getBits(1); | |
| 181 | + } | |
| 182 | + // Skip reserved | |
| 183 | + reader.skipBits(2 * (8 - maxSubLayersMinus1)); | |
| 184 | + for (int i = 0; i < maxSubLayersMinus1; ++i) { | |
| 185 | + if (subLayerProfilePresentFlag[i]) { | |
| 186 | + // Skip profile | |
| 187 | + reader.skipBits(88); | |
| 188 | + } | |
| 189 | + if (subLayerLevelPresentFlag[i]) { | |
| 190 | + // Skip sub_layer_level_idc[i] | |
| 191 | + reader.skipBits(8); | |
| 192 | + } | |
| 193 | + } | |
| 194 | + } | |
| 195 | + // Skip sps_seq_parameter_set_id | |
| 196 | + parseUE(&reader); | |
| 197 | + uint8_t chromaFormatIdc = parseUE(&reader); | |
| 198 | + mParams.add(kChromaFormatIdc, chromaFormatIdc); | |
| 199 | + if (chromaFormatIdc == 3) { | |
| 200 | + // Skip separate_colour_plane_flag | |
| 201 | + reader.skipBits(1); | |
| 202 | + } | |
| 203 | + // Skip pic_width_in_luma_samples | |
| 204 | + parseUE(&reader); | |
| 205 | + // Skip pic_height_in_luma_samples | |
| 206 | + parseUE(&reader); | |
| 207 | + if (reader.getBits(1) /* i.e. conformance_window_flag */) { | |
| 208 | + // Skip conf_win_left_offset | |
| 209 | + parseUE(&reader); | |
| 210 | + // Skip conf_win_right_offset | |
| 211 | + parseUE(&reader); | |
| 212 | + // Skip conf_win_top_offset | |
| 213 | + parseUE(&reader); | |
| 214 | + // Skip conf_win_bottom_offset | |
| 215 | + parseUE(&reader); | |
| 216 | + } | |
| 217 | + mParams.add(kBitDepthLumaMinus8, parseUE(&reader)); | |
| 218 | + mParams.add(kBitDepthChromaMinus8, parseUE(&reader)); | |
| 219 | + | |
| 220 | + return OK; | |
| 221 | +} | |
| 222 | + | |
| 223 | +status_t HevcParameterSets::parsePps( | |
| 224 | + const uint8_t* data __unused, size_t size __unused) { | |
| 225 | + return OK; | |
| 226 | +} | |
| 227 | + | |
| 228 | +status_t HevcParameterSets::makeHvcc(uint8_t *hvcc, size_t *hvccSize, | |
| 229 | + size_t nalSizeLength) { | |
| 230 | + if (hvcc == NULL || hvccSize == NULL | |
| 231 | + || (nalSizeLength != 4 && nalSizeLength != 2)) { | |
| 232 | + return BAD_VALUE; | |
| 233 | + } | |
| 234 | + // ISO 14496-15: HEVC file format | |
| 235 | + size_t size = 23; // 23 bytes in the header | |
| 236 | + size_t numOfArrays = 0; | |
| 237 | + const size_t numNalUnits = getNumNalUnits(); | |
| 238 | + for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) { | |
| 239 | + uint8_t type = kHevcNalUnitTypes[i]; | |
| 240 | + size_t numNalus = getNumNalUnitsOfType(type); | |
| 241 | + if (numNalus == 0) { | |
| 242 | + continue; | |
| 243 | + } | |
| 244 | + ++numOfArrays; | |
| 245 | + size += 3; | |
| 246 | + for (size_t j = 0; j < numNalUnits; ++j) { | |
| 247 | + if (getType(j) != type) { | |
| 248 | + continue; | |
| 249 | + } | |
| 250 | + size += 2 + getSize(j); | |
| 251 | + } | |
| 252 | + } | |
| 253 | + uint8_t generalProfileSpace, generalTierFlag, generalProfileIdc; | |
| 254 | + if (!findParam8(kGeneralProfileSpace, &generalProfileSpace) | |
| 255 | + || !findParam8(kGeneralTierFlag, &generalTierFlag) | |
| 256 | + || !findParam8(kGeneralProfileIdc, &generalProfileIdc)) { | |
| 257 | + return ERROR_MALFORMED; | |
| 258 | + } | |
| 259 | + uint32_t compatibilityFlags; | |
| 260 | + uint64_t constraintIdcFlags; | |
| 261 | + if (!findParam32(kGeneralProfileCompatibilityFlags, &compatibilityFlags) | |
| 262 | + || !findParam64(kGeneralConstraintIndicatorFlags, &constraintIdcFlags)) { | |
| 263 | + return ERROR_MALFORMED; | |
| 264 | + } | |
| 265 | + uint8_t generalLevelIdc; | |
| 266 | + if (!findParam8(kGeneralLevelIdc, &generalLevelIdc)) { | |
| 267 | + return ERROR_MALFORMED; | |
| 268 | + } | |
| 269 | + uint8_t chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8; | |
| 270 | + if (!findParam8(kChromaFormatIdc, &chromaFormatIdc) | |
| 271 | + || !findParam8(kBitDepthLumaMinus8, &bitDepthLumaMinus8) | |
| 272 | + || !findParam8(kBitDepthChromaMinus8, &bitDepthChromaMinus8)) { | |
| 273 | + return ERROR_MALFORMED; | |
| 274 | + } | |
| 275 | + if (size > *hvccSize) { | |
| 276 | + return NO_MEMORY; | |
| 277 | + } | |
| 278 | + *hvccSize = size; | |
| 279 | + | |
| 280 | + uint8_t *header = hvcc; | |
| 281 | + header[0] = 1; | |
| 282 | + header[1] = (kGeneralProfileSpace << 6) | (kGeneralTierFlag << 5) | kGeneralProfileIdc; | |
| 283 | + header[2] = (compatibilityFlags >> 24) & 0xff; | |
| 284 | + header[3] = (compatibilityFlags >> 16) & 0xff; | |
| 285 | + header[4] = (compatibilityFlags >> 8) & 0xff; | |
| 286 | + header[5] = compatibilityFlags & 0xff; | |
| 287 | + header[6] = (constraintIdcFlags >> 40) & 0xff; | |
| 288 | + header[7] = (constraintIdcFlags >> 32) & 0xff; | |
| 289 | + header[8] = (constraintIdcFlags >> 24) & 0xff; | |
| 290 | + header[9] = (constraintIdcFlags >> 16) & 0xff; | |
| 291 | + header[10] = (constraintIdcFlags >> 8) & 0xff; | |
| 292 | + header[11] = constraintIdcFlags & 0xff; | |
| 293 | + header[12] = generalLevelIdc; | |
| 294 | + // FIXME: parse min_spatial_segmentation_idc. | |
| 295 | + header[13] = 0xf0; | |
| 296 | + header[14] = 0; | |
| 297 | + // FIXME: derive parallelismType properly. | |
| 298 | + header[15] = 0xfc; | |
| 299 | + header[16] = 0xfc | chromaFormatIdc; | |
| 300 | + header[17] = 0xf8 | bitDepthLumaMinus8; | |
| 301 | + header[18] = 0xf8 | bitDepthChromaMinus8; | |
| 302 | + // FIXME: derive avgFrameRate | |
| 303 | + header[19] = 0; | |
| 304 | + header[20] = 0; | |
| 305 | + // constantFrameRate, numTemporalLayers, temporalIdNested all set to 0. | |
| 306 | + header[21] = nalSizeLength - 1; | |
| 307 | + header[22] = numOfArrays; | |
| 308 | + header += 23; | |
| 309 | + for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) { | |
| 310 | + uint8_t type = kHevcNalUnitTypes[i]; | |
| 311 | + size_t numNalus = getNumNalUnitsOfType(type); | |
| 312 | + if (numNalus == 0) { | |
| 313 | + continue; | |
| 314 | + } | |
| 315 | + // array_completeness set to 0. | |
| 316 | + header[0] = type; | |
| 317 | + header[1] = (numNalus >> 8) & 0xff; | |
| 318 | + header[2] = numNalus & 0xff; | |
| 319 | + header += 3; | |
| 320 | + for (size_t j = 0; j < numNalUnits; ++j) { | |
| 321 | + if (getType(j) != type) { | |
| 322 | + continue; | |
| 323 | + } | |
| 324 | + header[0] = (getSize(j) >> 8) & 0xff; | |
| 325 | + header[1] = getSize(j) & 0xff; | |
| 326 | + if (!write(j, header + 2, size - (header - (uint8_t *)hvcc))) { | |
| 327 | + return NO_MEMORY; | |
| 328 | + } | |
| 329 | + header += (2 + getSize(j)); | |
| 330 | + } | |
| 331 | + } | |
| 332 | + CHECK_EQ(header - size, hvcc); | |
| 333 | + | |
| 334 | + return OK; | |
| 335 | +} | |
| 336 | + | |
| 337 | +} // namespace android |
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
| @@ -41,7 +41,7 @@ | ||
| 41 | 41 | #include <cutils/properties.h> |
| 42 | 42 | |
| 43 | 43 | #include "include/ESDS.h" |
| 44 | - | |
| 44 | +#include "include/HevcUtils.h" | |
| 45 | 45 | |
| 46 | 46 | #ifndef __predict_false |
| 47 | 47 | #define __predict_false(exp) __builtin_expect((exp) != 0, 0) |
| @@ -70,6 +70,18 @@ static const char kMetaKey_Build[] = "com.android.build"; | ||
| 70 | 70 | #endif |
| 71 | 71 | static const char kMetaKey_CaptureFps[] = "com.android.capture.fps"; |
| 72 | 72 | |
| 73 | +static const uint8_t kMandatoryHevcNalUnitTypes[3] = { | |
| 74 | + kHevcNalUnitTypeVps, | |
| 75 | + kHevcNalUnitTypeSps, | |
| 76 | + kHevcNalUnitTypePps, | |
| 77 | +}; | |
| 78 | +static const uint8_t kHevcNalUnitTypes[5] = { | |
| 79 | + kHevcNalUnitTypeVps, | |
| 80 | + kHevcNalUnitTypeSps, | |
| 81 | + kHevcNalUnitTypePps, | |
| 82 | + kHevcNalUnitTypePrefixSei, | |
| 83 | + kHevcNalUnitTypeSuffixSei, | |
| 84 | +}; | |
| 73 | 85 | /* uncomment to include model and build in meta */ |
| 74 | 86 | //#define SHOW_MODEL_BUILD 1 |
| 75 | 87 |
| @@ -89,6 +101,7 @@ public: | ||
| 89 | 101 | void writeTrackHeader(bool use32BitOffset = true); |
| 90 | 102 | void bufferChunk(int64_t timestampUs); |
| 91 | 103 | bool isAvc() const { return mIsAvc; } |
| 104 | + bool isHevc() const { return mIsHevc; } | |
| 92 | 105 | bool isAudio() const { return mIsAudio; } |
| 93 | 106 | bool isMPEG4() const { return mIsMPEG4; } |
| 94 | 107 | void addChunkOffset(off64_t offset); |
| @@ -234,6 +247,7 @@ private: | ||
| 234 | 247 | volatile bool mResumed; |
| 235 | 248 | volatile bool mStarted; |
| 236 | 249 | bool mIsAvc; |
| 250 | + bool mIsHevc; | |
| 237 | 251 | bool mIsAudio; |
| 238 | 252 | bool mIsMPEG4; |
| 239 | 253 | int32_t mTrackId; |
| @@ -299,10 +313,17 @@ private: | ||
| 299 | 313 | const uint8_t *parseParamSet( |
| 300 | 314 | const uint8_t *data, size_t length, int type, size_t *paramSetLen); |
| 301 | 315 | |
| 316 | + status_t copyCodecSpecificData(const uint8_t *data, size_t size, size_t minLength = 0); | |
| 317 | + | |
| 302 | 318 | status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size); |
| 303 | 319 | status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size); |
| 304 | 320 | status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size); |
| 305 | 321 | |
| 322 | + status_t makeHEVCCodecSpecificData(const uint8_t *data, size_t size); | |
| 323 | + status_t copyHEVCCodecSpecificData(const uint8_t *data, size_t size); | |
| 324 | + status_t parseHEVCCodecSpecificData( | |
| 325 | + const uint8_t *data, size_t size, HevcParameterSets ¶mSets); | |
| 326 | + | |
| 306 | 327 | // Track authoring progress status |
| 307 | 328 | void trackProgressStatus(int64_t timeUs, status_t err = OK); |
| 308 | 329 | void initTrackingProgressStatus(MetaData *params); |
| @@ -340,6 +361,7 @@ private: | ||
| 340 | 361 | void writeD263Box(); |
| 341 | 362 | void writePaspBox(); |
| 342 | 363 | void writeAvccBox(); |
| 364 | + void writeHvccBox(); | |
| 343 | 365 | void writeUrlBox(); |
| 344 | 366 | void writeDrefBox(); |
| 345 | 367 | void writeDinfBox(); |
| @@ -463,6 +485,8 @@ const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) { | ||
| 463 | 485 | return "s263"; |
| 464 | 486 | } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { |
| 465 | 487 | return "avc1"; |
| 488 | + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { | |
| 489 | + return "hvc1"; | |
| 466 | 490 | } |
| 467 | 491 | } else { |
| 468 | 492 | ALOGE("Track (%s) other than video or audio is not supported", mime); |
| @@ -1465,6 +1489,7 @@ MPEG4Writer::Track::Track( | ||
| 1465 | 1489 | const char *mime; |
| 1466 | 1490 | mMeta->findCString(kKeyMIMEType, &mime); |
| 1467 | 1491 | mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); |
| 1492 | + mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC); | |
| 1468 | 1493 | mIsAudio = !strncasecmp(mime, "audio/", 6); |
| 1469 | 1494 | mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || |
| 1470 | 1495 | !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); |
| @@ -1560,31 +1585,26 @@ void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { | ||
| 1560 | 1585 | const char *mime; |
| 1561 | 1586 | CHECK(mMeta->findCString(kKeyMIMEType, &mime)); |
| 1562 | 1587 | |
| 1588 | + uint32_t type; | |
| 1589 | + const void *data = NULL; | |
| 1590 | + size_t size = 0; | |
| 1563 | 1591 | if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { |
| 1564 | - uint32_t type; | |
| 1565 | - const void *data; | |
| 1566 | - size_t size; | |
| 1567 | - if (mMeta->findData(kKeyAVCC, &type, &data, &size)) { | |
| 1568 | - mCodecSpecificData = malloc(size); | |
| 1569 | - mCodecSpecificDataSize = size; | |
| 1570 | - memcpy(mCodecSpecificData, data, size); | |
| 1571 | - mGotAllCodecSpecificData = true; | |
| 1572 | - } | |
| 1592 | + mMeta->findData(kKeyAVCC, &type, &data, &size); | |
| 1593 | + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) { | |
| 1594 | + mMeta->findData(kKeyHVCC, &type, &data, &size); | |
| 1573 | 1595 | } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) |
| 1574 | 1596 | || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { |
| 1575 | - uint32_t type; | |
| 1576 | - const void *data; | |
| 1577 | - size_t size; | |
| 1578 | 1597 | if (mMeta->findData(kKeyESDS, &type, &data, &size)) { |
| 1579 | 1598 | ESDS esds(data, size); |
| 1580 | - if (esds.getCodecSpecificInfo(&data, &size) == OK) { | |
| 1581 | - mCodecSpecificData = malloc(size); | |
| 1582 | - mCodecSpecificDataSize = size; | |
| 1583 | - memcpy(mCodecSpecificData, data, size); | |
| 1584 | - mGotAllCodecSpecificData = true; | |
| 1599 | + if (esds.getCodecSpecificInfo(&data, &size) != OK) { | |
| 1600 | + data = NULL; | |
| 1601 | + size = 0; | |
| 1585 | 1602 | } |
| 1586 | 1603 | } |
| 1587 | 1604 | } |
| 1605 | + if (data != NULL && copyCodecSpecificData((uint8_t *)data, size) == OK) { | |
| 1606 | + mGotAllCodecSpecificData = true; | |
| 1607 | + } | |
| 1588 | 1608 | } |
| 1589 | 1609 | |
| 1590 | 1610 | MPEG4Writer::Track::~Track() { |
| @@ -1661,7 +1681,7 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { | ||
| 1661 | 1681 | while (!chunk->mSamples.empty()) { |
| 1662 | 1682 | List<MediaBuffer *>::iterator it = chunk->mSamples.begin(); |
| 1663 | 1683 | |
| 1664 | - off64_t offset = chunk->mTrack->isAvc() | |
| 1684 | + off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHevc()) | |
| 1665 | 1685 | ? addLengthPrefixedSample_l(*it) |
| 1666 | 1686 | : addSample_l(*it); |
| 1667 | 1687 |
| @@ -1968,13 +1988,30 @@ status_t MPEG4Writer::Track::copyAVCCodecSpecificData( | ||
| 1968 | 1988 | |
| 1969 | 1989 | // 2 bytes for each of the parameter set length field |
| 1970 | 1990 | // plus the 7 bytes for the header |
| 1971 | - if (size < 4 + 7) { | |
| 1991 | + return copyCodecSpecificData(data, size, 4 + 7); | |
| 1992 | +} | |
| 1993 | + | |
| 1994 | +status_t MPEG4Writer::Track::copyHEVCCodecSpecificData( | |
| 1995 | + const uint8_t *data, size_t size) { | |
| 1996 | + ALOGV("copyHEVCCodecSpecificData"); | |
| 1997 | + | |
| 1998 | + // Min length of HEVC CSD is 23. (ISO/IEC 14496-15:2014 Chapter 8.3.3.1.2) | |
| 1999 | + return copyCodecSpecificData(data, size, 23); | |
| 2000 | +} | |
| 2001 | + | |
| 2002 | +status_t MPEG4Writer::Track::copyCodecSpecificData( | |
| 2003 | + const uint8_t *data, size_t size, size_t minLength) { | |
| 2004 | + if (size < minLength) { | |
| 1972 | 2005 | ALOGE("Codec specific data length too short: %zu", size); |
| 1973 | 2006 | return ERROR_MALFORMED; |
| 1974 | 2007 | } |
| 1975 | 2008 | |
| 1976 | - mCodecSpecificDataSize = size; | |
| 1977 | 2009 | mCodecSpecificData = malloc(size); |
| 2010 | + if (mCodecSpecificData == NULL) { | |
| 2011 | + ALOGE("Failed allocating codec specific data"); | |
| 2012 | + return NO_MEMORY; | |
| 2013 | + } | |
| 2014 | + mCodecSpecificDataSize = size; | |
| 1978 | 2015 | memcpy(mCodecSpecificData, data, size); |
| 1979 | 2016 | return OK; |
| 1980 | 2017 | } |
| @@ -2097,6 +2134,11 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( | ||
| 2097 | 2134 | // ISO 14496-15: AVC file format |
| 2098 | 2135 | mCodecSpecificDataSize += 7; // 7 more bytes in the header |
| 2099 | 2136 | mCodecSpecificData = malloc(mCodecSpecificDataSize); |
| 2137 | + if (mCodecSpecificData == NULL) { | |
| 2138 | + mCodecSpecificDataSize = 0; | |
| 2139 | + ALOGE("Failed allocating codec specific data"); | |
| 2140 | + return NO_MEMORY; | |
| 2141 | + } | |
| 2100 | 2142 | uint8_t *header = (uint8_t *)mCodecSpecificData; |
| 2101 | 2143 | header[0] = 1; // version |
| 2102 | 2144 | header[1] = mProfileIdc; // profile indication |
| @@ -2145,6 +2187,96 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( | ||
| 2145 | 2187 | return OK; |
| 2146 | 2188 | } |
| 2147 | 2189 | |
| 2190 | + | |
| 2191 | +status_t MPEG4Writer::Track::parseHEVCCodecSpecificData( | |
| 2192 | + const uint8_t *data, size_t size, HevcParameterSets ¶mSets) { | |
| 2193 | + | |
| 2194 | + ALOGV("parseHEVCCodecSpecificData"); | |
| 2195 | + const uint8_t *tmp = data; | |
| 2196 | + const uint8_t *nextStartCode = data; | |
| 2197 | + size_t bytesLeft = size; | |
| 2198 | + while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) { | |
| 2199 | + nextStartCode = findNextStartCode(tmp + 4, bytesLeft - 4); | |
| 2200 | + if (nextStartCode == NULL) { | |
| 2201 | + return ERROR_MALFORMED; | |
| 2202 | + } | |
| 2203 | + status_t err = paramSets.addNalUnit(tmp + 4, (nextStartCode - tmp) - 4); | |
| 2204 | + if (err != OK) { | |
| 2205 | + return ERROR_MALFORMED; | |
| 2206 | + } | |
| 2207 | + | |
| 2208 | + // Move on to find the next parameter set | |
| 2209 | + bytesLeft -= nextStartCode - tmp; | |
| 2210 | + tmp = nextStartCode; | |
| 2211 | + } | |
| 2212 | + | |
| 2213 | + size_t csdSize = 23; | |
| 2214 | + const size_t numNalUnits = paramSets.getNumNalUnits(); | |
| 2215 | + for (size_t i = 0; i < ARRAY_SIZE(kMandatoryHevcNalUnitTypes); ++i) { | |
| 2216 | + int type = kMandatoryHevcNalUnitTypes[i]; | |
| 2217 | + size_t numParamSets = paramSets.getNumNalUnitsOfType(type); | |
| 2218 | + if (numParamSets == 0) { | |
| 2219 | + ALOGE("Cound not find NAL unit of type %d", type); | |
| 2220 | + return ERROR_MALFORMED; | |
| 2221 | + } | |
| 2222 | + } | |
| 2223 | + for (size_t i = 0; i < ARRAY_SIZE(kHevcNalUnitTypes); ++i) { | |
| 2224 | + int type = kHevcNalUnitTypes[i]; | |
| 2225 | + size_t numParamSets = paramSets.getNumNalUnitsOfType(type); | |
| 2226 | + if (numParamSets > 0xffff) { | |
| 2227 | + ALOGE("Too many seq parameter sets (%zu) found", numParamSets); | |
| 2228 | + return ERROR_MALFORMED; | |
| 2229 | + } | |
| 2230 | + csdSize += 3; | |
| 2231 | + for (size_t j = 0; j < numNalUnits; ++j) { | |
| 2232 | + if (paramSets.getType(j) != type) { | |
| 2233 | + continue; | |
| 2234 | + } | |
| 2235 | + csdSize += 2 + paramSets.getSize(j); | |
| 2236 | + } | |
| 2237 | + } | |
| 2238 | + mCodecSpecificDataSize = csdSize; | |
| 2239 | + return OK; | |
| 2240 | +} | |
| 2241 | + | |
| 2242 | +status_t MPEG4Writer::Track::makeHEVCCodecSpecificData( | |
| 2243 | + const uint8_t *data, size_t size) { | |
| 2244 | + | |
| 2245 | + if (mCodecSpecificData != NULL) { | |
| 2246 | + ALOGE("Already have codec specific data"); | |
| 2247 | + return ERROR_MALFORMED; | |
| 2248 | + } | |
| 2249 | + | |
| 2250 | + if (size < 4) { | |
| 2251 | + ALOGE("Codec specific data length too short: %zu", size); | |
| 2252 | + return ERROR_MALFORMED; | |
| 2253 | + } | |
| 2254 | + | |
| 2255 | + // Data is in the form of HEVCCodecSpecificData | |
| 2256 | + if (memcmp("\x00\x00\x00\x01", data, 4)) { | |
| 2257 | + return copyHEVCCodecSpecificData(data, size); | |
| 2258 | + } | |
| 2259 | + | |
| 2260 | + HevcParameterSets paramSets; | |
| 2261 | + if (parseHEVCCodecSpecificData(data, size, paramSets) != OK) { | |
| 2262 | + return ERROR_MALFORMED; | |
| 2263 | + } | |
| 2264 | + | |
| 2265 | + mCodecSpecificData = malloc(mCodecSpecificDataSize); | |
| 2266 | + if (mCodecSpecificData == NULL) { | |
| 2267 | + mCodecSpecificDataSize = 0; | |
| 2268 | + ALOGE("Failed allocating codec specific data"); | |
| 2269 | + return NO_MEMORY; | |
| 2270 | + } | |
| 2271 | + status_t err = paramSets.makeHvcc((uint8_t *)mCodecSpecificData, | |
| 2272 | + &mCodecSpecificDataSize, mOwner->useNalLengthFour() ? 5 : 2); | |
| 2273 | + if (err != OK) { | |
| 2274 | + return err; | |
| 2275 | + } | |
| 2276 | + | |
| 2277 | + return OK; | |
| 2278 | +} | |
| 2279 | + | |
| 2148 | 2280 | /* |
| 2149 | 2281 | * Updates the drift time from the audio track so that |
| 2150 | 2282 | * the video track can get the updated drift time information |
| @@ -2228,13 +2360,15 @@ status_t MPEG4Writer::Track::threadEntry() { | ||
| 2228 | 2360 | + buffer->range_offset(), |
| 2229 | 2361 | buffer->range_length()); |
| 2230 | 2362 | CHECK_EQ((status_t)OK, err); |
| 2231 | - } else if (mIsMPEG4) { | |
| 2232 | - mCodecSpecificDataSize = buffer->range_length(); | |
| 2233 | - mCodecSpecificData = malloc(mCodecSpecificDataSize); | |
| 2234 | - memcpy(mCodecSpecificData, | |
| 2363 | + } else if (mIsHevc) { | |
| 2364 | + status_t err = makeHEVCCodecSpecificData( | |
| 2235 | 2365 | (const uint8_t *)buffer->data() |
| 2236 | 2366 | + buffer->range_offset(), |
| 2237 | - buffer->range_length()); | |
| 2367 | + buffer->range_length()); | |
| 2368 | + CHECK_EQ((status_t)OK, err); | |
| 2369 | + } else if (mIsMPEG4) { | |
| 2370 | + copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(), | |
| 2371 | + buffer->range_length()); | |
| 2238 | 2372 | } |
| 2239 | 2373 | |
| 2240 | 2374 | buffer->release(); |
| @@ -2254,10 +2388,10 @@ status_t MPEG4Writer::Track::threadEntry() { | ||
| 2254 | 2388 | buffer->release(); |
| 2255 | 2389 | buffer = NULL; |
| 2256 | 2390 | |
| 2257 | - if (mIsAvc) StripStartcode(copy); | |
| 2391 | + if (mIsAvc || mIsHevc) StripStartcode(copy); | |
| 2258 | 2392 | |
| 2259 | 2393 | size_t sampleSize = copy->range_length(); |
| 2260 | - if (mIsAvc) { | |
| 2394 | + if (mIsAvc || mIsHevc) { | |
| 2261 | 2395 | if (mOwner->useNalLengthFour()) { |
| 2262 | 2396 | sampleSize += 4; |
| 2263 | 2397 | } else { |
| @@ -2457,7 +2591,7 @@ status_t MPEG4Writer::Track::threadEntry() { | ||
| 2457 | 2591 | trackProgressStatus(timestampUs); |
| 2458 | 2592 | } |
| 2459 | 2593 | if (!hasMultipleTracks) { |
| 2460 | - off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) | |
| 2594 | + off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addLengthPrefixedSample_l(copy) | |
| 2461 | 2595 | : mOwner->addSample_l(copy); |
| 2462 | 2596 | |
| 2463 | 2597 | uint32_t count = (mOwner->use32BitFileOffset() |
| @@ -2709,7 +2843,8 @@ status_t MPEG4Writer::Track::checkCodecSpecificData() const { | ||
| 2709 | 2843 | CHECK(mMeta->findCString(kKeyMIMEType, &mime)); |
| 2710 | 2844 | if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) || |
| 2711 | 2845 | !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || |
| 2712 | - !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { | |
| 2846 | + !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) || | |
| 2847 | + !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { | |
| 2713 | 2848 | if (!mCodecSpecificData || |
| 2714 | 2849 | mCodecSpecificDataSize <= 0) { |
| 2715 | 2850 | ALOGE("Missing codec specific data"); |
| @@ -2815,6 +2950,8 @@ void MPEG4Writer::Track::writeVideoFourCCBox() { | ||
| 2815 | 2950 | writeD263Box(); |
| 2816 | 2951 | } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { |
| 2817 | 2952 | writeAvccBox(); |
| 2953 | + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { | |
| 2954 | + writeHvccBox(); | |
| 2818 | 2955 | } |
| 2819 | 2956 | |
| 2820 | 2957 | writePaspBox(); |
| @@ -3070,6 +3207,20 @@ void MPEG4Writer::Track::writeAvccBox() { | ||
| 3070 | 3207 | mOwner->endBox(); // avcC |
| 3071 | 3208 | } |
| 3072 | 3209 | |
| 3210 | + | |
| 3211 | +void MPEG4Writer::Track::writeHvccBox() { | |
| 3212 | + CHECK(mCodecSpecificData); | |
| 3213 | + CHECK_GE(mCodecSpecificDataSize, 5); | |
| 3214 | + | |
| 3215 | + // Patch avcc's lengthSize field to match the number | |
| 3216 | + // of bytes we use to indicate the size of a nal unit. | |
| 3217 | + uint8_t *ptr = (uint8_t *)mCodecSpecificData; | |
| 3218 | + ptr[21] = (ptr[21] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1); | |
| 3219 | + mOwner->beginBox("hvcC"); | |
| 3220 | + mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); | |
| 3221 | + mOwner->endBox(); // hvcC | |
| 3222 | +} | |
| 3223 | + | |
| 3073 | 3224 | void MPEG4Writer::Track::writeD263Box() { |
| 3074 | 3225 | mOwner->beginBox("d263"); |
| 3075 | 3226 | mOwner->writeInt32(0); // vendor |
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
| @@ -22,6 +22,7 @@ | ||
| 22 | 22 | #include <sys/stat.h> |
| 23 | 23 | |
| 24 | 24 | #include "include/ESDS.h" |
| 25 | +#include "include/HevcUtils.h" | |
| 25 | 26 | |
| 26 | 27 | #include <arpa/inet.h> |
| 27 | 28 | #include <cutils/properties.h> |
| @@ -575,6 +576,41 @@ static void reassembleESDS(const sp<ABuffer> &csd0, char *esds) { | ||
| 575 | 576 | |
| 576 | 577 | } |
| 577 | 578 | |
| 579 | +static size_t reassembleHVCC(const sp<ABuffer> &csd0, uint8_t *hvcc, size_t hvccSize, size_t nalSizeLength) { | |
| 580 | + HevcParameterSets paramSets; | |
| 581 | + uint8_t* data = csd0->data(); | |
| 582 | + if (csd0->size() < 4) { | |
| 583 | + ALOGE("csd0 too small"); | |
| 584 | + return 0; | |
| 585 | + } | |
| 586 | + if (memcmp(data, "\x00\x00\x00\x01", 4) != 0) { | |
| 587 | + ALOGE("csd0 doesn't start with a start code"); | |
| 588 | + return 0; | |
| 589 | + } | |
| 590 | + size_t prevNalOffset = 4; | |
| 591 | + status_t err = OK; | |
| 592 | + for (size_t i = 1; i < csd0->size() - 4; ++i) { | |
| 593 | + if (memcmp(&data[i], "\x00\x00\x00\x01", 4) != 0) { | |
| 594 | + continue; | |
| 595 | + } | |
| 596 | + err = paramSets.addNalUnit(&data[prevNalOffset], i - prevNalOffset); | |
| 597 | + if (err != OK) { | |
| 598 | + return 0; | |
| 599 | + } | |
| 600 | + prevNalOffset = i + 4; | |
| 601 | + } | |
| 602 | + err = paramSets.addNalUnit(&data[prevNalOffset], csd0->size() - prevNalOffset); | |
| 603 | + if (err != OK) { | |
| 604 | + return 0; | |
| 605 | + } | |
| 606 | + size_t size = hvccSize; | |
| 607 | + err = paramSets.makeHvcc(hvcc, &size, nalSizeLength); | |
| 608 | + if (err != OK) { | |
| 609 | + return 0; | |
| 610 | + } | |
| 611 | + return size; | |
| 612 | +} | |
| 613 | + | |
| 578 | 614 | void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { |
| 579 | 615 | AString mime; |
| 580 | 616 | if (msg->findString("mime", &mime)) { |
| @@ -693,6 +729,10 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { | ||
| 693 | 729 | // for transporting the CSD to muxers. |
| 694 | 730 | reassembleESDS(csd0, esds); |
| 695 | 731 | meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); |
| 732 | + } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) { | |
| 733 | + uint8_t hvcc[1024]; // that oughta be enough, right? | |
| 734 | + size_t outsize = reassembleHVCC(csd0, hvcc, 1024, 4); | |
| 735 | + meta->setData(kKeyHVCC, kKeyHVCC, hvcc, outsize); | |
| 696 | 736 | } |
| 697 | 737 | } |
| 698 | 738 |
--- /dev/null
+++ b/media/libstagefright/include/HevcUtils.h
| @@ -0,0 +1,93 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2015 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 | +#ifndef HEVC_UTILS_H_ | |
| 18 | + | |
| 19 | +#define HEVC_UTILS_H_ | |
| 20 | + | |
| 21 | +#include <stdint.h> | |
| 22 | + | |
| 23 | +#include <media/stagefright/foundation/ABase.h> | |
| 24 | +#include <media/stagefright/foundation/ABuffer.h> | |
| 25 | +#include <utils/Errors.h> | |
| 26 | +#include <utils/KeyedVector.h> | |
| 27 | +#include <utils/StrongPointer.h> | |
| 28 | +#include <utils/Vector.h> | |
| 29 | + | |
| 30 | +namespace android { | |
| 31 | + | |
| 32 | +enum { | |
| 33 | + kHevcNalUnitTypeVps = 32, | |
| 34 | + kHevcNalUnitTypeSps = 33, | |
| 35 | + kHevcNalUnitTypePps = 34, | |
| 36 | + kHevcNalUnitTypePrefixSei = 39, | |
| 37 | + kHevcNalUnitTypeSuffixSei = 40, | |
| 38 | +}; | |
| 39 | + | |
| 40 | +enum { | |
| 41 | + // uint8_t | |
| 42 | + kGeneralProfileSpace, | |
| 43 | + // uint8_t | |
| 44 | + kGeneralTierFlag, | |
| 45 | + // uint8_t | |
| 46 | + kGeneralProfileIdc, | |
| 47 | + // uint32_t | |
| 48 | + kGeneralProfileCompatibilityFlags, | |
| 49 | + // uint64_t | |
| 50 | + kGeneralConstraintIndicatorFlags, | |
| 51 | + // uint8_t | |
| 52 | + kGeneralLevelIdc, | |
| 53 | + // uint8_t | |
| 54 | + kChromaFormatIdc, | |
| 55 | + // uint8_t | |
| 56 | + kBitDepthLumaMinus8, | |
| 57 | + // uint8_t | |
| 58 | + kBitDepthChromaMinus8, | |
| 59 | +}; | |
| 60 | + | |
| 61 | +class HevcParameterSets { | |
| 62 | +public: | |
| 63 | + HevcParameterSets(); | |
| 64 | + | |
| 65 | + status_t addNalUnit(const uint8_t* data, size_t size); | |
| 66 | + | |
| 67 | + bool findParam8(uint32_t key, uint8_t *param); | |
| 68 | + bool findParam16(uint32_t key, uint16_t *param); | |
| 69 | + bool findParam32(uint32_t key, uint32_t *param); | |
| 70 | + bool findParam64(uint32_t key, uint64_t *param); | |
| 71 | + | |
| 72 | + inline size_t getNumNalUnits() { return mNalUnits.size(); } | |
| 73 | + size_t getNumNalUnitsOfType(uint8_t type); | |
| 74 | + uint8_t getType(size_t index); | |
| 75 | + size_t getSize(size_t index); | |
| 76 | + // Note that this method does not write the start code. | |
| 77 | + bool write(size_t index, uint8_t* dest, size_t size); | |
| 78 | + status_t makeHvcc(uint8_t *hvcc, size_t *hvccSize, size_t nalSizeLength); | |
| 79 | + | |
| 80 | +private: | |
| 81 | + status_t parseVps(const uint8_t* data, size_t size); | |
| 82 | + status_t parseSps(const uint8_t* data, size_t size); | |
| 83 | + status_t parsePps(const uint8_t* data, size_t size); | |
| 84 | + | |
| 85 | + KeyedVector<uint32_t, uint64_t> mParams; | |
| 86 | + Vector<sp<ABuffer>> mNalUnits; | |
| 87 | + | |
| 88 | + DISALLOW_EVIL_CONSTRUCTORS(HevcParameterSets); | |
| 89 | +}; | |
| 90 | + | |
| 91 | +} // namespace android | |
| 92 | + | |
| 93 | +#endif // HEVC_UTILS_H_ |
博客介绍了frameworks-av的一次提交,日志信息显示为在MediaRecorder中添加HEVC支持。还给出了更改概述,涉及多个文件的修改,如mediarecorder.h、MediaProfiles.cpp等,以及新增HevcUtils.cpp和HevcUtils.h文件。
1万+

被折叠的 条评论
为什么被折叠?



