1 /*
2    mkvmerge -- utility for splicing together matroska files
3    from component media subtypes
4 
5    Distributed under the GPL v2
6    see the file COPYING for details
7    or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8 
9    IVF demultiplexer module
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include <algorithm>
17 
18 #include "common/endian.h"
19 #include "common/ivf.h"
20 #include "common/id_info.h"
21 #include "input/r_ivf.h"
22 #include "output/p_av1.h"
23 #include "output/p_vpx.h"
24 #include "merge/input_x.h"
25 #include "merge/file_status.h"
26 
27 bool
probe_file()28 ivf_reader_c::probe_file() {
29   if (m_in->read(&m_header, sizeof(ivf::file_header_t)) < sizeof(ivf::file_header_t))
30     return false;
31 
32   if (memcmp(m_header.file_magic, "DKIF", 4))
33     return false;
34 
35   m_codec = m_header.get_codec();
36 
37   return m_codec.is(codec_c::type_e::V_AV1) || m_codec.is(codec_c::type_e::V_VP8) || m_codec.is(codec_c::type_e::V_VP9);
38 }
39 
40 void
read_headers()41 ivf_reader_c::read_headers() {
42   m_in->setFilePointer(sizeof(ivf::file_header_t));
43 
44   m_width          = get_uint16_le(&m_header.width);
45   m_height         = get_uint16_le(&m_header.height);
46   m_frame_rate_num = get_uint32_le(&m_header.frame_rate_num);
47   m_frame_rate_den = get_uint32_le(&m_header.frame_rate_den);
48   m_ok             = m_width && m_height && m_frame_rate_num && m_frame_rate_den;
49 
50   show_demuxer_info();
51 }
52 
53 void
add_available_track_ids()54 ivf_reader_c::add_available_track_ids() {
55   if (m_ok)
56     add_available_track_id(0);
57 }
58 
59 void
create_packetizer(int64_t)60 ivf_reader_c::create_packetizer(int64_t) {
61   if (!demuxing_requested('v', 0) || !m_reader_packetizers.empty() || !m_ok)
62     return;
63 
64   if (m_codec.is(codec_c::type_e::V_AV1))
65     create_av1_packetizer();
66 
67   else if (m_codec.is(codec_c::type_e::V_VP8) || m_codec.is(codec_c::type_e::V_VP9))
68     create_vpx_packetizer();
69 
70   auto &packetizer = ptzr(0);
71 
72   packetizer.set_video_pixel_width(m_width);
73   packetizer.set_video_pixel_height(m_height);
74 
75   auto default_duration = 1000000000ll * m_frame_rate_den / m_frame_rate_num;
76   if (default_duration >= 1000000)
77     packetizer.set_track_default_duration(default_duration);
78 
79   show_packetizer_info(0, packetizer);
80 }
81 
82 void
create_av1_packetizer()83 ivf_reader_c::create_av1_packetizer() {
84   auto av1_ptzr = new av1_video_packetizer_c(this, m_ti);
85   av1_ptzr->set_is_unframed();
86 
87   add_packetizer(av1_ptzr);
88 }
89 
90 void
create_vpx_packetizer()91 ivf_reader_c::create_vpx_packetizer() {
92   add_packetizer(new vpx_video_packetizer_c(this, m_ti, m_codec.get_type()));
93 }
94 
95 file_status_e
read(generic_packetizer_c *,bool)96 ivf_reader_c::read(generic_packetizer_c *,
97                    bool) {
98   size_t remaining_bytes = m_size - m_in->getFilePointer();
99 
100   ivf::frame_header_t header;
101   if ((sizeof(ivf::frame_header_t) > remaining_bytes) || (m_in->read(&header, sizeof(ivf::frame_header_t)) != sizeof(ivf::frame_header_t)))
102     return flush_packetizers();
103 
104   remaining_bytes     -= sizeof(ivf::frame_header_t);
105   uint32_t frame_size  = get_uint32_le(&header.frame_size);
106 
107   if (remaining_bytes < frame_size) {
108     m_in->setFilePointer(0, seek_end);
109     return flush_packetizers();
110   }
111 
112   memory_cptr buffer = memory_c::alloc(frame_size);
113   if (m_in->read(buffer->get_buffer(), frame_size) < frame_size) {
114     m_in->setFilePointer(0, seek_end);
115     return flush_packetizers();
116   }
117 
118   int64_t timestamp = get_uint64_le(&header.timestamp) * 1000000000ull * m_frame_rate_den / m_frame_rate_num;
119 
120   mxdebug_if(m_debug, fmt::format("key {4} header.ts {0} num {1} den {2} res {3}\n", get_uint64_le(&header.timestamp), m_frame_rate_num, m_frame_rate_den, timestamp, ivf::is_keyframe(buffer, m_codec.get_type())));
121 
122   ptzr(0).process(std::make_shared<packet_t>(buffer, timestamp));
123 
124   return FILE_STATUS_MOREDATA;
125 }
126 
127 void
identify()128 ivf_reader_c::identify() {
129   id_result_container();
130 
131   if (!m_ok)
132     return;
133 
134   auto info = mtx::id::info_c{};
135   info.add(mtx::id::pixel_dimensions, fmt::format("{0}x{1}", m_width, m_height));
136   id_result_track(0, ID_RESULT_TRACK_VIDEO, m_codec.get_name(), info.get());
137 }
138