Move current bitstream parser to more appropriate directory.
This CL groups together the code that has to do with parsing H264 bitstreams. This code logically belongs together, and having it in the same directory not only simplifies things from a project structure perspective, but also makes it easier to refactor out common parts incrementally. An added benefit is that this simplifies modular compilation, where for example one would like a build of WebRTC without the H264 codec-specific parts. BUG=webrtc:6338 Review-Url: https://codereview.webrtc.org/2370853005 Cr-Commit-Position: refs/heads/master@{#14684}
This commit is contained in:
@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "webrtc/modules/video_coding/utility/h264_bitstream_parser.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/bitbuffer.h"
|
||||
#include "webrtc/base/bytebuffer.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
|
||||
#include "webrtc/common_video/h264/h264_common.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
#define RETURN_FALSE_ON_FAIL(x) \
|
||||
if (!(x)) { \
|
||||
LOG_F(LS_ERROR) << "FAILED: " #x; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
H264BitstreamParser::H264BitstreamParser() {}
|
||||
H264BitstreamParser::~H264BitstreamParser() {}
|
||||
|
||||
bool H264BitstreamParser::ParseNonParameterSetNalu(const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type) {
|
||||
RTC_CHECK(sps_);
|
||||
RTC_CHECK(pps_);
|
||||
last_slice_qp_delta_ = rtc::Optional<int32_t>();
|
||||
std::unique_ptr<rtc::Buffer> slice_rbsp(
|
||||
H264::ParseRbsp(source, source_length));
|
||||
rtc::BitBuffer slice_reader(slice_rbsp->data() + H264::kNaluTypeSize,
|
||||
slice_rbsp->size() - H264::kNaluTypeSize);
|
||||
// Check to see if this is an IDR slice, which has an extra field to parse
|
||||
// out.
|
||||
bool is_idr = (source[0] & 0x0F) == H264::NaluType::kIdr;
|
||||
uint8_t nal_ref_idc = (source[0] & 0x60) >> 5;
|
||||
uint32_t golomb_tmp;
|
||||
uint32_t bits_tmp;
|
||||
|
||||
// first_mb_in_slice: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
// slice_type: ue(v)
|
||||
uint32_t slice_type;
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&slice_type));
|
||||
// slice_type's 5..9 range is used to indicate that all slices of a picture
|
||||
// have the same value of slice_type % 5, we don't care about that, so we map
|
||||
// to the corresponding 0..4 range.
|
||||
slice_type %= 5;
|
||||
// pic_parameter_set_id: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
if (sps_->separate_colour_plane_flag == 1) {
|
||||
// colour_plane_id
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2));
|
||||
}
|
||||
// frame_num: u(v)
|
||||
// Represented by log2_max_frame_num_minus4 + 4 bits.
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadBits(&bits_tmp, sps_->log2_max_frame_num_minus4 + 4));
|
||||
uint32_t field_pic_flag = 0;
|
||||
if (sps_->frame_mbs_only_flag == 0) {
|
||||
// field_pic_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&field_pic_flag, 1));
|
||||
if (field_pic_flag != 0) {
|
||||
// bottom_field_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
}
|
||||
if (is_idr) {
|
||||
// idr_pic_id: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
// pic_order_cnt_lsb: u(v)
|
||||
// Represented by sps_.log2_max_pic_order_cnt_lsb_minus4 + 4 bits.
|
||||
if (sps_->pic_order_cnt_type == 0) {
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(
|
||||
&bits_tmp, sps_->log2_max_pic_order_cnt_lsb_minus4 + 4));
|
||||
if (pps_->bottom_field_pic_order_in_frame_present_flag &&
|
||||
field_pic_flag == 0) {
|
||||
// delta_pic_order_cnt_bottom: se(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
}
|
||||
if (sps_->pic_order_cnt_type == 1 &&
|
||||
!sps_->delta_pic_order_always_zero_flag) {
|
||||
// delta_pic_order_cnt[0]: se(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
if (pps_->bottom_field_pic_order_in_frame_present_flag && !field_pic_flag) {
|
||||
// delta_pic_order_cnt[1]: se(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
}
|
||||
if (pps_->redundant_pic_cnt_present_flag) {
|
||||
// redundant_pic_cnt: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
if (slice_type == H264::SliceType::kB) {
|
||||
// direct_spatial_mv_pred_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 1));
|
||||
}
|
||||
switch (slice_type) {
|
||||
case H264::SliceType::kP:
|
||||
case H264::SliceType::kB:
|
||||
case H264::SliceType::kSp:
|
||||
uint32_t num_ref_idx_active_override_flag;
|
||||
// num_ref_idx_active_override_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadBits(&num_ref_idx_active_override_flag, 1));
|
||||
if (num_ref_idx_active_override_flag != 0) {
|
||||
// num_ref_idx_l0_active_minus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
if (slice_type == H264::SliceType::kB) {
|
||||
// num_ref_idx_l1_active_minus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// assume nal_unit_type != 20 && nal_unit_type != 21:
|
||||
RTC_CHECK_NE(nalu_type, 20);
|
||||
RTC_CHECK_NE(nalu_type, 21);
|
||||
// if (nal_unit_type == 20 || nal_unit_type == 21)
|
||||
// ref_pic_list_mvc_modification()
|
||||
// else
|
||||
{
|
||||
// ref_pic_list_modification():
|
||||
// |slice_type| checks here don't use named constants as they aren't named
|
||||
// in the spec for this segment. Keeping them consistent makes it easier to
|
||||
// verify that they are both the same.
|
||||
if (slice_type % 5 != 2 && slice_type % 5 != 4) {
|
||||
// ref_pic_list_modification_flag_l0: u(1)
|
||||
uint32_t ref_pic_list_modification_flag_l0;
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadBits(&ref_pic_list_modification_flag_l0, 1));
|
||||
if (ref_pic_list_modification_flag_l0) {
|
||||
uint32_t modification_of_pic_nums_idc;
|
||||
do {
|
||||
// modification_of_pic_nums_idc: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(
|
||||
&modification_of_pic_nums_idc));
|
||||
if (modification_of_pic_nums_idc == 0 ||
|
||||
modification_of_pic_nums_idc == 1) {
|
||||
// abs_diff_pic_num_minus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
} else if (modification_of_pic_nums_idc == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
} while (modification_of_pic_nums_idc != 3);
|
||||
}
|
||||
}
|
||||
if (slice_type % 5 == 1) {
|
||||
// ref_pic_list_modification_flag_l1: u(1)
|
||||
uint32_t ref_pic_list_modification_flag_l1;
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadBits(&ref_pic_list_modification_flag_l1, 1));
|
||||
if (ref_pic_list_modification_flag_l1) {
|
||||
uint32_t modification_of_pic_nums_idc;
|
||||
do {
|
||||
// modification_of_pic_nums_idc: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(
|
||||
&modification_of_pic_nums_idc));
|
||||
if (modification_of_pic_nums_idc == 0 ||
|
||||
modification_of_pic_nums_idc == 1) {
|
||||
// abs_diff_pic_num_minus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
} else if (modification_of_pic_nums_idc == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
} while (modification_of_pic_nums_idc != 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(pbos): Do we need support for pred_weight_table()?
|
||||
RTC_CHECK(
|
||||
!((pps_->weighted_pred_flag && (slice_type == H264::SliceType::kP ||
|
||||
slice_type == H264::SliceType::kSp)) ||
|
||||
(pps_->weighted_bipred_idc != 0 && slice_type == H264::SliceType::kB)))
|
||||
<< "Missing support for pred_weight_table().";
|
||||
// if ((weighted_pred_flag && (slice_type == P || slice_type == SP)) ||
|
||||
// (weighted_bipred_idc == 1 && slice_type == B)) {
|
||||
// pred_weight_table()
|
||||
// }
|
||||
if (nal_ref_idc != 0) {
|
||||
// dec_ref_pic_marking():
|
||||
if (is_idr) {
|
||||
// no_output_of_prior_pics_flag: u(1)
|
||||
// long_term_reference_flag: u(1)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadBits(&bits_tmp, 2));
|
||||
} else {
|
||||
// adaptive_ref_pic_marking_mode_flag: u(1)
|
||||
uint32_t adaptive_ref_pic_marking_mode_flag;
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadBits(&adaptive_ref_pic_marking_mode_flag, 1));
|
||||
if (adaptive_ref_pic_marking_mode_flag) {
|
||||
uint32_t memory_management_control_operation;
|
||||
do {
|
||||
// memory_management_control_operation: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(
|
||||
&memory_management_control_operation));
|
||||
if (memory_management_control_operation == 1 ||
|
||||
memory_management_control_operation == 3) {
|
||||
// difference_of_pic_nums_minus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
if (memory_management_control_operation == 2) {
|
||||
// long_term_pic_num: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
if (memory_management_control_operation == 3 ||
|
||||
memory_management_control_operation == 6) {
|
||||
// long_term_frame_idx: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
if (memory_management_control_operation == 4) {
|
||||
// max_long_term_frame_idx_plus1: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
} while (memory_management_control_operation != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pps_->entropy_coding_mode_flag &&
|
||||
slice_type != H264::SliceType::kI && slice_type != H264::SliceType::kSi) {
|
||||
// cabac_init_idc: ue(v)
|
||||
RETURN_FALSE_ON_FAIL(slice_reader.ReadExponentialGolomb(&golomb_tmp));
|
||||
}
|
||||
int32_t last_slice_qp_delta;
|
||||
RETURN_FALSE_ON_FAIL(
|
||||
slice_reader.ReadSignedExponentialGolomb(&last_slice_qp_delta));
|
||||
last_slice_qp_delta_ = rtc::Optional<int32_t>(last_slice_qp_delta);
|
||||
return true;
|
||||
}
|
||||
|
||||
void H264BitstreamParser::ParseSlice(const uint8_t* slice, size_t length) {
|
||||
H264::NaluType nalu_type = H264::ParseNaluType(slice[0]);
|
||||
switch (nalu_type) {
|
||||
case H264::NaluType::kSps: {
|
||||
sps_ = SpsParser::ParseSps(slice + H264::kNaluTypeSize,
|
||||
length - H264::kNaluTypeSize);
|
||||
if (!sps_)
|
||||
FATAL() << "Unable to parse SPS from H264 bitstream.";
|
||||
break;
|
||||
}
|
||||
case H264::NaluType::kPps: {
|
||||
pps_ = PpsParser::ParsePps(slice + H264::kNaluTypeSize,
|
||||
length - H264::kNaluTypeSize);
|
||||
if (!pps_)
|
||||
FATAL() << "Unable to parse PPS from H264 bitstream.";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
RTC_CHECK(ParseNonParameterSetNalu(slice, length, nalu_type))
|
||||
<< "Failed to parse picture slice.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void H264BitstreamParser::ParseBitstream(const uint8_t* bitstream,
|
||||
size_t length) {
|
||||
std::vector<H264::NaluIndex> nalu_indices =
|
||||
H264::FindNaluIndices(bitstream, length);
|
||||
RTC_CHECK(!nalu_indices.empty());
|
||||
for (const H264::NaluIndex& index : nalu_indices)
|
||||
ParseSlice(&bitstream[index.payload_start_offset], index.payload_size);
|
||||
}
|
||||
|
||||
bool H264BitstreamParser::GetLastSliceQp(int* qp) const {
|
||||
if (!last_slice_qp_delta_ || !pps_)
|
||||
return false;
|
||||
*qp = 26 + pps_->pic_init_qp_minus26 + *last_slice_qp_delta_;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_H264_BITSTREAM_PARSER_H_
|
||||
#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_H264_BITSTREAM_PARSER_H_
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "webrtc/base/optional.h"
|
||||
#include "webrtc/common_video/h264/pps_parser.h"
|
||||
#include "webrtc/common_video/h264/sps_parser.h"
|
||||
|
||||
namespace rtc {
|
||||
class BitBufferWriter;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Stateful H264 bitstream parser (due to SPS/PPS). Used to parse out QP values
|
||||
// from the bitstream.
|
||||
// TODO(pbos): Unify with RTP SPS parsing and only use one H264 parser.
|
||||
// TODO(pbos): If/when this gets used on the receiver side CHECKs must be
|
||||
// removed and gracefully abort as we have no control over receive-side
|
||||
// bitstreams.
|
||||
class H264BitstreamParser {
|
||||
public:
|
||||
H264BitstreamParser();
|
||||
virtual ~H264BitstreamParser();
|
||||
|
||||
// Parse an additional chunk of H264 bitstream.
|
||||
void ParseBitstream(const uint8_t* bitstream, size_t length);
|
||||
|
||||
// Get the last extracted QP value from the parsed bitstream.
|
||||
bool GetLastSliceQp(int* qp) const;
|
||||
|
||||
protected:
|
||||
void ParseSlice(const uint8_t* slice, size_t length);
|
||||
bool ParseNonParameterSetNalu(const uint8_t* source,
|
||||
size_t source_length,
|
||||
uint8_t nalu_type);
|
||||
|
||||
// SPS/PPS state, updated when parsing new SPS/PPS, used to parse slices.
|
||||
rtc::Optional<SpsParser::SpsState> sps_;
|
||||
rtc::Optional<PpsParser::PpsState> pps_;
|
||||
|
||||
// Last parsed slice QP.
|
||||
rtc::Optional<int32_t> last_slice_qp_delta_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_H264_BITSTREAM_PARSER_H_
|
||||
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/video_coding/utility/h264_bitstream_parser.h"
|
||||
|
||||
#include "webrtc/test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// SPS/PPS part of below chunk.
|
||||
uint8_t kH264SpsPps[] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x20, 0xda,
|
||||
0x01, 0x40, 0x16, 0xe8, 0x06, 0xd0, 0xa1, 0x35, 0x00,
|
||||
0x00, 0x00, 0x01, 0x68, 0xce, 0x06, 0xe2};
|
||||
|
||||
// Contains enough of the image slice to contain slice QP.
|
||||
uint8_t kH264BitstreamChunk[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x20, 0xda, 0x01, 0x40, 0x16,
|
||||
0xe8, 0x06, 0xd0, 0xa1, 0x35, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x06,
|
||||
0xe2, 0x00, 0x00, 0x00, 0x01, 0x65, 0xb8, 0x40, 0xf0, 0x8c, 0x03, 0xf2,
|
||||
0x75, 0x67, 0xad, 0x41, 0x64, 0x24, 0x0e, 0xa0, 0xb2, 0x12, 0x1e, 0xf8,
|
||||
};
|
||||
|
||||
// Contains enough of the image slice to contain slice QP.
|
||||
uint8_t kH264BitstreamNextImageSliceChunk[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x41, 0xe2, 0x01, 0x16, 0x0e, 0x3e, 0x2b, 0x86,
|
||||
};
|
||||
|
||||
uint8_t kH264BitstreamChunkCabac[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x27, 0x64, 0x00, 0x0d, 0xac, 0x52, 0x30, 0x50,
|
||||
0x7e, 0xc0, 0x5a, 0x81, 0x01, 0x01, 0x18, 0x56, 0xbd, 0xef, 0x80, 0x80,
|
||||
0x00, 0x00, 0x00, 0x01, 0x28, 0xfe, 0x09, 0x8b,
|
||||
};
|
||||
|
||||
// Contains enough of the image slice to contain slice QP.
|
||||
uint8_t kH264BitstreamNextImageSliceChunkCabac[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x21, 0xe1, 0x05, 0x11, 0x3f, 0x9a, 0xae, 0x46,
|
||||
0x70, 0xbf, 0xc1, 0x4a, 0x16, 0x8f, 0x51, 0xf4, 0xca, 0xfb, 0xa3, 0x65,
|
||||
};
|
||||
|
||||
TEST(H264BitstreamParserTest, ReportsNoQpWithoutParsedSlices) {
|
||||
H264BitstreamParser h264_parser;
|
||||
int qp;
|
||||
EXPECT_FALSE(h264_parser.GetLastSliceQp(&qp));
|
||||
}
|
||||
|
||||
TEST(H264BitstreamParserTest, ReportsNoQpWithOnlyParsedPpsAndSpsSlices) {
|
||||
H264BitstreamParser h264_parser;
|
||||
h264_parser.ParseBitstream(kH264SpsPps, sizeof(kH264SpsPps));
|
||||
int qp;
|
||||
EXPECT_FALSE(h264_parser.GetLastSliceQp(&qp));
|
||||
}
|
||||
|
||||
TEST(H264BitstreamParserTest, ReportsLastSliceQpForImageSlices) {
|
||||
H264BitstreamParser h264_parser;
|
||||
h264_parser.ParseBitstream(kH264BitstreamChunk, sizeof(kH264BitstreamChunk));
|
||||
int qp;
|
||||
ASSERT_TRUE(h264_parser.GetLastSliceQp(&qp));
|
||||
EXPECT_EQ(35, qp);
|
||||
|
||||
// Parse an additional image slice.
|
||||
h264_parser.ParseBitstream(kH264BitstreamNextImageSliceChunk,
|
||||
sizeof(kH264BitstreamNextImageSliceChunk));
|
||||
ASSERT_TRUE(h264_parser.GetLastSliceQp(&qp));
|
||||
EXPECT_EQ(37, qp);
|
||||
}
|
||||
|
||||
TEST(H264BitstreamParserTest, ReportsLastSliceQpForCABACImageSlices) {
|
||||
H264BitstreamParser h264_parser;
|
||||
h264_parser.ParseBitstream(kH264BitstreamChunkCabac,
|
||||
sizeof(kH264BitstreamChunkCabac));
|
||||
int qp;
|
||||
EXPECT_FALSE(h264_parser.GetLastSliceQp(&qp));
|
||||
|
||||
// Parse an additional image slice.
|
||||
h264_parser.ParseBitstream(kH264BitstreamNextImageSliceChunkCabac,
|
||||
sizeof(kH264BitstreamNextImageSliceChunkCabac));
|
||||
ASSERT_TRUE(h264_parser.GetLastSliceQp(&qp));
|
||||
EXPECT_EQ(24, qp);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -21,8 +21,6 @@
|
||||
'sources': [
|
||||
'frame_dropper.cc',
|
||||
'frame_dropper.h',
|
||||
'h264_bitstream_parser.cc',
|
||||
'h264_bitstream_parser.h',
|
||||
'ivf_file_writer.cc',
|
||||
'ivf_file_writer.h',
|
||||
'moving_average.cc',
|
||||
|
||||
Reference in New Issue
Block a user