1 /*
2 mkvextract -- extract tracks from Matroska files into other files
3
4 Distributed under the GPL v2
5 see the file COPYING for details
6 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
7
8 extracts tracks from Matroska files into other files
9
10 Written by Moritz Bunkus <moritz@bunkus.org>.
11 */
12
13 #include "common/common_pch.h"
14
15 #include "common/av1.h"
16 #include "common/codec.h"
17 #include "common/ebml.h"
18 #include "common/endian.h"
19 #include "common/frame_timing.h"
20 #include "common/math.h"
21 #include "extract/xtr_ivf.h"
22
23 namespace {
24 unsigned char const s_av1_temporal_delimiter_obu[2] = {
25 0x12, // type = OBU_TEMPORAL_DELIMITER, extension not present, OBU size field present
26 0x00, // OBU size
27 };
28 }
29
xtr_ivf_c(const std::string & codec_id,int64_t tid,track_spec_t & tspec)30 xtr_ivf_c::xtr_ivf_c(const std::string &codec_id,
31 int64_t tid,
32 track_spec_t &tspec)
33 : xtr_base_c(codec_id, tid, tspec)
34 , m_is_av1{codec_id == MKV_V_AV1}
35 {
36 }
37
38 void
create_file(xtr_base_c * master,libmatroska::KaxTrackEntry & track)39 xtr_ivf_c::create_file(xtr_base_c *master,
40 libmatroska::KaxTrackEntry &track) {
41 xtr_base_c::create_file(master, track);
42
43 auto default_duration = FindChildValue<libmatroska::KaxTrackDefaultDuration>(track, 1'000'000'000 / 25); // Default to 25 FPS if unknown.
44 auto rate = mtx::frame_timing::determine_frame_rate(default_duration);
45 if (!rate)
46 rate = mtx::rational(1'000'000'000ll, default_duration);
47 rate = mtx::math::clamp_values_to(rate, std::numeric_limits<uint16_t>::max());
48
49 m_frame_rate_num = static_cast<uint64_t>(boost::multiprecision::numerator(rate));
50 m_frame_rate_den = static_cast<uint64_t>(boost::multiprecision::denominator(rate));
51
52 mxdebug_if(m_debug, fmt::format("frame rate determination: default duration {0} numerator {1} denominator {2}\n", default_duration, m_frame_rate_num, m_frame_rate_den));
53
54 if (master)
55 mxerror(fmt::format(Y("Cannot write track {0} with the CodecID '{1}' to the file '{2}' because "
56 "track {3} with the CodecID '{4}' is already being written to the same file.\n"),
57 m_tid, m_codec_id, m_file_name, master->m_tid, master->m_codec_id));
58
59 auto fourcc = m_is_av1 ? "AV01"
60 : m_codec_id == MKV_V_VP8 ? "VP80"
61 : "VP90";
62
63 memcpy(m_file_header.file_magic, "DKIF", 4);
64 memcpy(m_file_header.fourcc, fourcc, 4);
65
66 put_uint16_le(&m_file_header.header_size, sizeof(m_file_header));
67 put_uint16_le(&m_file_header.width, kt_get_v_pixel_width(track));
68 put_uint16_le(&m_file_header.height, kt_get_v_pixel_height(track));
69 put_uint16_le(&m_file_header.frame_rate_num, m_frame_rate_num);
70 put_uint16_le(&m_file_header.frame_rate_den, m_frame_rate_den);
71
72 m_out->write(&m_file_header, sizeof(m_file_header));
73 }
74
75 void
handle_frame(xtr_frame_t & f)76 xtr_ivf_c::handle_frame(xtr_frame_t &f) {
77 uint64_t frame_number = f.timestamp * m_frame_rate_num / m_frame_rate_den / 1'000'000'000ull;
78
79 mxdebug_if(m_debug,
80 fmt::format("handle frame: timestamp {0} num {1} den {2} frame_number {3} calculated back {4}\n",
81 f.timestamp, m_frame_rate_num, m_frame_rate_den, frame_number,
82 frame_number * 1'000'000'000ull * m_frame_rate_den / m_frame_rate_num));
83
84 av1_prepend_temporal_delimiter_obu_if_needed(*f.frame);
85
86 ivf::frame_header_t frame_header;
87 put_uint32_le(&frame_header.frame_size, f.frame->get_size());
88 put_uint32_le(&frame_header.timestamp, frame_number);
89
90 m_out->write(&frame_header, sizeof(frame_header));
91 m_out->write(f.frame->get_buffer(), f.frame->get_size());
92
93 ++m_frame_count;
94 }
95
96 void
finish_file()97 xtr_ivf_c::finish_file() {
98 put_uint32_le(&m_file_header.frame_count, m_frame_count);
99
100 m_out->setFilePointer(0);
101 m_out->write(&m_file_header, sizeof(m_file_header));
102 }
103
104 void
av1_prepend_temporal_delimiter_obu_if_needed(memory_c & frame)105 xtr_ivf_c::av1_prepend_temporal_delimiter_obu_if_needed(memory_c &frame) {
106 if (!m_is_av1 || !frame.get_size())
107 return;
108
109 auto type = (frame.get_buffer()[0] & 0x74) >> 3;
110
111 if (type != mtx::av1::OBU_TEMPORAL_DELIMITER)
112 frame.prepend(s_av1_temporal_delimiter_obu, 2);
113 }
114