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    header removal compressor
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include "common/ac3.h"
17 #include "common/dirac.h"
18 #include "common/dts.h"
19 #include "common/endian.h"
20 #include "common/compression/header_removal.h"
21 
header_removal_compressor_c()22 header_removal_compressor_c::header_removal_compressor_c()
23   : compressor_c(COMPRESSION_HEADER_REMOVAL)
24 {
25 }
26 
27 memory_cptr
do_decompress(unsigned char const * buffer,std::size_t size)28 header_removal_compressor_c::do_decompress(unsigned char const *buffer,
29                                            std::size_t size) {
30   if (!m_bytes || (0 == m_bytes->get_size()))
31     return memory_c::clone(buffer, size);
32 
33   memory_cptr new_buffer = memory_c::alloc(size + m_bytes->get_size());
34 
35   memcpy(new_buffer->get_buffer(),                       m_bytes->get_buffer(), m_bytes->get_size());
36   memcpy(new_buffer->get_buffer() + m_bytes->get_size(), buffer,                size);
37 
38   return new_buffer;
39 }
40 
41 memory_cptr
do_compress(unsigned char const * buffer,std::size_t size)42 header_removal_compressor_c::do_compress(unsigned char const *buffer,
43                                          std::size_t size) {
44   if (!m_bytes || (0 == m_bytes->get_size()))
45     return memory_c::clone(buffer, size);
46 
47   size_t to_remove_size = m_bytes->get_size();
48   if (size < to_remove_size)
49     throw mtx::compression_x(fmt::format(Y("Header removal compression not possible because the buffer contained {0} bytes "
50                                            "which is less than the size of the headers that should be removed, {1}."), size, to_remove_size));
51 
52   auto bytes_ptr = m_bytes->get_buffer();
53 
54   if (memcmp(buffer, bytes_ptr, to_remove_size)) {
55     std::string b_buffer, b_bytes;
56     size_t i;
57 
58     for (i = 0; to_remove_size > i; ++i) {
59       b_buffer += fmt::format(" {0:02x}", static_cast<unsigned int>(buffer[i]));
60       b_bytes  += fmt::format(" {0:02x}", static_cast<unsigned int>(bytes_ptr[i]));
61     }
62     throw mtx::compression_x(fmt::format(Y("Header removal compression not possible because the buffer did not start with the bytes that should be removed. "
63                                            "Wanted bytes:{0}; found:{1}."), b_bytes, b_buffer));
64   }
65 
66   return memory_c::clone(buffer + size, size - to_remove_size);
67 }
68 
69 void
set_track_headers(libmatroska::KaxContentEncoding & c_encoding)70 header_removal_compressor_c::set_track_headers(libmatroska::KaxContentEncoding &c_encoding) {
71   compressor_c::set_track_headers(c_encoding);
72 
73   // Set compression parameters.
74   GetChild<libmatroska::KaxContentCompSettings>(GetChild<libmatroska::KaxContentCompression>(c_encoding)).CopyBuffer(m_bytes->get_buffer(), m_bytes->get_size());
75 }
76 
77 // ------------------------------------------------------------
78 
analyze_header_removal_compressor_c()79 analyze_header_removal_compressor_c::analyze_header_removal_compressor_c()
80   : compressor_c(COMPRESSION_ANALYZE_HEADER_REMOVAL)
81   , m_packet_counter(0)
82 {
83 }
84 
~analyze_header_removal_compressor_c()85 analyze_header_removal_compressor_c::~analyze_header_removal_compressor_c() {
86   if (!m_bytes)
87     mxinfo("Analysis failed: no packet encountered\n");
88 
89   else if (m_bytes->get_size() == 0)
90     mxinfo("Analysis complete but no similarities found.\n");
91 
92   else {
93     mxinfo(fmt::format("Analysis complete. {0} identical byte(s) at the start of each of the {1} packet(s). Hex dump of the content:\n", m_bytes->get_size(), m_packet_counter));
94     debugging_c::hexdump(m_bytes->get_buffer(), m_bytes->get_size());
95   }
96 }
97 
98 memory_cptr
do_decompress(unsigned char const *,std::size_t)99 analyze_header_removal_compressor_c::do_decompress(unsigned char const *,
100                                                    std::size_t) {
101   mxerror("analyze_header_removal_compressor_c::do_decompress(): not supported\n");
102   return {};
103 }
104 
105 memory_cptr
do_compress(unsigned char const * buffer,std::size_t size)106 analyze_header_removal_compressor_c::do_compress(unsigned char const *buffer,
107                                                  std::size_t size) {
108   ++m_packet_counter;
109 
110   if (!m_bytes) {
111     m_bytes = memory_c::clone(buffer, size);
112     return memory_c::clone(buffer, size);
113   }
114 
115   auto saved         = m_bytes->get_buffer();
116   size_t i, new_size = 0;
117 
118   for (i = 0; i < std::min(size, m_bytes->get_size()); ++i, ++new_size)
119     if (buffer[i] != saved[i])
120       break;
121 
122   m_bytes->set_size(new_size);
123 
124   return memory_c::clone(buffer, size);
125 }
126 
127 void
set_track_headers(libmatroska::KaxContentEncoding &)128 analyze_header_removal_compressor_c::set_track_headers(libmatroska::KaxContentEncoding &) {
129 }
130 
131 // ------------------------------------------------------------
132 
mpeg4_p2_compressor_c()133 mpeg4_p2_compressor_c::mpeg4_p2_compressor_c() {
134   memory_cptr bytes = memory_c::alloc(3);
135   put_uint24_be(bytes->get_buffer(), 0x000001);
136   set_bytes(bytes);
137 }
138 
mpeg4_p10_compressor_c()139 mpeg4_p10_compressor_c::mpeg4_p10_compressor_c() {
140   memory_cptr bytes      = memory_c::alloc(1);
141   bytes->get_buffer()[0] = 0;
142   set_bytes(bytes);
143 }
144 
dirac_compressor_c()145 dirac_compressor_c::dirac_compressor_c() {
146   memory_cptr bytes = memory_c::alloc(4);
147   put_uint32_be(bytes->get_buffer(), mtx::dirac::SYNC_WORD);
148   set_bytes(bytes);
149 }
150 
dts_compressor_c()151 dts_compressor_c::dts_compressor_c() {
152   memory_cptr bytes = memory_c::alloc(4);
153   put_uint32_be(bytes->get_buffer(), static_cast<uint32_t>(mtx::dts::sync_word_e::core));
154   set_bytes(bytes);
155 }
156 
ac3_compressor_c()157 ac3_compressor_c::ac3_compressor_c() {
158   memory_cptr bytes = memory_c::alloc(2);
159   put_uint16_be(bytes->get_buffer(), mtx::ac3::SYNC_WORD);
160   set_bytes(bytes);
161 }
162 
mp3_compressor_c()163 mp3_compressor_c::mp3_compressor_c() {
164   memory_cptr bytes = memory_c::alloc(1);
165   bytes->get_buffer()[0] = 0xff;
166   set_bytes(bytes);
167 }
168