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