diff options
-rw-r--r-- | common/Android.bp | 1 | ||||
-rw-r--r-- | common/EncodeHelpers.cpp | 7 | ||||
-rw-r--r-- | common/NalParser.cpp | 178 | ||||
-rw-r--r-- | common/include/v4l2_codec2/common/NalParser.h | 22 | ||||
-rw-r--r-- | components/V4L2DecodeComponent.cpp | 39 |
5 files changed, 212 insertions, 35 deletions
diff --git a/common/Android.bp b/common/Android.bp index af26b49..9c795ab 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -31,6 +31,7 @@ cc_library { "libchrome", "libcutils", "liblog", + "libstagefright_foundation", "libui", "libutils", "libv4l2_codec2_accel" diff --git a/common/EncodeHelpers.cpp b/common/EncodeHelpers.cpp index 31646eb..2ab0e71 100644 --- a/common/EncodeHelpers.cpp +++ b/common/EncodeHelpers.cpp @@ -124,9 +124,6 @@ android_ycbcr getGraphicBlockInfo(const C2ConstGraphicBlock& block) { void extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, const uint8_t* data, size_t length) { - constexpr uint8_t kTypeSeqParamSet = 7; - constexpr uint8_t kTypePicParamSet = 8; - // Android frameworks needs 4 bytes start code. constexpr uint8_t kStartCode[] = {0x00, 0x00, 0x00, 0x01}; constexpr int kStartCodeLength = 4; @@ -142,9 +139,9 @@ void extractCSDInfo(std::unique_ptr<C2StreamInitDataInfo::output>* const csd, co NalParser parser(data, length); while (parser.locateNextNal()) { if (parser.length() == 0) continue; - uint8_t nalType = *parser.data() & 0x1f; + uint8_t nalType = parser.type(); ALOGV("find next NAL: type=%d, length=%zu", nalType, parser.length()); - if (nalType != kTypeSeqParamSet && nalType != kTypePicParamSet) continue; + if (nalType != NalParser::kSPSType && nalType != NalParser::kPPSType) continue; if (tmpOutput + kStartCodeLength + parser.length() > tmpConfigDataEnd) { ALOGE("Buffer overflow on extracting codec config data (length=%zu)", length); diff --git a/common/NalParser.cpp b/common/NalParser.cpp index 4682775..3216574 100644 --- a/common/NalParser.cpp +++ b/common/NalParser.cpp @@ -2,12 +2,85 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +//#define LOG_NDEBUG 0 +#define LOG_TAG "NalParser" + #include <v4l2_codec2/common/NalParser.h> #include <algorithm> +#include <media/stagefright/foundation/ABitReader.h> +#include <utils/Log.h> + namespace android { +namespace { + +enum H264ProfileIDC { + kProfileIDCAVLC444 = 44, + kProfileIDScalableBaseline = 83, + kProfileIDScalableHigh = 86, + kProfileIDCHigh = 100, + kProfileIDHigh10 = 110, + kProfileIDSMultiviewHigh = 118, + kProfileIDHigh422 = 122, + kProfileIDStereoHigh = 128, + kProfileIDHigh444Predictive = 244, +}; + +constexpr uint32_t kYUV444Idc = 3; + +// Read unsigned int encoded with exponential-golomb. +uint32_t parseUE(ABitReader* br) { + uint32_t numZeroes = 0; + while (br->getBits(1) == 0) { + ++numZeroes; + } + uint32_t val = br->getBits(numZeroes); + return val + (1u << numZeroes) - 1; +} + +// Read signed int encoded with exponential-golomb. +int32_t parseSE(ABitReader* br) { + uint32_t codeNum = parseUE(br); + return (codeNum & 1) ? (codeNum + 1) >> 1 : -static_cast<int32_t>(codeNum >> 1); +} + +// Skip a H.264 sequence scaling list in the specified bitstream. +void skipScalingList(ABitReader* br, size_t scalingListSize) { + size_t nextScale = 8; + size_t lastScale = 8; + for (size_t j = 0; j < scalingListSize; ++j) { + if (nextScale != 0) { + int32_t deltaScale = parseSE(br); // delta_sl + if (deltaScale < -128) { + ALOGW("delta scale (%d) is below range, capping to -128", deltaScale); + deltaScale = -128; + } else if (deltaScale > 127) { + ALOGW("delta scale (%d) is above range, capping to 127", deltaScale); + deltaScale = 127; + } + nextScale = (lastScale + (deltaScale + 256)) % 256; + } + lastScale = (nextScale == 0) ? lastScale : nextScale; + } +} + +// Skip the H.264 sequence scaling matrix in the specified bitstream. +void skipScalingMatrix(ABitReader* br, size_t numScalingLists) { + for (size_t i = 0; i < numScalingLists; ++i) { + if (br->getBits(1)) { // seq_scaling_list_present_flag + if (i < 6) { + skipScalingList(br, 16); + } else { + skipScalingList(br, 64); + } + } + } +} + +} // namespace + NalParser::NalParser(const uint8_t* data, size_t length) : mCurrNalDataPos(data), mDataEnd(data + length) { mNextNalStartCodePos = findNextStartCodePos(); @@ -20,6 +93,16 @@ bool NalParser::locateNextNal() { return true; } +bool NalParser::locateSPS() { + while (locateNextNal()) { + if (length() == 0) continue; + if (type() != kSPSType) continue; + return true; + } + + return false; +} + const uint8_t* NalParser::data() const { return mCurrNalDataPos; } @@ -31,9 +114,104 @@ size_t NalParser::length() const { return *(mNextNalStartCodePos - 1) == 0x00 ? length - 1 : length; } +uint8_t NalParser::type() const { + // First byte is forbidden_zero_bit (1) + nal_ref_idc (2) + nal_unit_type (5) + constexpr uint8_t kNALTypeMask = 0x1f; + return *mCurrNalDataPos & kNALTypeMask; +} + const uint8_t* NalParser::findNextStartCodePos() const { return std::search(mCurrNalDataPos, mDataEnd, kNalStartCode, kNalStartCode + kNalStartCodeLength); } +bool NalParser::findCodedColorAspects(ColorAspects* colorAspects) { + ALOG_ASSERT(colorAspects); + ALOG_ASSERT(type() == kSPSType); + + // Unfortunately we can't directly jump to the Video Usability Information (VUI) parameters that + // contain the color aspects. We need to parse the entire SPS header up until the values we + // need. + + // Skip first byte containing type. + ABitReader br(mCurrNalDataPos + 1, length() - 1); + + uint32_t profileIDC = br.getBits(8); // profile_idc + br.skipBits(16); // constraint flags + reserved bits + level_idc + parseUE(&br); // seq_parameter_set_id + + if (profileIDC == kProfileIDCHigh || profileIDC == kProfileIDHigh10 || + profileIDC == kProfileIDHigh422 || profileIDC == kProfileIDHigh444Predictive || + profileIDC == kProfileIDCAVLC444 || profileIDC == kProfileIDScalableBaseline || + profileIDC == kProfileIDScalableHigh || profileIDC == kProfileIDSMultiviewHigh || + profileIDC == kProfileIDStereoHigh) { + uint32_t chromaFormatIDC = parseUE(&br); + if (chromaFormatIDC == kYUV444Idc) { // chroma_format_idc + br.skipBits(1); // separate_colour_plane_flag + } + parseUE(&br); // bit_depth_luma_minus8 + parseUE(&br); // bit_depth_chroma_minus8 + br.skipBits(1); // lossless_qpprime_y_zero_flag + + if (br.getBits(1)) { // seq_scaling_matrix_present_flag + const size_t numScalingLists = (chromaFormatIDC != kYUV444Idc) ? 8 : 12; + skipScalingMatrix(&br, numScalingLists); + } + } + + parseUE(&br); // log2_max_frame_num_minus4 + uint32_t pictureOrderCountType = parseUE(&br); // pic_order_cnt_type + if (pictureOrderCountType == 0) { + parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4 + } else if (pictureOrderCountType == 1) { + br.skipBits(1); // delta_pic_order_always_zero_flag + parseSE(&br); // offset_for_non_ref_pic + parseSE(&br); // offset_for_top_to_bottom_field + uint32_t numReferenceFrames = parseUE(&br); // num_ref_frames_in_pic_order_cnt_cycle + for (uint32_t i = 0; i < numReferenceFrames; ++i) { + parseUE(&br); // offset_for_ref_frame + } + } + + parseUE(&br); // num_ref_frames + br.skipBits(1); // gaps_in_frame_num_value_allowed_flag + parseUE(&br); // pic_width_in_mbs_minus1 + parseUE(&br); // pic_height_in_map_units_minus1 + if (!br.getBits(1)) { // frame_mbs_only_flag + br.skipBits(1); // mb_adaptive_frame_field_flag + } + br.skipBits(1); // direct_8x8_inference_flag + + if (br.getBits(1)) { // frame_cropping_flag + parseUE(&br); // frame_cropping_rect_left_offset + parseUE(&br); // frame_cropping_rect_right_offset + parseUE(&br); // frame_cropping_rect_top_offset + parseUE(&br); // frame_cropping_rect_bottom_offset + } + + if (br.getBits(1)) { // vui_parameters_present_flag + if (br.getBits(1)) { // VUI aspect_ratio_info_present_flag + if (br.getBits(8) == 255) { // VUI aspect_ratio_idc == extended sample aspect ratio + br.skipBits(32); // VUI sar_width + sar_height + } + } + + if (br.getBits(1)) { // VUI overscan_info_present_flag + br.skipBits(1); // VUI overscan_appropriate_flag + } + if (br.getBits(1)) { // VUI video_signal_type_present_flag + br.skipBits(3); // VUI video_format + colorAspects->fullRange = br.getBits(1); // VUI video_full_range_flag + if (br.getBits(1)) { // VUI color_description_present_flag + colorAspects->primaries = br.getBits(8); // VUI colour_primaries + colorAspects->transfer = br.getBits(8); // VUI transfer_characteristics + colorAspects->coeffs = br.getBits(8); // VUI matrix_coefficients + return !br.overRead(); + } + } + } + + return false; +} + } // namespace android diff --git a/common/include/v4l2_codec2/common/NalParser.h b/common/include/v4l2_codec2/common/NalParser.h index ba323ee..69f56c3 100644 --- a/common/include/v4l2_codec2/common/NalParser.h +++ b/common/include/v4l2_codec2/common/NalParser.h @@ -12,6 +12,19 @@ namespace android { // Helper class to parse H264 NAL units from data. class NalParser { public: + // Type of a SPS NAL unit. + static constexpr uint8_t kSPSType = 7; + // Type of a PPS NAL unit. + static constexpr uint8_t kPPSType = 8; + + // Parameters related to a video's color aspects. + struct ColorAspects { + int32_t primaries; + int32_t transfer; + int32_t coeffs; + bool fullRange; + }; + NalParser(const uint8_t* data, size_t length); // Locates the next NAL after |mNextNalStartCodePos|. If there is one, updates |mCurrNalDataPos| @@ -22,12 +35,21 @@ public: // Note: This method must be called prior to data() and length(). bool locateNextNal(); + // Locate the sequence parameter set (SPS). + bool locateSPS(); + // Gets current NAL data (start code is not included). const uint8_t* data() const; // Gets the byte length of current NAL data (start code is not included). size_t length() const; + // Get the type of the current NAL unit. + uint8_t type() const; + + // Find the H.264 video's color aspects in the current SPS NAL. + bool findCodedColorAspects(ColorAspects* colorAspects); + private: const uint8_t* findNextStartCodePos() const; diff --git a/components/V4L2DecodeComponent.cpp b/components/V4L2DecodeComponent.cpp index 97cfc36..c48878b 100644 --- a/components/V4L2DecodeComponent.cpp +++ b/components/V4L2DecodeComponent.cpp @@ -24,7 +24,7 @@ #include <log/log.h> #include <media/stagefright/foundation/ColorUtils.h> -#include <h264_parser.h> +#include <v4l2_codec2/common/NalParser.h> #include <v4l2_codec2/common/VideoTypes.h> #include <v4l2_codec2/components/BitstreamBuffer.h> #include <v4l2_codec2/components/V4L2Decoder.h> @@ -42,44 +42,23 @@ int32_t frameIndexToBitstreamId(c2_cntr64_t frameIndex) { bool parseCodedColorAspects(const C2ConstLinearBlock& input, C2StreamColorAspectsInfo::input* codedAspects) { C2ReadView view = input.map().get(); - const uint8_t* data = view.data(); - const uint32_t size = view.capacity(); - - std::unique_ptr<media::H264Parser> h264Parser = std::make_unique<media::H264Parser>(); - h264Parser->SetStream(data, static_cast<off_t>(size)); - media::H264NALU nalu; - media::H264Parser::Result parRes = h264Parser->AdvanceToNextNALU(&nalu); - if (parRes != media::H264Parser::kEOStream && parRes != media::H264Parser::kOk) { - ALOGE("H264 AdvanceToNextNALU error: %d", static_cast<int>(parRes)); - return false; - } - if (nalu.nal_unit_type != media::H264NALU::kSPS) { - ALOGV("NALU is not SPS"); - return false; - } + NalParser parser(view.data(), view.capacity()); - int spsId; - parRes = h264Parser->ParseSPS(&spsId); - if (parRes != media::H264Parser::kEOStream && parRes != media::H264Parser::kOk) { - ALOGE("H264 ParseSPS error: %d", static_cast<int>(parRes)); + if (!parser.locateSPS()) { + ALOGV("Couldn't find SPS"); return false; } - // Parse ISO color aspects from H264 SPS bitstream. - const media::H264SPS* sps = h264Parser->GetSPS(spsId); - if (!sps->colour_description_present_flag) { - ALOGV("No Color Description in SPS"); + NalParser::ColorAspects aspects; + if (!parser.findCodedColorAspects(&aspects)) { + ALOGV("Couldn't find color description in SPS"); return false; } - int32_t primaries = sps->colour_primaries; - int32_t transfer = sps->transfer_characteristics; - int32_t coeffs = sps->matrix_coefficients; - bool fullRange = sps->video_full_range_flag; // Convert ISO color aspects to ColorUtils::ColorAspects. ColorAspects colorAspects; - ColorUtils::convertIsoColorAspectsToCodecAspects(primaries, transfer, coeffs, fullRange, - colorAspects); + ColorUtils::convertIsoColorAspectsToCodecAspects( + aspects.primaries, aspects.transfer, aspects.coeffs, aspects.fullRange, colorAspects); ALOGV("Parsed ColorAspects from bitstream: (R:%d, P:%d, M:%d, T:%d)", colorAspects.mRange, colorAspects.mPrimaries, colorAspects.mMatrixCoeffs, colorAspects.mTransfer); |