AVC/HEVC ES packetizers: move shared code to common base class

This commit is contained in:
Moritz Bunkus 2021-08-17 23:02:15 +02:00
parent 9da7cb3080
commit b9b94d0149
No known key found for this signature in database
GPG Key ID: 74AF00ADF2E32C85
14 changed files with 156 additions and 257 deletions

View File

@ -608,11 +608,22 @@ es_parser_c::create_nalu_with_size(const memory_cptr &src,
}
memory_cptr
es_parser_c::get_avcc()
es_parser_c::get_configuration_record()
const {
return avcc_c{static_cast<unsigned int>(m_nalu_size_length), m_sps_list, m_pps_list}.pack();
}
void
es_parser_c::set_configuration_record(memory_cptr const &bytes) {
auto avcc = avcc_c::unpack(bytes);
for (auto const &nalu : avcc.m_sps_list)
handle_sps_nalu(nalu); // TODO: , extra_data_position_e::dont_store);
for (auto const &nalu : avcc.m_pps_list)
handle_pps_nalu(nalu); // TODO: , extra_data_position_e::dont_store);
}
void
es_parser_c::dump_info()
const {

View File

@ -58,7 +58,8 @@ public:
virtual void flush() override;
virtual void clear() override;
memory_cptr get_avcc() const;
virtual void set_configuration_record(memory_cptr const &bytes) override;
virtual memory_cptr get_configuration_record() const override;
virtual int get_width() const override {
assert(!m_sps_info_list.empty());

View File

@ -123,6 +123,8 @@ public:
virtual void handle_nalu(memory_cptr const &nalu, uint64_t nalu_pos) = 0;
virtual void set_configuration_record(memory_cptr const &bytes) = 0;
virtual memory_cptr get_configuration_record() const = 0;
virtual int get_width() const = 0;
virtual int get_height() const = 0;

View File

@ -694,14 +694,14 @@ es_parser_c::calculate_frame_order() {
}
memory_cptr
es_parser_c::get_hevcc()
es_parser_c::get_configuration_record()
const {
return hevcc_c{static_cast<unsigned int>(m_nalu_size_length), m_vps_list, m_sps_list, m_pps_list, m_user_data, m_codec_private}.pack();
}
void
es_parser_c::set_hevcc(memory_cptr const &hevcc_bytes) {
auto hevcc = hevcc_c::unpack(hevcc_bytes);
es_parser_c::set_configuration_record(memory_cptr const &bytes) {
auto hevcc = hevcc_c::unpack(bytes);
for (auto const &nalu : hevcc.m_vps_list)
handle_vps_nalu(nalu, extra_data_position_e::dont_store);

View File

@ -53,8 +53,8 @@ public:
virtual void flush() override;
virtual void clear() override;
void set_hevcc(memory_cptr const &hevcc_bytes);
memory_cptr get_hevcc() const;
virtual void set_configuration_record(memory_cptr const &bytes) override;
virtual memory_cptr get_configuration_record() const override;
virtual int get_width() const override {
assert(!m_sps_info_list.empty());

View File

@ -39,7 +39,7 @@ xtr_hevc_c::create_file(xtr_base_c *master,
mxerror(fmt::format(Y("Track {0} CodecPrivate is too small.\n"), m_tid));
m_parser.normalize_parameter_sets(m_normalize_parameter_sets);
m_parser.set_hevcc(m_decoded_codec_private);
m_parser.set_configuration_record(m_decoded_codec_private);
m_nal_size_size = 1 + (m_decoded_codec_private->get_buffer()[21] & 3);
}

View File

@ -3395,7 +3395,7 @@ qtmp4_demuxer_c::check_for_hevc_video_annex_b_bitstream() {
}
priv.clear();
priv.emplace_back(parser.get_hevcc());
priv.emplace_back(parser.get_configuration_record());
break;
}
@ -3527,7 +3527,7 @@ qtmp4_demuxer_c::derive_track_params_from_avc_bitstream() {
parser.flush();
if (parser.headers_parsed())
priv.emplace_back(parser.get_avcc());
priv.emplace_back(parser.get_configuration_record());
mxdebug_if(m_debug_headers, fmt::format("derive_track_params_from_avc_bitstream: avcC derived? size {0} bytes\n", !priv.empty() && priv[0] ? priv[0]->get_size() : 0));

View File

@ -38,28 +38,8 @@ avc_es_video_packetizer_c(generic_reader_c *p_reader,
}
void
avc_es_video_packetizer_c::process_impl(packet_cptr const &packet) {
try {
if (packet->has_timestamp())
m_parser.add_timestamp(packet->timestamp);
m_parser.add_bytes(packet->data->get_buffer(), packet->data->get_size());
flush_frames();
} catch (mtx::exception &error) {
mxerror_tid(m_ti.m_fname, m_ti.m_id,
fmt::format(Y("mkvmerge encountered broken or unparsable data in this AVC/H.264 video track. "
"Either your file is damaged (which mkvmerge cannot cope with yet) or this is a bug in mkvmerge itself. "
"The error message was:\n{0}\n"), error.error()));
}
}
void
avc_es_video_packetizer_c::handle_delayed_headers() {
if (0 < m_parser.get_num_skipped_frames())
mxwarn_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("This AVC/H.264 track does not start with a key frame. The first {0} frames have been skipped.\n"), m_parser.get_num_skipped_frames()));
set_codec_private(m_parser.get_avcc());
avc_es_video_packetizer_c::check_if_default_duration_available()
const {
if ( !m_reader->is_providing_timestamps()
&& !m_timestamp_factory
&& !m_parser.is_default_duration_forced()
@ -69,88 +49,6 @@ avc_es_video_packetizer_c::handle_delayed_headers() {
mxwarn_tid(m_ti.m_fname, m_ti.m_id, Y("This AVC/H.264 track's timing information indicates that it uses a variable frame rate. "
"However, no default duration nor an external timestamp file has been provided for it, nor does the source container provide timestamps. "
"The resulting timestamps may not be useful.\n"));
handle_aspect_ratio();
handle_actual_default_duration();
rerender_track_headers();
}
void
avc_es_video_packetizer_c::handle_aspect_ratio() {
mxdebug_if(m_debug_aspect_ratio, fmt::format("already set? {0} has par been found? {1}\n", display_dimensions_or_aspect_ratio_set(), m_parser.has_par_been_found()));
if (display_dimensions_or_aspect_ratio_set() || !m_parser.has_par_been_found())
return;
auto dimensions = m_parser.get_display_dimensions(m_hvideo_pixel_width, m_hvideo_pixel_height);
set_video_display_dimensions(dimensions.first, dimensions.second, generic_packetizer_c::ddu_pixels, OPTION_SOURCE_BITSTREAM);
mxinfo_tid(m_ti.m_fname, m_ti.m_id,
fmt::format(Y("Extracted the aspect ratio information from the MPEG-4 layer 10 (AVC) video data "
"and set the display dimensions to {0}/{1}.\n"), m_ti.m_display_width, m_ti.m_display_height));
mxdebug_if(m_debug_aspect_ratio,
fmt::format("PAR {0} pixel_width/hgith {1}/{2} display_width/height {3}/{4}\n",
m_parser.get_par(), m_hvideo_pixel_width, m_hvideo_pixel_height, m_ti.m_display_width, m_ti.m_display_height));
}
void
avc_es_video_packetizer_c::handle_actual_default_duration() {
int64_t actual_default_duration = m_parser.get_most_often_used_duration();
mxdebug_if(m_debug_timestamps, fmt::format("Most often used duration: {0} forced? {1} current default duration: {2}\n", actual_default_duration, m_default_duration_forced, m_htrack_default_duration));
if ( !m_default_duration_forced
&& (0 < actual_default_duration)
&& (m_htrack_default_duration != actual_default_duration))
set_track_default_duration(actual_default_duration);
else if ( m_default_duration_forced
&& (0 < m_default_duration_for_interlaced_content)
&& (std::abs(actual_default_duration - m_default_duration_for_interlaced_content) <= 20000)) {
m_default_duration_forced = false;
set_track_default_duration(m_default_duration_for_interlaced_content);
}
}
void
avc_es_video_packetizer_c::flush_frames() {
while (m_parser.frame_available()) {
if (m_first_frame) {
handle_delayed_headers();
m_first_frame = false;
}
auto frame = m_parser.get_frame();
auto duration = frame.m_end > frame.m_start ? frame.m_end - frame.m_start : m_htrack_default_duration;
auto packet = std::make_shared<packet_t>(frame.m_data, frame.m_start, duration,
frame.is_i_frame() ? -1 : frame.m_start + frame.m_ref1,
!frame.is_b_frame() ? -1 : frame.m_start + frame.m_ref2);
packet->key_flag = frame.is_key_frame();
packet->discardable_flag = frame.is_discardable();
add_packet(packet);
}
}
void
avc_es_video_packetizer_c::connect(generic_packetizer_c *src,
int64_t p_append_timestamp_offset) {
generic_packetizer_c::connect(src, p_append_timestamp_offset);
if (2 != m_connected_to)
return;
avc_es_video_packetizer_c *real_src = dynamic_cast<avc_es_video_packetizer_c *>(src);
assert(real_src);
m_htrack_default_duration = real_src->m_htrack_default_duration;
m_default_duration_forced = real_src->m_default_duration_forced;
if (m_default_duration_forced && (-1 != m_htrack_default_duration)) {
m_default_duration_for_interlaced_content = m_htrack_default_duration / 2;
m_parser.force_default_duration(m_default_duration_for_interlaced_content);
}
}
connection_result_e

View File

@ -25,18 +25,12 @@ protected:
public:
avc_es_video_packetizer_c(generic_reader_c *p_reader, track_info_c &p_ti);
virtual void flush_frames() override;
virtual translatable_string_c get_format_name() const {
virtual translatable_string_c get_format_name() const override {
return YT("AVC/H.264 (unframed)");
};
virtual void connect(generic_packetizer_c *src, int64_t p_append_timestamp_offset = -1);
virtual connection_result_e can_connect_to(generic_packetizer_c *src, std::string &error_message);
virtual connection_result_e can_connect_to(generic_packetizer_c *src, std::string &error_message) override;
protected:
virtual void process_impl(packet_cptr const &packet) override;
virtual void handle_delayed_headers();
virtual void handle_aspect_ratio();
virtual void handle_actual_default_duration();
virtual void check_if_default_duration_available() const override;
};

View File

@ -83,24 +83,124 @@ avc_hevc_es_video_packetizer_c::add_extra_data(memory_cptr const &data) {
m_parser_base->add_bytes(data->get_buffer(), data->get_size());
}
void
avc_hevc_es_video_packetizer_c::process_impl(packet_cptr const &packet) {
try {
if (packet->has_timestamp())
m_parser_base->add_timestamp(packet->timestamp);
m_parser_base->add_bytes(packet->data->get_buffer(), packet->data->get_size());
flush_frames();
} catch (mtx::exception &error) {
mxerror_tid(m_ti.m_fname, m_ti.m_id,
fmt::format("{0} {1}\n{2}\n",
Y("mkvmerge encountered broken or unparsable data in this video track."),
Y("The error message was:"),
error.error()));
}
}
void
avc_hevc_es_video_packetizer_c::flush_impl() {
m_parser_base->flush();
flush_frames();
}
// void
// hevc_es_video_packetizer_c::flush_frames() {
// while (m_parser_base->frame_available()) {
// if (m_first_frame) {
// handle_delayed_headers();
// m_first_frame = false;
// }
void
avc_hevc_es_video_packetizer_c::flush_frames() {
while (m_parser_base->frame_available()) {
if (m_first_frame) {
handle_delayed_headers();
m_first_frame = false;
}
// auto frame = m_parser_base->get_frame();
// add_packet(std::make_shared<packet_t>(frame.m_data, frame.m_start,
// frame.m_end > frame.m_start ? frame.m_end - frame.m_start : m_htrack_default_duration,
// frame.is_key_frame() ? -1 : frame.m_start + frame.m_ref1,
// !frame.is_b_frame() ? -1 : frame.m_start + frame.m_ref2));
// }
// }
auto frame = m_parser_base->get_frame();
auto duration = frame.m_end > frame.m_start ? frame.m_end - frame.m_start : m_htrack_default_duration;
auto packet = std::make_shared<packet_t>(frame.m_data, frame.m_start, duration,
frame.is_key_frame() ? -1 : frame.m_start + frame.m_ref1,
!frame.is_b_frame() ? -1 : frame.m_start + frame.m_ref2);
packet->key_flag = frame.is_key_frame();
packet->discardable_flag = frame.is_discardable();
add_packet(packet);
}
}
void
avc_hevc_es_video_packetizer_c::check_if_default_duration_available()
const {
// No default implementation, but not required either.
}
void
avc_hevc_es_video_packetizer_c::handle_delayed_headers() {
if (0 < m_parser_base->get_num_skipped_frames())
mxwarn_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("This AVC/H.264 track does not start with a key frame. The first {0} frames have been skipped.\n"), m_parser_base->get_num_skipped_frames()));
set_codec_private(m_parser_base->get_configuration_record());
check_if_default_duration_available();
handle_aspect_ratio();
handle_actual_default_duration();
rerender_track_headers();
}
void
avc_hevc_es_video_packetizer_c::handle_aspect_ratio() {
mxdebug_if(m_debug_aspect_ratio, fmt::format("already set? {0} has par been found? {1}\n", display_dimensions_or_aspect_ratio_set(), m_parser_base->has_par_been_found()));
if (display_dimensions_or_aspect_ratio_set() || !m_parser_base->has_par_been_found())
return;
auto dimensions = m_parser_base->get_display_dimensions(m_hvideo_pixel_width, m_hvideo_pixel_height);
set_video_display_dimensions(dimensions.first, dimensions.second, generic_packetizer_c::ddu_pixels, OPTION_SOURCE_BITSTREAM);
mxinfo_tid(m_ti.m_fname, m_ti.m_id,
fmt::format(Y("Extracted the aspect ratio information from the video bitstream and set the display dimensions to {0}/{1}.\n"),
m_ti.m_display_width, m_ti.m_display_height));
mxdebug_if(m_debug_aspect_ratio,
fmt::format("PAR {0} pixel_width/hgith {1}/{2} display_width/height {3}/{4}\n",
m_parser_base->get_par(), m_hvideo_pixel_width, m_hvideo_pixel_height, m_ti.m_display_width, m_ti.m_display_height));
}
void
avc_hevc_es_video_packetizer_c::handle_actual_default_duration() {
int64_t actual_default_duration = m_parser_base->get_most_often_used_duration();
mxdebug_if(m_debug_timestamps, fmt::format("Most often used duration: {0} forced? {1} current default duration: {2}\n", actual_default_duration, m_default_duration_forced, m_htrack_default_duration));
if ( !m_default_duration_forced
&& (0 < actual_default_duration)
&& (m_htrack_default_duration != actual_default_duration))
set_track_default_duration(actual_default_duration);
else if ( m_default_duration_forced
&& (0 < m_default_duration_for_interlaced_content)
&& (std::abs(actual_default_duration - m_default_duration_for_interlaced_content) <= 20000)) {
m_default_duration_forced = false;
set_track_default_duration(m_default_duration_for_interlaced_content);
}
}
void
avc_hevc_es_video_packetizer_c::connect(generic_packetizer_c *src,
int64_t p_append_timestamp_offset) {
generic_packetizer_c::connect(src, p_append_timestamp_offset);
if (2 != m_connected_to)
return;
auto real_src = dynamic_cast<avc_hevc_es_video_packetizer_c *>(src);
assert(real_src);
m_htrack_default_duration = real_src->m_htrack_default_duration;
m_default_duration_forced = real_src->m_default_duration_forced;
if (m_default_duration_forced && (-1 != m_htrack_default_duration)) {
m_default_duration_for_interlaced_content = m_htrack_default_duration / 2;
m_parser_base->force_default_duration(m_default_duration_for_interlaced_content);
}
}

View File

@ -35,8 +35,17 @@ public:
virtual void set_container_default_field_duration(int64_t default_duration);
virtual unsigned int get_nalu_size_length() const;
virtual void flush_frames() = 0;
virtual void connect(generic_packetizer_c *src, int64_t p_append_timestamp_offset) override;
protected:
virtual void process_impl(packet_cptr const &packet) override;
virtual void flush_impl() override;
virtual void flush_frames();
virtual void check_if_default_duration_available() const;
virtual void handle_delayed_headers();
virtual void handle_aspect_ratio();
virtual void handle_actual_default_duration();
};

View File

@ -49,7 +49,7 @@ hevc_video_packetizer_c(generic_reader_c *p_reader,
set_codec_private(m_ti.m_private_data);
p.parser->normalize_parameter_sets(!mtx::hacks::is_engaged(mtx::hacks::DONT_NORMALIZE_PARAMETER_SETS));
p.parser->set_hevcc(m_hcodec_private);
p.parser->set_configuration_record(m_hcodec_private);
}
void

View File

@ -34,113 +34,6 @@ hevc_es_video_packetizer_c::hevc_es_video_packetizer_c(generic_reader_c *p_reade
m_parser.normalize_parameter_sets(!mtx::hacks::is_engaged(mtx::hacks::DONT_NORMALIZE_PARAMETER_SETS));
}
void
hevc_es_video_packetizer_c::process_impl(packet_cptr const &packet) {
try {
if (packet->has_timestamp())
m_parser.add_timestamp(packet->timestamp);
m_parser.add_bytes(packet->data->get_buffer(), packet->data->get_size());
flush_frames();
} catch (mtx::exception &error) {
mxerror_tid(m_ti.m_fname, m_ti.m_id,
fmt::format(Y("mkvmerge encountered broken or unparsable data in this HEVC video track. "
"Either your file is damaged (which mkvmerge cannot cope with yet) or this is a bug in mkvmerge itself. "
"The error message was:\n{0}\n"), error.error()));
}
}
void
hevc_es_video_packetizer_c::handle_delayed_headers() {
if (0 < m_parser.get_num_skipped_frames())
mxwarn_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("This HEVC track does not start with a key frame. The first {0} frames have been skipped.\n"), m_parser.get_num_skipped_frames()));
set_codec_private(m_parser.get_hevcc());
handle_aspect_ratio();
handle_actual_default_duration();
rerender_track_headers();
}
void
hevc_es_video_packetizer_c::handle_aspect_ratio() {
mxdebug_if(m_debug_aspect_ratio, fmt::format("already set? {0} has par been found? {1}\n", display_dimensions_or_aspect_ratio_set(), m_parser.has_par_been_found()));
if (display_dimensions_or_aspect_ratio_set() || !m_parser.has_par_been_found())
return;
auto dimensions = m_parser.get_display_dimensions(m_hvideo_pixel_width, m_hvideo_pixel_height);
set_video_display_dimensions(dimensions.first, dimensions.second, generic_packetizer_c::ddu_pixels, OPTION_SOURCE_BITSTREAM);
mxinfo_tid(m_ti.m_fname, m_ti.m_id,
fmt::format(Y("Extracted the aspect ratio information from the HEVC video data "
"and set the display dimensions to {0}/{1}.\n"), m_ti.m_display_width, m_ti.m_display_height));
mxdebug_if(m_debug_aspect_ratio,
fmt::format("PAR {0} pixel_width/hgith {1}/{2} display_width/height {3}/{4}\n",
m_parser.get_par(), m_hvideo_pixel_width, m_hvideo_pixel_height, m_ti.m_display_width, m_ti.m_display_height));
}
void
hevc_es_video_packetizer_c::handle_actual_default_duration() {
int64_t actual_default_duration = m_parser.get_most_often_used_duration();
mxdebug_if(m_debug_timestamps, fmt::format("Most often used duration: {0} forced? {1} current default duration: {2}\n", actual_default_duration, m_default_duration_forced, m_htrack_default_duration));
if ( !m_default_duration_forced
&& (0 < actual_default_duration)
&& (m_htrack_default_duration != actual_default_duration))
set_track_default_duration(actual_default_duration);
else if ( m_default_duration_forced
&& (0 < m_default_duration_for_interlaced_content)
&& (std::abs(actual_default_duration - m_default_duration_for_interlaced_content) <= 20000)) {
m_default_duration_forced = false;
set_track_default_duration(m_default_duration_for_interlaced_content);
}
}
void
hevc_es_video_packetizer_c::flush_frames() {
while (m_parser_base->frame_available()) {
if (m_first_frame) {
handle_delayed_headers();
m_first_frame = false;
}
auto frame = m_parser_base->get_frame();
auto duration = frame.m_end > frame.m_start ? frame.m_end - frame.m_start : m_htrack_default_duration;
auto packet = std::make_shared<packet_t>(frame.m_data, frame.m_start, duration,
frame.is_key_frame() ? -1 : frame.m_start + frame.m_ref1,
!frame.is_b_frame() ? -1 : frame.m_start + frame.m_ref2);
packet->key_flag = frame.is_key_frame();
packet->discardable_flag = frame.is_discardable();
add_packet(packet);
}
}
void
hevc_es_video_packetizer_c::connect(generic_packetizer_c *src,
int64_t p_append_timestamp_offset) {
generic_packetizer_c::connect(src, p_append_timestamp_offset);
if (2 != m_connected_to)
return;
auto *real_src = dynamic_cast<hevc_es_video_packetizer_c *>(src);
assert(real_src);
m_htrack_default_duration = real_src->m_htrack_default_duration;
m_default_duration_forced = real_src->m_default_duration_forced;
if (m_default_duration_forced && (-1 != m_htrack_default_duration)) {
m_default_duration_for_interlaced_content = m_htrack_default_duration / 2;
m_parser.force_default_duration(m_default_duration_for_interlaced_content);
}
}
connection_result_e
hevc_es_video_packetizer_c::can_connect_to(generic_packetizer_c *src,
[[maybe_unused]] std::string &error_message) {

View File

@ -24,18 +24,9 @@ protected:
public:
hevc_es_video_packetizer_c(generic_reader_c *p_reader, track_info_c &p_ti);
virtual void flush_frames() override;
virtual translatable_string_c get_format_name() const {
virtual translatable_string_c get_format_name() const override {
return YT("HEVC/H.265 (unframed)");
};
virtual void connect(generic_packetizer_c *src, int64_t p_append_timestamp_offset = -1);
virtual connection_result_e can_connect_to(generic_packetizer_c *src, std::string &error_message);
protected:
virtual void process_impl(packet_cptr const &packet) override;
virtual void handle_delayed_headers();
virtual void handle_aspect_ratio();
virtual void handle_actual_default_duration();
virtual connection_result_e can_connect_to(generic_packetizer_c *src, std::string &error_message) override;
};