зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 5 changesets (bug 1852333) for causing failures in test_hevc_playback.html CLOSED TREE
Backed out changeset c7a5969f8559 (bug 1852333) Backed out changeset ccf4b32b18bc (bug 1852333) Backed out changeset a4b9a5ac9fec (bug 1852333) Backed out changeset 2d92dd455f19 (bug 1852333) Backed out changeset ee2c23cc0a50 (bug 1852333)
This commit is contained in:
Родитель
7b16815000
Коммит
b237a3cd5d
|
@ -3,9 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BitWriter.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "MediaData.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
|
@ -96,9 +93,4 @@ void BitWriter::CloseWithRbspTrailing() {
|
|||
WriteBits(0, (8 - mBitIndex) & 7);
|
||||
}
|
||||
|
||||
void BitWriter::AdvanceBytes(uint32_t aByteOffset) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mBitIndex == 0);
|
||||
mPosition += aByteOffset;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -29,12 +29,6 @@ class BitWriter {
|
|||
// Write RBSP trailing bits.
|
||||
void CloseWithRbspTrailing();
|
||||
|
||||
// Advance position forward without modifying buffer, which is usually used
|
||||
// along with the case when directly appending a byte array to the
|
||||
// MediaByteBuffer for the efficiency, instead of writing bits one by one.
|
||||
// So this can only be called when the bit index is zero.
|
||||
void AdvanceBytes(uint32_t aByteOffset);
|
||||
|
||||
// Return the number of bits written so far;
|
||||
size_t BitCount() const { return mPosition * 8 + mBitIndex; }
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include "BitReader.h"
|
||||
#include "BitWriter.h"
|
||||
|
@ -57,25 +56,6 @@ TEST(BitWriter, BitWriter)
|
|||
EXPECT_EQ(length, BitReader::GetBitLength(test));
|
||||
}
|
||||
|
||||
TEST(BitWriter, AdvanceBytes)
|
||||
{
|
||||
RefPtr<MediaByteBuffer> test = new MediaByteBuffer();
|
||||
BitWriter b(test);
|
||||
b.WriteBits(0xff, 8);
|
||||
EXPECT_EQ(test->Length(), 1u);
|
||||
|
||||
uint8_t data[] = {0xfe, 0xfd};
|
||||
test->AppendElements(data, sizeof(data));
|
||||
EXPECT_EQ(test->Length(), 3u);
|
||||
b.AdvanceBytes(2);
|
||||
|
||||
b.WriteBits(0xfc, 8);
|
||||
EXPECT_EQ(test->Length(), 4u);
|
||||
|
||||
BitReader c(test);
|
||||
EXPECT_EQ(c.ReadU32(), 0xfffefdfc);
|
||||
}
|
||||
|
||||
TEST(BitWriter, SPS)
|
||||
{
|
||||
uint8_t sps_pps[] = {0x01, 0x4d, 0x40, 0x0c, 0xff, 0xe1, 0x00, 0x1b, 0x67,
|
||||
|
|
|
@ -456,36 +456,6 @@ bool AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool AnnexB::ConvertSampleToHVCC(mozilla::MediaRawData* aSample) {
|
||||
if (IsHVCC(aSample)) {
|
||||
return ConvertHVCCTo4BytesHVCC(aSample).isOk();
|
||||
}
|
||||
if (!IsAnnexB(aSample)) {
|
||||
// Not AnnexB, nothing to convert.
|
||||
return true;
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> nalu;
|
||||
ByteWriter<BigEndian> writer(nalu);
|
||||
BufferReader reader(aSample->Data(), aSample->Size());
|
||||
if (ParseNALUnits(writer, reader).isErr()) {
|
||||
LOG("Failed fo parse AnnexB NALU for HVCC");
|
||||
return false;
|
||||
}
|
||||
UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
|
||||
if (!samplewriter->Replace(nalu.Elements(), nalu.Length())) {
|
||||
LOG("Failed fo replace NALU");
|
||||
return false;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT_IF(aSample->mExtraData,
|
||||
HVCCConfig::Parse(aSample).isOk());
|
||||
// TODO : currently we don't set the fake header because we expect the sample
|
||||
// already has a valid extradata. (set by the media change monitor) We can
|
||||
// support setting a specific/fake header if we want to support HEVC encoding.
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Result<mozilla::Ok, nsresult> AnnexB::ConvertAVCCTo4BytesAVCC(
|
||||
mozilla::MediaRawData* aSample) {
|
||||
|
|
|
@ -43,8 +43,6 @@ class AnnexB {
|
|||
static bool ConvertSampleToAVCC(
|
||||
mozilla::MediaRawData* aSample,
|
||||
const RefPtr<mozilla::MediaByteBuffer>& aAVCCHeader = nullptr);
|
||||
// Convert a sample from Annex B to HVCC. An HVCC extradata must not be set.
|
||||
static bool ConvertSampleToHVCC(mozilla::MediaRawData* aSample);
|
||||
|
||||
// Covert sample to 4 bytes NALU byte stream.
|
||||
static mozilla::Result<mozilla::Ok, nsresult> ConvertAVCCTo4BytesAVCC(
|
||||
|
|
|
@ -34,10 +34,6 @@
|
|||
aDest.var = uval; \
|
||||
}
|
||||
|
||||
mozilla::LazyLogModule gH264("H264");
|
||||
|
||||
#define LOG(msg, ...) MOZ_LOG(gH264, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Default scaling lists (per spec).
|
||||
|
@ -1294,12 +1290,7 @@ void H264::WriteExtraData(MediaByteBuffer* aDestExtraData,
|
|||
if (!aSample || aSample->Size() < 3) {
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (aSample->mTrackInfo &&
|
||||
!aSample->mTrackInfo->mMimeType.EqualsLiteral("video/avc")) {
|
||||
LOG("Only allow 'video/avc' (mimeType=%s)",
|
||||
aSample->mTrackInfo->mMimeType.get());
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
// TODO : check video mime type to ensure the sample is AVC
|
||||
return AVCCConfig::Parse(aSample->mExtraData);
|
||||
}
|
||||
|
||||
|
@ -1326,5 +1317,3 @@ void H264::WriteExtraData(MediaByteBuffer* aDestExtraData,
|
|||
#undef READSE
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#undef LOG
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "ByteStreamsUtils.h"
|
||||
#include "ByteWriter.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
|
@ -26,24 +25,32 @@ mozilla::LazyLogModule gH265("H265");
|
|||
#define LOG(msg, ...) MOZ_LOG(gH265, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
||||
#define LOGV(msg, ...) MOZ_LOG(gH265, LogLevel::Verbose, (msg, ##__VA_ARGS__))
|
||||
|
||||
#define READ_IN_RANGE_OR_RETURN(dest, val, min, max) \
|
||||
do { \
|
||||
if ((int64_t)(min) <= (int64_t)(val) && (val) <= (max)) { \
|
||||
(dest) = (val); \
|
||||
} else { \
|
||||
LOG(#dest " is not in the range of [" #min "," #max "]"); \
|
||||
return mozilla::Err(NS_ERROR_FAILURE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define IN_RANGE_OR_RETURN(val, min, max) \
|
||||
do { \
|
||||
int64_t temp = AssertedCast<int64_t>(val); \
|
||||
if ((temp) < (min) || (max) < (temp)) { \
|
||||
if ((int64_t)(val) < (int64_t)(min) || (max) < (val)) { \
|
||||
LOG(#val " is not in the range of [" #min "," #max "]"); \
|
||||
return mozilla::Err(NS_ERROR_FAILURE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NON_ZERO_OR_RETURN(dest, val) \
|
||||
do { \
|
||||
int64_t temp = AssertedCast<int64_t>(val); \
|
||||
if ((temp) != 0) { \
|
||||
(dest) = (temp); \
|
||||
} else { \
|
||||
LOG(#dest " should be non-zero"); \
|
||||
return mozilla::Err(NS_ERROR_FAILURE); \
|
||||
} \
|
||||
#define NON_ZERO_OR_RETURN(dest, val) \
|
||||
do { \
|
||||
if ((val) != 0) { \
|
||||
(dest) = (val); \
|
||||
} else { \
|
||||
LOG(#dest " should be non-zero"); \
|
||||
return mozilla::Err(NS_ERROR_FAILURE); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -61,20 +68,11 @@ H265NALU::H265NALU(const uint8_t* aData, uint32_t aByteSize)
|
|||
|
||||
/* static */ Result<HVCCConfig, nsresult> HVCCConfig::Parse(
|
||||
const mozilla::MediaRawData* aSample) {
|
||||
if (!aSample) {
|
||||
LOG("No sample");
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (aSample->Size() < 3) {
|
||||
LOG("Incorrect sample size %zu", aSample->Size());
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (aSample->mTrackInfo &&
|
||||
!aSample->mTrackInfo->mMimeType.EqualsLiteral("video/hevc")) {
|
||||
LOG("Only allow 'video/hevc' (mimeType=%s)",
|
||||
aSample->mTrackInfo->mMimeType.get());
|
||||
if (!aSample || aSample->Size() < 3) {
|
||||
LOG("No sample or incorrect sample size");
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
// TODO : check video mime type to ensure the sample is for HEVC
|
||||
return HVCCConfig::Parse(aSample->mExtraData);
|
||||
}
|
||||
|
||||
|
@ -82,12 +80,8 @@ H265NALU::H265NALU(const uint8_t* aData, uint32_t aByteSize)
|
|||
Result<HVCCConfig, nsresult> HVCCConfig::Parse(
|
||||
const mozilla::MediaByteBuffer* aExtraData) {
|
||||
// From configurationVersion to numOfArrays, total 184 bits (23 bytes)
|
||||
if (!aExtraData) {
|
||||
LOG("No extra-data");
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (aExtraData->Length() < 23) {
|
||||
LOG("Incorrect extra-data size %zu", aExtraData->Length());
|
||||
if (!aExtraData || aExtraData->Length() < 23) {
|
||||
LOG("No extra-data or incorrect extra-data size");
|
||||
return mozilla::Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
const auto& byteBuffer = *aExtraData;
|
||||
|
@ -154,17 +148,10 @@ Result<HVCCConfig, nsresult> HVCCConfig::Parse(
|
|||
return hvcc;
|
||||
}
|
||||
|
||||
uint32_t HVCCConfig::NumSPS() const {
|
||||
uint32_t spsCounter = 0;
|
||||
for (const auto& nalu : mNALUs) {
|
||||
if (nalu.IsSPS()) {
|
||||
spsCounter++;
|
||||
}
|
||||
}
|
||||
return spsCounter;
|
||||
}
|
||||
|
||||
bool HVCCConfig::HasSPS() const {
|
||||
if (mNALUs.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
bool hasSPS = false;
|
||||
for (const auto& nalu : mNALUs) {
|
||||
if (nalu.IsSPS()) {
|
||||
|
@ -187,18 +174,15 @@ Result<H265SPS, nsresult> H265::DecodeSPSFromSPSNALU(const H265NALU& aSPSNALU) {
|
|||
// H265 spec, 7.3.2.2.1 seq_parameter_set_rbsp
|
||||
H265SPS sps;
|
||||
BitReader reader(rbsp);
|
||||
sps.sps_video_parameter_set_id = reader.ReadBits(4);
|
||||
IN_RANGE_OR_RETURN(sps.sps_video_parameter_set_id, 0, 15);
|
||||
sps.sps_max_sub_layers_minus1 = reader.ReadBits(3);
|
||||
IN_RANGE_OR_RETURN(sps.sps_max_sub_layers_minus1, 0, 6);
|
||||
READ_IN_RANGE_OR_RETURN(sps.sps_video_parameter_set_id, reader.ReadBits(4), 0,
|
||||
15);
|
||||
READ_IN_RANGE_OR_RETURN(sps.sps_max_sub_layers_minus1, reader.ReadBits(3), 0,
|
||||
6);
|
||||
sps.sps_temporal_id_nesting_flag = reader.ReadBit();
|
||||
ParseProfileTierLevel(reader, true /* aProfilePresentFlag, true per spec*/,
|
||||
sps.sps_max_sub_layers_minus1, sps.profile_tier_level);
|
||||
sps.sps_seq_parameter_set_id = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.sps_seq_parameter_set_id, 0, 15);
|
||||
sps.chroma_format_idc = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.chroma_format_idc, 0, 3);
|
||||
|
||||
READ_IN_RANGE_OR_RETURN(sps.sps_seq_parameter_set_id, reader.ReadUE(), 0, 15);
|
||||
READ_IN_RANGE_OR_RETURN(sps.chroma_format_idc, reader.ReadUE(), 0, 3);
|
||||
if (sps.chroma_format_idc == 3) {
|
||||
sps.separate_colour_plane_flag = reader.ReadBit();
|
||||
}
|
||||
|
@ -222,12 +206,10 @@ Result<H265SPS, nsresult> H265::DecodeSPSFromSPSNALU(const H265NALU& aSPSNALU) {
|
|||
sps.conf_win_top_offset = reader.ReadUE();
|
||||
sps.conf_win_bottom_offset = reader.ReadUE();
|
||||
}
|
||||
sps.bit_depth_luma_minus8 = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.bit_depth_luma_minus8, 0, 8);
|
||||
sps.bit_depth_chroma_minus8 = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.bit_depth_chroma_minus8, 0, 8);
|
||||
sps.log2_max_pic_order_cnt_lsb_minus4 = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.log2_max_pic_order_cnt_lsb_minus4, 0, 12);
|
||||
READ_IN_RANGE_OR_RETURN(sps.bit_depth_luma_minus8, reader.ReadUE(), 0, 8);
|
||||
READ_IN_RANGE_OR_RETURN(sps.bit_depth_chroma_minus8, reader.ReadUE(), 0, 8);
|
||||
READ_IN_RANGE_OR_RETURN(sps.log2_max_pic_order_cnt_lsb_minus4,
|
||||
reader.ReadUE(), 0, 12);
|
||||
sps.sps_sub_layer_ordering_info_present_flag = reader.ReadBit();
|
||||
for (auto i = sps.sps_sub_layer_ordering_info_present_flag
|
||||
? 0
|
||||
|
@ -257,14 +239,12 @@ Result<H265SPS, nsresult> H265::DecodeSPSFromSPSNALU(const H265NALU& aSPSNALU) {
|
|||
|
||||
sps.pcm_enabled_flag = reader.ReadBit();
|
||||
if (sps.pcm_enabled_flag) {
|
||||
sps.pcm_sample_bit_depth_luma_minus1 = reader.ReadBits(3);
|
||||
IN_RANGE_OR_RETURN(sps.pcm_sample_bit_depth_luma_minus1, 0,
|
||||
sps.BitDepthLuma());
|
||||
sps.pcm_sample_bit_depth_chroma_minus1 = reader.ReadBits(3);
|
||||
IN_RANGE_OR_RETURN(sps.pcm_sample_bit_depth_chroma_minus1, 0,
|
||||
sps.BitDepthChroma());
|
||||
sps.log2_min_pcm_luma_coding_block_size_minus3 = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.log2_min_pcm_luma_coding_block_size_minus3, 0, 2);
|
||||
READ_IN_RANGE_OR_RETURN(sps.pcm_sample_bit_depth_luma_minus1,
|
||||
reader.ReadBits(3), 0, sps.BitDepthLuma());
|
||||
READ_IN_RANGE_OR_RETURN(sps.pcm_sample_bit_depth_chroma_minus1,
|
||||
reader.ReadBits(3), 0, sps.BitDepthChroma());
|
||||
READ_IN_RANGE_OR_RETURN(sps.log2_min_pcm_luma_coding_block_size_minus3,
|
||||
reader.ReadUE(), 0, 2);
|
||||
uint32_t log2MinIpcmCbSizeY{sps.log2_min_pcm_luma_coding_block_size_minus3 +
|
||||
3};
|
||||
sps.log2_diff_max_min_pcm_luma_coding_block_size = reader.ReadUE();
|
||||
|
@ -283,9 +263,8 @@ Result<H265SPS, nsresult> H265::DecodeSPSFromSPSNALU(const H265NALU& aSPSNALU) {
|
|||
sps.pcm_loop_filter_disabled_flag = reader.ReadBit();
|
||||
}
|
||||
|
||||
sps.num_short_term_ref_pic_sets = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(sps.num_short_term_ref_pic_sets, 0,
|
||||
kMaxShortTermRefPicSets);
|
||||
READ_IN_RANGE_OR_RETURN(sps.num_short_term_ref_pic_sets, reader.ReadUE(), 0,
|
||||
kMaxShortTermRefPicSets);
|
||||
for (auto i = 0; i < sps.num_short_term_ref_pic_sets; i++) {
|
||||
if (auto rv = ParseStRefPicSet(reader, i, sps); rv.isErr()) {
|
||||
LOG("Failed to parse short-term reference picture set.");
|
||||
|
@ -295,8 +274,8 @@ Result<H265SPS, nsresult> H265::DecodeSPSFromSPSNALU(const H265NALU& aSPSNALU) {
|
|||
const auto long_term_ref_pics_present_flag = reader.ReadBit();
|
||||
if (long_term_ref_pics_present_flag) {
|
||||
uint32_t num_long_term_ref_pics_sps;
|
||||
num_long_term_ref_pics_sps = reader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(num_long_term_ref_pics_sps, 0, kMaxLongTermRefPicSets);
|
||||
READ_IN_RANGE_OR_RETURN(num_long_term_ref_pics_sps, reader.ReadUE(), 0,
|
||||
kMaxLongTermRefPicSets);
|
||||
for (auto i = 0; i < num_long_term_ref_pics_sps; i++) {
|
||||
Unused << reader.ReadBits(sps.log2_max_pic_order_cnt_lsb_minus4 +
|
||||
4); // lt_ref_pic_poc_lsb_sps[i]
|
||||
|
@ -433,8 +412,8 @@ Result<Ok, nsresult> H265::ParseStRefPicSet(BitReader& aReader,
|
|||
if (inter_ref_pic_set_prediction_flag) {
|
||||
int delta_idx_minus1 = 0;
|
||||
if (aStRpsIdx == aSPS.num_short_term_ref_pic_sets) {
|
||||
delta_idx_minus1 = aReader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(delta_idx_minus1, 0, aStRpsIdx - 1);
|
||||
READ_IN_RANGE_OR_RETURN(delta_idx_minus1, aReader.ReadUE(), 0,
|
||||
aStRpsIdx - 1);
|
||||
}
|
||||
const uint32_t RefRpsIdx = aStRpsIdx - (delta_idx_minus1 + 1); // (7-59)
|
||||
const bool delta_rps_sign = aReader.ReadBit();
|
||||
|
@ -505,8 +484,8 @@ Result<Ok, nsresult> H265::ParseStRefPicSet(BitReader& aReader,
|
|||
curStRefPicSet.num_negative_pics = aReader.ReadUE();
|
||||
curStRefPicSet.num_positive_pics = aReader.ReadUE();
|
||||
for (auto i = 0; i < curStRefPicSet.num_negative_pics; i++) {
|
||||
const uint32_t delta_poc_s0_minus1 = aReader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(delta_poc_s0_minus1, 0, 0x7FFF);
|
||||
uint32_t delta_poc_s0_minus1;
|
||||
READ_IN_RANGE_OR_RETURN(delta_poc_s0_minus1, aReader.ReadUE(), 0, 0x7FFF);
|
||||
if (i == 0) {
|
||||
// (7-67)
|
||||
curStRefPicSet.deltaPocS0[i] = -(delta_poc_s0_minus1 + 1);
|
||||
|
@ -518,8 +497,8 @@ Result<Ok, nsresult> H265::ParseStRefPicSet(BitReader& aReader,
|
|||
curStRefPicSet.usedByCurrPicS0[i] = aReader.ReadBit();
|
||||
}
|
||||
for (auto i = 0; i < curStRefPicSet.num_positive_pics; i++) {
|
||||
const int delta_poc_s1_minus1 = aReader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(delta_poc_s1_minus1, 0, 0x7FFF);
|
||||
int delta_poc_s1_minus1;
|
||||
READ_IN_RANGE_OR_RETURN(delta_poc_s1_minus1, aReader.ReadUE(), 0, 0x7FFF);
|
||||
if (i == 0) {
|
||||
// (7-68)
|
||||
curStRefPicSet.deltaPocS1[i] = delta_poc_s1_minus1 + 1;
|
||||
|
@ -711,8 +690,7 @@ Result<Ok, nsresult> H265::ParseAndIgnoreHrdParameters(
|
|||
}
|
||||
int cpb_cnt_minus1 = 0;
|
||||
if (!low_delay_hrd_flag) {
|
||||
cpb_cnt_minus1 = aReader.ReadUE();
|
||||
IN_RANGE_OR_RETURN(cpb_cnt_minus1, 0, 31);
|
||||
READ_IN_RANGE_OR_RETURN(cpb_cnt_minus1, aReader.ReadUE(), 0, 31);
|
||||
}
|
||||
if (nal_hrd_parameters_present_flag) {
|
||||
if (auto rv = ParseAndIgnoreSubLayerHrdParameters(
|
||||
|
@ -750,14 +728,6 @@ Result<Ok, nsresult> H265::ParseAndIgnoreSubLayerHrdParameters(
|
|||
return Ok();
|
||||
}
|
||||
|
||||
bool H265SPS::operator==(const H265SPS& aOther) const {
|
||||
return memcmp(this, &aOther, sizeof(H265SPS)) == 0;
|
||||
}
|
||||
|
||||
bool H265SPS::operator!=(const H265SPS& aOther) const {
|
||||
return !(operator==(aOther));
|
||||
}
|
||||
|
||||
gfx::IntSize H265SPS::GetImageSize() const {
|
||||
return gfx::IntSize(pic_width_in_luma_samples, pic_height_in_luma_samples);
|
||||
}
|
||||
|
@ -961,228 +931,6 @@ already_AddRefed<mozilla::MediaByteBuffer> H265::DecodeNALUnit(
|
|||
return rbsp.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<mozilla::MediaByteBuffer> H265::ExtractHVCCExtraData(
|
||||
const mozilla::MediaRawData* aSample) {
|
||||
size_t sampleSize = aSample->Size();
|
||||
if (aSample->mCrypto.IsEncrypted()) {
|
||||
// The content is encrypted, we can only parse the non-encrypted data.
|
||||
MOZ_ASSERT(aSample->mCrypto.mPlainSizes.Length() > 0);
|
||||
if (aSample->mCrypto.mPlainSizes.Length() == 0 ||
|
||||
aSample->mCrypto.mPlainSizes[0] > sampleSize) {
|
||||
LOG("Invalid crypto content");
|
||||
return nullptr;
|
||||
}
|
||||
sampleSize = aSample->mCrypto.mPlainSizes[0];
|
||||
}
|
||||
|
||||
auto hvcc = HVCCConfig::Parse(aSample);
|
||||
if (hvcc.isErr()) {
|
||||
LOG("Only support extracting extradata from HVCC");
|
||||
return nullptr;
|
||||
}
|
||||
const auto nalLenSize = hvcc.unwrap().NALUSize();
|
||||
BufferReader reader(aSample->Data(), sampleSize);
|
||||
|
||||
nsTArray<Maybe<H265SPS>> spsRefTable;
|
||||
nsTArray<H265NALU> spsNALUs;
|
||||
// If we encounter SPS with the same id but different content, we will stop
|
||||
// attempting to detect duplicates.
|
||||
bool checkDuplicate = true;
|
||||
const H265SPS* firstSPS = nullptr;
|
||||
|
||||
RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer;
|
||||
while (reader.Remaining() > nalLenSize) {
|
||||
// ISO/IEC 14496-15, 4.2.3.2 Syntax. (NALUSample) Reading the size of NALU.
|
||||
uint32_t nalLen = 0;
|
||||
switch (nalLenSize) {
|
||||
case 1:
|
||||
Unused << reader.ReadU8().map(
|
||||
[&](uint8_t x) mutable { return nalLen = x; });
|
||||
break;
|
||||
case 2:
|
||||
Unused << reader.ReadU16().map(
|
||||
[&](uint16_t x) mutable { return nalLen = x; });
|
||||
break;
|
||||
case 3:
|
||||
Unused << reader.ReadU24().map(
|
||||
[&](uint32_t x) mutable { return nalLen = x; });
|
||||
break;
|
||||
default:
|
||||
MOZ_DIAGNOSTIC_ASSERT(nalLenSize == 4);
|
||||
Unused << reader.ReadU32().map(
|
||||
[&](uint32_t x) mutable { return nalLen = x; });
|
||||
break;
|
||||
}
|
||||
const uint8_t* p = reader.Read(nalLen);
|
||||
if (!p) {
|
||||
// The read failed, but we may already have some SPS data so break out of
|
||||
// reading and process what we have, if any.
|
||||
break;
|
||||
}
|
||||
const H265NALU nalu(p, nalLen);
|
||||
LOGV("Found NALU, type=%u", nalu.mNalUnitType);
|
||||
if (nalu.IsSPS()) {
|
||||
auto rv = H265::DecodeSPSFromSPSNALU(nalu);
|
||||
if (rv.isErr()) {
|
||||
// Invalid SPS, ignore.
|
||||
LOG("Ignore invalid SPS");
|
||||
continue;
|
||||
}
|
||||
const H265SPS sps = rv.unwrap();
|
||||
const uint8_t spsId = sps.sps_seq_parameter_set_id; // 0~15
|
||||
if (spsId >= spsRefTable.Length()) {
|
||||
if (!spsRefTable.SetLength(spsId + 1, fallible)) {
|
||||
NS_WARNING("OOM while expanding spsRefTable!");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (checkDuplicate && spsRefTable[spsId] &&
|
||||
*(spsRefTable[spsId]) == sps) {
|
||||
// Duplicate ignore.
|
||||
continue;
|
||||
}
|
||||
if (spsRefTable[spsId]) {
|
||||
// We already have detected a SPS with this Id. Just to be safe we
|
||||
// disable SPS duplicate detection.
|
||||
checkDuplicate = false;
|
||||
} else {
|
||||
spsRefTable[spsId] = Some(sps);
|
||||
spsNALUs.AppendElement(nalu);
|
||||
if (!firstSPS) {
|
||||
firstSPS = spsRefTable[spsId].ptr();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGV("Found %zu SPS NALU", spsNALUs.Length());
|
||||
if (!spsNALUs.IsEmpty()) {
|
||||
MOZ_ASSERT(firstSPS);
|
||||
BitWriter writer(extradata);
|
||||
|
||||
// ISO/IEC 14496-15, HEVCDecoderConfigurationRecord. But we only append SPS.
|
||||
writer.WriteBits(1, 8); // version
|
||||
const auto& profile = firstSPS->profile_tier_level;
|
||||
writer.WriteBits(profile.general_profile_space, 2);
|
||||
writer.WriteBits(profile.general_tier_flag, 1);
|
||||
writer.WriteBits(profile.general_profile_idc, 5);
|
||||
writer.WriteU32(profile.general_profile_compatibility_flags);
|
||||
|
||||
// general_constraint_indicator_flags
|
||||
writer.WriteBit(profile.general_progressive_source_flag);
|
||||
writer.WriteBit(profile.general_interlaced_source_flag);
|
||||
writer.WriteBit(profile.general_non_packed_constraint_flag);
|
||||
writer.WriteBit(profile.general_frame_only_constraint_flag);
|
||||
writer.WriteBits(0, 44); /* ignored 44 bits */
|
||||
|
||||
writer.WriteU8(profile.general_level_idc);
|
||||
writer.WriteBits(0, 4); // reserved
|
||||
writer.WriteBits(0, 12); // min_spatial_segmentation_idc
|
||||
writer.WriteBits(0, 6); // reserved
|
||||
writer.WriteBits(0, 2); // parallelismType
|
||||
writer.WriteBits(0, 6); // reserved
|
||||
writer.WriteBits(firstSPS->chroma_format_idc, 2);
|
||||
writer.WriteBits(0, 5); // reserved
|
||||
writer.WriteBits(firstSPS->bit_depth_luma_minus8, 3);
|
||||
writer.WriteBits(0, 5); // reserved
|
||||
writer.WriteBits(firstSPS->bit_depth_chroma_minus8, 3);
|
||||
// avgFrameRate + constantFrameRate + numTemporalLayers + temporalIdNested
|
||||
writer.WriteBits(0, 22);
|
||||
writer.WriteBits(nalLenSize - 1, 2); // lengthSizeMinusOne
|
||||
writer.WriteU8(1); // numOfArrays, only SPS
|
||||
for (auto j = 0; j < 1; j++) {
|
||||
writer.WriteBits(0, 2); // array_completeness + reserved
|
||||
writer.WriteBits(H265NALU::SPS_NUT, 6); // NAL_unit_type
|
||||
writer.WriteBits(spsNALUs.Length(), 16); // numNalus
|
||||
for (auto i = 0; i < spsNALUs.Length(); i++) {
|
||||
writer.WriteBits(spsNALUs[i].mNALU.Length(),
|
||||
16); // nalUnitLength
|
||||
MOZ_ASSERT(writer.BitCount() % 8 == 0);
|
||||
extradata->AppendElements(spsNALUs[i].mNALU.Elements(),
|
||||
spsNALUs[i].mNALU.Length());
|
||||
writer.AdvanceBytes(spsNALUs[i].mNALU.Length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extradata.forget();
|
||||
}
|
||||
|
||||
class SPSIterator final {
|
||||
public:
|
||||
explicit SPSIterator(const HVCCConfig& aConfig) : mConfig(aConfig) {}
|
||||
|
||||
SPSIterator& operator++() {
|
||||
size_t idx = 0;
|
||||
for (idx = mNextIdx; idx < mConfig.mNALUs.Length(); idx++) {
|
||||
if (mConfig.mNALUs[idx].IsSPS()) {
|
||||
mSPS = &mConfig.mNALUs[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
mNextIdx = idx + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return mNextIdx < mConfig.mNALUs.Length(); }
|
||||
|
||||
const H265NALU* operator*() const { return mSPS ? mSPS : nullptr; }
|
||||
|
||||
private:
|
||||
size_t mNextIdx = 0;
|
||||
const H265NALU* mSPS = nullptr;
|
||||
const HVCCConfig& mConfig;
|
||||
};
|
||||
|
||||
/* static */
|
||||
bool AreTwoSPSIdentical(const H265NALU& aLhs, const H265NALU& aRhs) {
|
||||
MOZ_ASSERT(aLhs.IsSPS() && aRhs.IsSPS());
|
||||
auto rv1 = H265::DecodeSPSFromSPSNALU(aLhs);
|
||||
auto rv2 = H265::DecodeSPSFromSPSNALU(aRhs);
|
||||
if (rv1.isErr() || rv2.isErr()) {
|
||||
return false;
|
||||
}
|
||||
return rv1.unwrap() == rv2.unwrap();
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool H265::CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
|
||||
const mozilla::MediaByteBuffer* aExtraData2) {
|
||||
if (aExtraData1 == aExtraData2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto config1 = HVCCConfig::Parse(aExtraData1);
|
||||
auto config2 = HVCCConfig::Parse(aExtraData2);
|
||||
if (config1.isErr() || config2.isErr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t numSPS = config1.unwrap().NumSPS();
|
||||
if (numSPS == 0 || numSPS != config2.unwrap().NumSPS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only compare if the SPS are the same as the various HEVC decoders can
|
||||
// deal with in-band change of PPS.
|
||||
SPSIterator it1(config1.unwrap());
|
||||
SPSIterator it2(config2.unwrap());
|
||||
while (it1 && it2) {
|
||||
const H265NALU* nalu1 = *it1;
|
||||
const H265NALU* nalu2 = *it2;
|
||||
if (!nalu1 || !nalu2) {
|
||||
return false;
|
||||
}
|
||||
if (!AreTwoSPSIdentical(*nalu1, *nalu2)) {
|
||||
return false;
|
||||
}
|
||||
++it1;
|
||||
++it2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
#undef LOGV
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ enum {
|
|||
class H265NALU final {
|
||||
public:
|
||||
H265NALU(const uint8_t* aData, uint32_t aByteSize);
|
||||
H265NALU() = default;
|
||||
|
||||
// Table 7-1
|
||||
enum NAL_TYPES {
|
||||
|
@ -184,9 +183,6 @@ struct H265VUIParameters {
|
|||
struct H265SPS final {
|
||||
H265SPS() = default;
|
||||
|
||||
bool operator==(const H265SPS& aOther) const;
|
||||
bool operator!=(const H265SPS& aOther) const;
|
||||
|
||||
// Syntax elements.
|
||||
uint8_t sps_video_parameter_set_id = {};
|
||||
uint8_t sps_max_sub_layers_minus1 = {};
|
||||
|
@ -259,7 +255,6 @@ struct HVCCConfig final {
|
|||
const mozilla::MediaByteBuffer* aExtraData);
|
||||
|
||||
uint8_t NALUSize() const { return lengthSizeMinusOne + 1; }
|
||||
uint32_t NumSPS() const;
|
||||
bool HasSPS() const;
|
||||
|
||||
uint8_t configurationVersion;
|
||||
|
@ -297,14 +292,6 @@ class H265 final {
|
|||
static Result<H265SPS, nsresult> DecodeSPSFromSPSNALU(
|
||||
const H265NALU& aSPSNALU);
|
||||
|
||||
// Extract SPS and PPS NALs from aSample by looking into each NALs.
|
||||
static already_AddRefed<mozilla::MediaByteBuffer> ExtractHVCCExtraData(
|
||||
const mozilla::MediaRawData* aSample);
|
||||
|
||||
// Return true if both extradata are equal.
|
||||
static bool CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1,
|
||||
const mozilla::MediaByteBuffer* aExtraData2);
|
||||
|
||||
private:
|
||||
// Return RAW BYTE SEQUENCE PAYLOAD (rbsp) from NAL content.
|
||||
static already_AddRefed<mozilla::MediaByteBuffer> DecodeNALUnit(
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "ByteWriter.h"
|
||||
#include "H264.h"
|
||||
#include "H265.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -55,62 +54,6 @@ static already_AddRefed<MediaRawData> GetAvccSample(uint32_t aSampleSize) {
|
|||
return rawData.forget();
|
||||
}
|
||||
|
||||
static const uint8_t sHvccBytesBuffer[] = {
|
||||
1 /* version */,
|
||||
1 /* general_profile_space/general_tier_flag/general_profile_idc */,
|
||||
0x60 /* general_profile_compatibility_flags 1/4 */,
|
||||
0 /* general_profile_compatibility_flags 2/4 */,
|
||||
0 /* general_profile_compatibility_flags 3/4 */,
|
||||
0 /* general_profile_compatibility_flags 4/4 */,
|
||||
0x90 /* general_constraint_indicator_flags 1/6 */,
|
||||
0 /* general_constraint_indicator_flags 2/6 */,
|
||||
0 /* general_constraint_indicator_flags 3/6 */,
|
||||
0 /* general_constraint_indicator_flags 4/6 */,
|
||||
0 /* general_constraint_indicator_flags 5/6 */,
|
||||
0 /* general_constraint_indicator_flags 6/6 */,
|
||||
0x5A /* general_level_idc */,
|
||||
0 /* min_spatial_segmentation_idc 1/2 */,
|
||||
0 /* min_spatial_segmentation_idc 2/2 */,
|
||||
0 /* parallelismType */,
|
||||
1 /* chroma_format_idc */,
|
||||
0 /* bit_depth_luma_minus8 */,
|
||||
0 /* bit_depth_chroma_minus8 */,
|
||||
0 /* avgFrameRate 1/2 */,
|
||||
0 /* avgFrameRate 2/2 */,
|
||||
0x0F /* constantFrameRate/numTemporalLayers/temporalIdNested/lengthSizeMinusOne
|
||||
*/
|
||||
,
|
||||
2 /* numOfArrays */,
|
||||
/* SPS Array */
|
||||
0x21 /* NAL_unit_type (SPS) */,
|
||||
0 /* numNalus 1/2 */,
|
||||
1 /* numNalus 2/2 */,
|
||||
|
||||
/* SPS */
|
||||
0 /* nalUnitLength 1/2 */,
|
||||
8 /* nalUnitLength 2/2 (header + rsbp) */,
|
||||
0x42 /* NALU header 1/2 */,
|
||||
0 /* NALU header 2/2 */,
|
||||
0 /* rbsp 1/6 */,
|
||||
0 /* rbsp 2/6 */,
|
||||
0 /* rbsp 3/6 */,
|
||||
0 /* rbsp 4/6 */,
|
||||
0 /* rbsp 5/6 */,
|
||||
0 /* rbsp 6/6 */,
|
||||
|
||||
/* PPS Array */
|
||||
0x22 /* NAL_unit_type (PPS) */,
|
||||
0 /* numNalus 1/2 */,
|
||||
1 /* numNalus 2/2 */,
|
||||
|
||||
/* PPS */
|
||||
0 /* nalUnitLength 1/2 */,
|
||||
3 /* nalUnitLength 2/2 (header + rsbp) */,
|
||||
0x44 /* NALU header 1/2 */,
|
||||
0 /* NALU header 2/2 */,
|
||||
0 /* rbsp */,
|
||||
};
|
||||
|
||||
// Create a HVCC sample, which contain fake data, in given size.
|
||||
static already_AddRefed<MediaRawData> GetHVCCSample(uint32_t aSampleSize) {
|
||||
if (aSampleSize < 4) {
|
||||
|
@ -118,7 +61,62 @@ static already_AddRefed<MediaRawData> GetHVCCSample(uint32_t aSampleSize) {
|
|||
EXPECT_FALSE(true) << "Samples should be requested with sane sizes";
|
||||
}
|
||||
auto extradata = MakeRefPtr<mozilla::MediaByteBuffer>();
|
||||
extradata->AppendElements(sHvccBytesBuffer, ArrayLength(sHvccBytesBuffer));
|
||||
uint8_t hvccBytesBuffer[] = {
|
||||
1 /* version */,
|
||||
1 /* general_profile_space/general_tier_flag/general_profile_idc */,
|
||||
0x60 /* general_profile_compatibility_flags 1/4 */,
|
||||
0 /* general_profile_compatibility_flags 2/4 */,
|
||||
0 /* general_profile_compatibility_flags 3/4 */,
|
||||
0 /* general_profile_compatibility_flags 4/4 */,
|
||||
0x90 /* general_constraint_indicator_flags 1/6 */,
|
||||
0 /* general_constraint_indicator_flags 2/6 */,
|
||||
0 /* general_constraint_indicator_flags 3/6 */,
|
||||
0 /* general_constraint_indicator_flags 4/6 */,
|
||||
0 /* general_constraint_indicator_flags 5/6 */,
|
||||
0 /* general_constraint_indicator_flags 6/6 */,
|
||||
0x5A /* general_level_idc */,
|
||||
0 /* min_spatial_segmentation_idc 1/2 */,
|
||||
0 /* min_spatial_segmentation_idc 2/2 */,
|
||||
0 /* parallelismType */,
|
||||
1 /* chroma_format_idc */,
|
||||
0 /* bit_depth_luma_minus8 */,
|
||||
0 /* bit_depth_chroma_minus8 */,
|
||||
0 /* avgFrameRate 1/2 */,
|
||||
0 /* avgFrameRate 2/2 */,
|
||||
0x0F /* constantFrameRate/numTemporalLayers/temporalIdNested/lengthSizeMinusOne
|
||||
*/
|
||||
,
|
||||
2 /* numOfArrays */,
|
||||
/* SPS Array */
|
||||
0x21 /* NAL_unit_type (SPS) */,
|
||||
0 /* numNalus 1/2 */,
|
||||
1 /* numNalus 2/2 */,
|
||||
|
||||
/* SPS */
|
||||
0 /* nalUnitLength 1/2 */,
|
||||
8 /* nalUnitLength 2/2 (header + rsbp) */,
|
||||
0x42 /* NALU header 1/2 */,
|
||||
0 /* NALU header 2/2 */,
|
||||
0 /* rbsp 1/6 */,
|
||||
0 /* rbsp 2/6 */,
|
||||
0 /* rbsp 3/6 */,
|
||||
0 /* rbsp 4/6 */,
|
||||
0 /* rbsp 5/6 */,
|
||||
0 /* rbsp 6/6 */,
|
||||
|
||||
/* PPS Array */
|
||||
0x22 /* NAL_unit_type (PPS) */,
|
||||
0 /* numNalus 1/2 */,
|
||||
1 /* numNalus 2/2 */,
|
||||
|
||||
/* PPS */
|
||||
0 /* nalUnitLength 1/2 */,
|
||||
3 /* nalUnitLength 2/2 (header + rsbp) */,
|
||||
0x44 /* NALU header 1/2 */,
|
||||
0 /* NALU header 2/2 */,
|
||||
0 /* rbsp */,
|
||||
};
|
||||
extradata->AppendElements(hvccBytesBuffer, ArrayLength(hvccBytesBuffer));
|
||||
|
||||
// Write the NAL size.
|
||||
nsTArray<uint8_t> sampleData;
|
||||
|
@ -137,30 +135,6 @@ static already_AddRefed<MediaRawData> GetHVCCSample(uint32_t aSampleSize) {
|
|||
return rawData.forget();
|
||||
}
|
||||
|
||||
// Create a HVCC sample by using given data in given size.
|
||||
static already_AddRefed<MediaRawData> GetHVCCSample(
|
||||
const uint8_t* aData, const uint32_t aDataLength) {
|
||||
if (aDataLength < 4) {
|
||||
// Stop tests asking for insane samples.
|
||||
EXPECT_FALSE(true) << "Samples should be requested with sane sizes";
|
||||
}
|
||||
auto extradata = MakeRefPtr<mozilla::MediaByteBuffer>();
|
||||
extradata->AppendElements(sHvccBytesBuffer, ArrayLength(sHvccBytesBuffer));
|
||||
|
||||
// Write the NAL size.
|
||||
nsTArray<uint8_t> sampleData;
|
||||
ByteWriter<BigEndian> writer(sampleData);
|
||||
EXPECT_TRUE(writer.WriteU32(aDataLength)); // Assume it's a 4 bytes NALU
|
||||
sampleData.AppendElements(aData, aDataLength);
|
||||
|
||||
RefPtr<MediaRawData> rawData =
|
||||
new MediaRawData{sampleData.Elements(), sampleData.Length()};
|
||||
EXPECT_NE(rawData->Data(), nullptr);
|
||||
EXPECT_EQ(rawData->Size(), aDataLength + 4);
|
||||
rawData->mExtraData = extradata;
|
||||
return rawData.forget();
|
||||
}
|
||||
|
||||
// Test that conversion from AVCC to AnnexB works as expected.
|
||||
TEST(AnnexB, AVCCToAnnexBConversion)
|
||||
{
|
||||
|
@ -684,104 +658,4 @@ TEST(H265, HVCCToAnnexB)
|
|||
EXPECT_EQ(pps.mNALU.Length(), 3u);
|
||||
}
|
||||
|
||||
TEST(H265, AnnexBToHVCC)
|
||||
{
|
||||
RefPtr<MediaRawData> rawData{GetHVCCSample(128)};
|
||||
RefPtr<MediaRawData> rawDataClone = rawData->Clone();
|
||||
Result<Ok, nsresult> result =
|
||||
AnnexB::ConvertHVCCSampleToAnnexB(rawDataClone, /* aAddSps */ false);
|
||||
EXPECT_TRUE(result.isOk()) << "HVCC to AnnexB Conversion should succeed";
|
||||
EXPECT_TRUE(AnnexB::IsAnnexB(rawDataClone))
|
||||
<< "The sample should be AnnexB following conversion";
|
||||
|
||||
auto rv = AnnexB::ConvertSampleToHVCC(rawDataClone);
|
||||
EXPECT_TRUE(rv) << "AnnexB to HVCC Conversion should succeed";
|
||||
EXPECT_TRUE(AnnexB::IsHVCC(rawDataClone))
|
||||
<< "The sample should be HVCC following conversion";
|
||||
}
|
||||
|
||||
// This is SPS from 'hevc_white_frame.mp4'
|
||||
static const uint8_t sSps[] = {
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00,
|
||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x00, 0x80,
|
||||
0x30, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x5a, 0x02, 0x00,
|
||||
0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x3c, 0x10};
|
||||
|
||||
TEST(H265, ExtractHVCCExtraData)
|
||||
{
|
||||
RefPtr<MediaRawData> rawData{GetHVCCSample(sSps, ArrayLength(sSps))};
|
||||
RefPtr<MediaByteBuffer> extradata = H265::ExtractHVCCExtraData(rawData);
|
||||
EXPECT_TRUE(extradata);
|
||||
auto rv = HVCCConfig::Parse(extradata);
|
||||
EXPECT_TRUE(rv.isOk());
|
||||
auto hvcc = rv.unwrap();
|
||||
EXPECT_EQ(hvcc.mNALUs.Length(), 1u);
|
||||
EXPECT_EQ(hvcc.mNALUs[0].mNalUnitType, H265NALU::NAL_TYPES::SPS_NUT);
|
||||
EXPECT_EQ(hvcc.mNALUs[0].mNuhLayerId, 0u);
|
||||
EXPECT_EQ(hvcc.mNALUs[0].mNuhTemporalIdPlus1, 1);
|
||||
EXPECT_EQ(hvcc.mNALUs[0].IsSPS(), true);
|
||||
EXPECT_EQ(hvcc.mNALUs[0].mNALU.Length(), 43u);
|
||||
}
|
||||
|
||||
TEST(H265, DecodeSPSFromSPSNALU)
|
||||
{
|
||||
H265NALU nalu{sSps, ArrayLength(sSps)};
|
||||
auto rv = H265::DecodeSPSFromSPSNALU(nalu);
|
||||
EXPECT_TRUE(rv.isOk());
|
||||
auto sps = rv.unwrap();
|
||||
// Examine the value by using HEVCESBrowser.
|
||||
EXPECT_EQ(sps.sps_video_parameter_set_id, 0u);
|
||||
EXPECT_EQ(sps.sps_max_sub_layers_minus1, 0u);
|
||||
EXPECT_EQ(sps.sps_temporal_id_nesting_flag, 1);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_profile_space, 0u);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_tier_flag, false);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_profile_idc, 1u);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_profile_compatibility_flags,
|
||||
0x60000000u);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_progressive_source_flag, true);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_interlaced_source_flag, false);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_non_packed_constraint_flag, false);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_frame_only_constraint_flag, true);
|
||||
EXPECT_EQ(sps.profile_tier_level.general_level_idc, 93u);
|
||||
EXPECT_EQ(sps.sps_seq_parameter_set_id, 0u);
|
||||
EXPECT_EQ(sps.chroma_format_idc, 1u);
|
||||
EXPECT_EQ(sps.separate_colour_plane_flag, false);
|
||||
EXPECT_EQ(sps.pic_width_in_luma_samples, 1024u);
|
||||
EXPECT_EQ(sps.pic_height_in_luma_samples, 768u);
|
||||
EXPECT_EQ(sps.conformance_window_flag, false);
|
||||
EXPECT_EQ(sps.bit_depth_luma_minus8, 0u);
|
||||
EXPECT_EQ(sps.bit_depth_chroma_minus8, 0u);
|
||||
EXPECT_EQ(sps.log2_max_pic_order_cnt_lsb_minus4, 4u);
|
||||
EXPECT_EQ(sps.sps_sub_layer_ordering_info_present_flag, true);
|
||||
EXPECT_EQ(sps.sps_max_dec_pic_buffering_minus1[0], 4u);
|
||||
EXPECT_EQ(sps.sps_max_num_reorder_pics[0], 2u);
|
||||
EXPECT_EQ(sps.sps_max_latency_increase_plus1[0], 5u);
|
||||
EXPECT_EQ(sps.log2_min_luma_coding_block_size_minus3, 0u);
|
||||
EXPECT_EQ(sps.log2_diff_max_min_luma_coding_block_size, 3u);
|
||||
EXPECT_EQ(sps.log2_min_luma_transform_block_size_minus2, 0u);
|
||||
EXPECT_EQ(sps.log2_diff_max_min_luma_transform_block_size, 3u);
|
||||
EXPECT_EQ(sps.max_transform_hierarchy_depth_inter, 0u);
|
||||
EXPECT_EQ(sps.max_transform_hierarchy_depth_inter, 0u);
|
||||
EXPECT_EQ(sps.pcm_enabled_flag, false);
|
||||
EXPECT_EQ(sps.num_short_term_ref_pic_sets, 0u);
|
||||
EXPECT_EQ(sps.sps_temporal_mvp_enabled_flag, true);
|
||||
EXPECT_EQ(sps.strong_intra_smoothing_enabled_flag, true);
|
||||
EXPECT_TRUE(sps.vui_parameters);
|
||||
EXPECT_EQ(sps.vui_parameters->video_full_range_flag, false);
|
||||
|
||||
// Test public methods
|
||||
EXPECT_EQ(sps.BitDepthLuma(), 8u);
|
||||
EXPECT_EQ(sps.BitDepthChroma(), 8u);
|
||||
const auto imgSize = sps.GetImageSize();
|
||||
EXPECT_EQ(imgSize.Width(), 1024);
|
||||
EXPECT_EQ(imgSize.Height(), 768);
|
||||
const auto disSize = sps.GetDisplaySize();
|
||||
EXPECT_EQ(disSize, imgSize);
|
||||
EXPECT_EQ(sps.ColorDepth(), gfx::ColorDepth::COLOR_8);
|
||||
EXPECT_EQ(sps.ColorSpace(), gfx::YUVColorSpace::BT709);
|
||||
EXPECT_EQ(sps.IsFullColorRange(), false);
|
||||
EXPECT_EQ(sps.ColorPrimaries(), 2u);
|
||||
EXPECT_EQ(sps.TransferFunction(), 2u);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -188,56 +188,8 @@ class HEVCChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
}
|
||||
|
||||
MediaResult CheckForChange(MediaRawData* aSample) override {
|
||||
// To be usable we need to convert the sample to 4 bytes NAL size HVCC.
|
||||
if (!AnnexB::ConvertSampleToHVCC(aSample)) {
|
||||
// We need HVCC content to be able to later parse the SPS.
|
||||
// This is a no-op if the data is already HVCC.
|
||||
nsPrintfCString msg("Failed to convert to HVCC");
|
||||
LOG("%s", msg.get());
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY, msg);
|
||||
}
|
||||
|
||||
if (!AnnexB::IsHVCC(aSample)) {
|
||||
nsPrintfCString msg("Invalid HVCC content");
|
||||
LOG("%s", msg.get());
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, msg);
|
||||
}
|
||||
|
||||
RefPtr<MediaByteBuffer> extraData =
|
||||
aSample->mKeyframe || !mGotSPS ? H265::ExtractHVCCExtraData(aSample)
|
||||
: nullptr;
|
||||
// Sample doesn't contain any SPS and we already have SPS, do nothing.
|
||||
auto curConfig = HVCCConfig::Parse(mCurrentConfig.mExtraData);
|
||||
if ((!extraData || extraData->IsEmpty()) && curConfig.unwrap().HasSPS()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
auto newConfig = HVCCConfig::Parse(extraData);
|
||||
// Ignore a corrupted extradata.
|
||||
if (newConfig.isErr()) {
|
||||
LOG("Ignore corrupted extradata");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!newConfig.unwrap().HasSPS() && !curConfig.unwrap().HasSPS()) {
|
||||
// We don't have inband data and the original config didn't contain a SPS.
|
||||
// We can't decode this content.
|
||||
LOG("No sps found, waiting for initialization");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
mGotSPS = true;
|
||||
if (H265::CompareExtraData(extraData, mCurrentConfig.mExtraData)) {
|
||||
return NS_OK;
|
||||
}
|
||||
UpdateConfigFromExtraData(extraData);
|
||||
|
||||
nsPrintfCString msg(
|
||||
"HEVCChangeMonitor::CheckForChange has detected a change in the stream "
|
||||
"and will request a new decoder");
|
||||
LOG("%s", msg.get());
|
||||
PROFILER_MARKER_TEXT("HEVC Stream Change", MEDIA_PLAYBACK, {}, msg);
|
||||
return NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
|
||||
// TODO : detect config change in bug 1852333.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const TrackInfo& Config() const override { return mCurrentConfig; }
|
||||
|
@ -281,14 +233,12 @@ class HEVCChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
|
|||
? gfx::ColorRange::FULL
|
||||
: gfx::ColorRange::LIMITED;
|
||||
}
|
||||
MOZ_ASSERT(HVCCConfig::Parse(aExtraData).isOk());
|
||||
mCurrentConfig.mExtraData = aExtraData;
|
||||
mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, mStreamID++);
|
||||
}
|
||||
|
||||
VideoInfo mCurrentConfig;
|
||||
uint32_t mStreamID = 0;
|
||||
bool mGotSPS = false;
|
||||
RefPtr<TrackInfoSharedPtr> mTrackInfo;
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Cache-Control: no-store
|
|
@ -540,18 +540,6 @@ support-files = [
|
|||
"gzipped_mp4.sjs",
|
||||
"hevc_white_frame.mp4",
|
||||
"hevc_white_frame.mp4^headers^",
|
||||
# This HEVC file is created by following steps.
|
||||
# 1. generate two videos
|
||||
# - a white frame video (0-2 seconds) at 1024x768 resolution
|
||||
# ffmpeg -t 2 -f lavfi -i color=size=1024x768:rate=30:color=white -c:v libx265 -pix_fmt yuv420p -strict experimental -metadata:s:v:0 rotate=0 -metadata:s:v:0 width=1024 -metadata:s:v:0 height=768 -y white_frames.mp4
|
||||
# - a red frame video (2-4 seconds) at 3480x2160 resolution with inband SPS
|
||||
# ffmpeg -t 2 -f lavfi -i color=size=3480x2160:rate=30:color=red -c:v libx265 -x265-params "sps-id=1" -strict experimental -metadata:s:v:0 rotate=0 -metadata:s:v:0 width=3480 -metadata:s:v:0 height=2160 -y red_frames.mp4
|
||||
# 2. covert them to annexB
|
||||
# ffmpeg -i XXX.mp4 -codec copy -bsf:v h264_mp4toannexb XXX.ts
|
||||
# 3. combine them together
|
||||
# ffmpeg -i "concat:white_annexb.ts|red_annexb.ts" -c copy -strict -2 -y hevc_white_red_frames.mp4
|
||||
"hevc_white_red_frames.mp4",
|
||||
"hevc_white_red_frames.mp4^headers^",
|
||||
"huge-id3.mp3",
|
||||
"huge-id3.mp3^headers^",
|
||||
"id3tags.mp3",
|
||||
|
|
|
@ -29,30 +29,6 @@ add_task(async function testHEVCPlayback() {
|
|||
ok(await video.play().then(_=>true, _=>false), "video started playing");
|
||||
});
|
||||
|
||||
|
||||
// This test video contains an inband SPS that is different from the SPS in the
|
||||
// extradata, so we would recreate a new decoder.
|
||||
add_task(async function testHEVCInbandConfigChange() {
|
||||
let video = document.createElement('video');
|
||||
const isHEVCSupported = video.canPlayType('video/mp4; codecs="hev1.1.6.L93.B0"');
|
||||
const os = SpecialPowers.Services.appinfo.OS;
|
||||
info(`check if HEVC is supported on ${os}, rv=${isHEVCSupported}`);
|
||||
if (os != "WINNT") {
|
||||
ok(isHEVCSupported == "", "HEVC is not supported on current platform");
|
||||
return;
|
||||
}
|
||||
ok(isHEVCSupported != "", `HEVC is supported`);
|
||||
|
||||
info(`try to play HEVC video`);
|
||||
video.src = "hevc_white_red_frames.mp4";
|
||||
video.playbackRate = 2.0; // speed up the test.
|
||||
video.controls = true;
|
||||
document.body.appendChild(video);
|
||||
ok(await video.play().then(_=>true, _=>false), "video started playing");
|
||||
ok(await new Promise(r => video.onended = r).then(_=>true, _=>false),
|
||||
"video played to end");
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
Загрузка…
Ссылка в новой задаче