|
|
/*
|
|
|
* 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 <utility>
|
|
|
|
|
|
#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()
|
|
|
: mInfo(kInfoNone) {
|
|
|
}
|
|
|
|
|
|
status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) {
|
|
|
if (size < 1) {
|
|
|
ALOGE("empty NAL b/35467107");
|
|
|
return ERROR_MALFORMED;
|
|
|
}
|
|
|
uint8_t nalUnitType = (data[0] >> 1) & 0x3f;
|
|
|
status_t err = OK;
|
|
|
switch (nalUnitType) {
|
|
|
case 32: // VPS
|
|
|
if (size < 2) {
|
|
|
ALOGE("invalid NAL/VPS size b/35467107");
|
|
|
return ERROR_MALFORMED;
|
|
|
}
|
|
|
err = parseVps(data + 2, size - 2);
|
|
|
break;
|
|
|
case 33: // SPS
|
|
|
if (size < 2) {
|
|
|
ALOGE("invalid NAL/SPS size b/35467107");
|
|
|
return ERROR_MALFORMED;
|
|
|
}
|
|
|
err = parseSps(data + 2, size - 2);
|
|
|
break;
|
|
|
case 34: // PPS
|
|
|
if (size < 2) {
|
|
|
ALOGE("invalid NAL/PPS size b/35467107");
|
|
|
return ERROR_MALFORMED;
|
|
|
}
|
|
|
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> ¶ms) {
|
|
|
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);
|
|
|
|
|
|
if (reader.atLeastNumBitsLeft(96)) {
|
|
|
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.
|
|
|
} else {
|
|
|
reader.skipBits(96);
|
|
|
}
|
|
|
|
|
|
return reader.overRead() ? ERROR_MALFORMED : 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.getBitsWithFallback(3, 0);
|
|
|
// 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.getBitsWithFallback(1, 0);
|
|
|
subLayerLevelPresentFlag[i] = reader.getBitsWithFallback(1, 0);
|
|
|
}
|
|
|
// 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
|
|
|
skipUE(&reader);
|
|
|
uint8_t chromaFormatIdc = parseUEWithFallback(&reader, 0);
|
|
|
mParams.add(kChromaFormatIdc, chromaFormatIdc);
|
|
|
if (chromaFormatIdc == 3) {
|
|
|
// Skip separate_colour_plane_flag
|
|
|
reader.skipBits(1);
|
|
|
}
|
|
|
// Skip pic_width_in_luma_samples
|
|
|
skipUE(&reader);
|
|
|
// Skip pic_height_in_luma_samples
|
|
|
skipUE(&reader);
|
|
|
if (reader.getBitsWithFallback(1, 0) /* i.e. conformance_window_flag */) {
|
|
|
// Skip conf_win_left_offset
|
|
|
skipUE(&reader);
|
|
|
// Skip conf_win_right_offset
|
|
|
skipUE(&reader);
|
|
|
// Skip conf_win_top_offset
|
|
|
skipUE(&reader);
|
|
|
// Skip conf_win_bottom_offset
|
|
|
skipUE(&reader);
|
|
|
}
|
|
|
mParams.add(kBitDepthLumaMinus8, parseUEWithFallback(&reader, 0));
|
|
|
mParams.add(kBitDepthChromaMinus8, parseUEWithFallback(&reader, 0));
|
|
|
|
|
|
// log2_max_pic_order_cnt_lsb_minus4
|
|
|
size_t log2MaxPicOrderCntLsb = parseUEWithFallback(&reader, 0) + (size_t)4;
|
|
|
bool spsSubLayerOrderingInfoPresentFlag = reader.getBitsWithFallback(1, 0);
|
|
|
for (uint32_t i = spsSubLayerOrderingInfoPresentFlag ? 0 : maxSubLayersMinus1;
|
|
|
i <= maxSubLayersMinus1; ++i) {
|
|
|
skipUE(&reader); // sps_max_dec_pic_buffering_minus1[i]
|
|
|
skipUE(&reader); // sps_max_num_reorder_pics[i]
|
|
|
skipUE(&reader); // sps_max_latency_increase_plus1[i]
|
|
|
}
|
|
|
|
|
|
skipUE(&reader); // log2_min_luma_coding_block_size_minus3
|
|
|
skipUE(&reader); // log2_diff_max_min_luma_coding_block_size
|
|
|
skipUE(&reader); // log2_min_luma_transform_block_size_minus2
|
|
|
skipUE(&reader); // log2_diff_max_min_luma_transform_block_size
|
|
|
skipUE(&reader); // max_transform_hierarchy_depth_inter
|
|
|
skipUE(&reader); // max_transform_hierarchy_depth_intra
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // scaling_list_enabled_flag u(1)
|
|
|
// scaling_list_data
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // sps_scaling_list_data_present_flag
|
|
|
for (uint32_t sizeId = 0; sizeId < 4; ++sizeId) {
|
|
|
for (uint32_t matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) {
|
|
|
if (!reader.getBitsWithFallback(1, 1)) {
|
|
|
// scaling_list_pred_mode_flag[sizeId][matrixId]
|
|
|
skipUE(&reader); // scaling_list_pred_matrix_id_delta[sizeId][matrixId]
|
|
|
} else {
|
|
|
uint32_t coefNum = std::min(64, (1 << (4 + (sizeId << 1))));
|
|
|
if (sizeId > 1) {
|
|
|
skipSE(&reader); // scaling_list_dc_coef_minus8[sizeId − 2][matrixId]
|
|
|
}
|
|
|
for (uint32_t i = 0; i < coefNum; ++i) {
|
|
|
skipSE(&reader); // scaling_list_delta_coef
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
reader.skipBits(1); // amp_enabled_flag
|
|
|
reader.skipBits(1); // sample_adaptive_offset_enabled_flag u(1)
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // pcm_enabled_flag
|
|
|
reader.skipBits(4); // pcm_sample_bit_depth_luma_minus1
|
|
|
reader.skipBits(4); // pcm_sample_bit_depth_chroma_minus1 u(4)
|
|
|
skipUE(&reader); // log2_min_pcm_luma_coding_block_size_minus3
|
|
|
skipUE(&reader); // log2_diff_max_min_pcm_luma_coding_block_size
|
|
|
reader.skipBits(1); // pcm_loop_filter_disabled_flag
|
|
|
}
|
|
|
uint32_t numShortTermRefPicSets = parseUEWithFallback(&reader, 0);
|
|
|
uint32_t numPics = 0;
|
|
|
for (uint32_t i = 0; i < numShortTermRefPicSets; ++i) {
|
|
|
// st_ref_pic_set(i)
|
|
|
if (i != 0 && reader.getBitsWithFallback(1, 0)) { // inter_ref_pic_set_prediction_flag
|
|
|
reader.skipBits(1); // delta_rps_sign
|
|
|
skipUE(&reader); // abs_delta_rps_minus1
|
|
|
uint32_t nextNumPics = 0;
|
|
|
for (uint32_t j = 0; j <= numPics; ++j) {
|
|
|
if (reader.getBitsWithFallback(1, 0) // used_by_curr_pic_flag[j]
|
|
|
|| reader.getBitsWithFallback(1, 0)) { // use_delta_flag[j]
|
|
|
++nextNumPics;
|
|
|
}
|
|
|
}
|
|
|
numPics = nextNumPics;
|
|
|
} else {
|
|
|
uint32_t numNegativePics = parseUEWithFallback(&reader, 0);
|
|
|
uint32_t numPositivePics = parseUEWithFallback(&reader, 0);
|
|
|
if (numNegativePics > UINT32_MAX - numPositivePics) {
|
|
|
return ERROR_MALFORMED;
|
|
|
}
|
|
|
numPics = numNegativePics + numPositivePics;
|
|
|
for (uint32_t j = 0; j < numPics; ++j) {
|
|
|
skipUE(&reader); // delta_poc_s0|1_minus1[i]
|
|
|
reader.skipBits(1); // used_by_curr_pic_s0|1_flag[i]
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // long_term_ref_pics_present_flag
|
|
|
uint32_t numLongTermRefPicSps = parseUEWithFallback(&reader, 0);
|
|
|
for (uint32_t i = 0; i < numLongTermRefPicSps; ++i) {
|
|
|
reader.skipBits(log2MaxPicOrderCntLsb); // lt_ref_pic_poc_lsb_sps[i]
|
|
|
reader.skipBits(1); // used_by_curr_pic_lt_sps_flag[i]
|
|
|
}
|
|
|
}
|
|
|
reader.skipBits(1); // sps_temporal_mvp_enabled_flag
|
|
|
reader.skipBits(1); // strong_intra_smoothing_enabled_flag
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // vui_parameters_present_flag
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // aspect_ratio_info_present_flag
|
|
|
uint32_t aspectRatioIdc = reader.getBitsWithFallback(8, 0);
|
|
|
if (aspectRatioIdc == 0xFF /* EXTENDED_SAR */) {
|
|
|
reader.skipBits(16); // sar_width
|
|
|
reader.skipBits(16); // sar_height
|
|
|
}
|
|
|
}
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // overscan_info_present_flag
|
|
|
reader.skipBits(1); // overscan_appropriate_flag
|
|
|
}
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // video_signal_type_present_flag
|
|
|
reader.skipBits(3); // video_format
|
|
|
uint32_t videoFullRangeFlag;
|
|
|
if (reader.getBitsGraceful(1, &videoFullRangeFlag)) {
|
|
|
mParams.add(kVideoFullRangeFlag, videoFullRangeFlag);
|
|
|
}
|
|
|
if (reader.getBitsWithFallback(1, 0)) { // colour_description_present_flag
|
|
|
mInfo = (Info)(mInfo | kInfoHasColorDescription);
|
|
|
uint32_t colourPrimaries, transferCharacteristics, matrixCoeffs;
|
|
|
if (reader.getBitsGraceful(8, &colourPrimaries)) {
|
|
|
mParams.add(kColourPrimaries, colourPrimaries);
|
|
|
}
|
|
|
if (reader.getBitsGraceful(8, &transferCharacteristics)) {
|
|
|
mParams.add(kTransferCharacteristics, transferCharacteristics);
|
|
|
if (transferCharacteristics == 16 /* ST 2084 */
|
|
|
|| transferCharacteristics == 18 /* ARIB STD-B67 HLG */) {
|
|
|
mInfo = (Info)(mInfo | kInfoIsHdr);
|
|
|
}
|
|
|
}
|
|
|
if (reader.getBitsGraceful(8, &matrixCoeffs)) {
|
|
|
mParams.add(kMatrixCoeffs, matrixCoeffs);
|
|
|
}
|
|
|
}
|
|
|
// skip rest of VUI
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return reader.overRead() ? ERROR_MALFORMED : 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
|