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    MP3 output module
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include "common/codec.h"
17 #include "common/mp3.h"
18 #include "common/strings/formatting.h"
19 #include "merge/connection_checks.h"
20 #include "merge/output_control.h"
21 #include "merge/packet_extensions.h"
22 #include "output/p_mp3.h"
23 
24 using namespace libmatroska;
25 
mp3_packetizer_c(generic_reader_c * p_reader,track_info_c & p_ti,int samples_per_sec,int channels,bool source_is_good)26 mp3_packetizer_c::mp3_packetizer_c(generic_reader_c *p_reader,
27                                    track_info_c &p_ti,
28                                    int samples_per_sec,
29                                    int channels,
30                                    bool source_is_good)
31   : generic_packetizer_c(p_reader, p_ti)
32   , m_first_packet{true}
33   , m_bytes_skipped(0)
34   , m_samples_per_sec(samples_per_sec)
35   , m_channels(channels)
36   , m_samples_per_frame(1152)
37   , m_byte_buffer(128 * 1024)
38   , m_codec_id_set(false)
39   , m_valid_headers_found(source_is_good)
40   , m_timestamp_calculator{static_cast<int64_t>(samples_per_sec)}
41   , m_packet_duration{m_timestamp_calculator.get_duration(m_samples_per_frame).to_ns()}
42 {
43   set_track_type(track_audio);
44   set_track_default_duration(m_packet_duration);
45   m_timestamp_calculator.set_allow_smaller_timestamps(true);
46 }
47 
~mp3_packetizer_c()48 mp3_packetizer_c::~mp3_packetizer_c() {
49 }
50 
51 void
handle_garbage(int64_t bytes)52 mp3_packetizer_c::handle_garbage(int64_t bytes) {
53   bool warning_printed = false;
54 
55   if (m_first_packet) {
56     auto offset = calculate_avi_audio_sync(bytes, m_samples_per_frame, m_packet_duration);
57 
58     if (0 < offset) {
59       mxinfo_tid(m_ti.m_fname, m_ti.m_id,
60                  fmt::format(Y("This MPEG audio track contains {0} bytes of non-MP3 data at the beginning. "
61                                "This corresponds to a delay of {1}ms. This delay will be used instead of the garbage data.\n"), bytes, offset / 1000000));
62       warning_printed             = true;
63       m_ti.m_tcsync.displacement += offset;
64     }
65   }
66 
67   if (!warning_printed)
68     m_packet_extensions.push_back(std::make_shared<before_adding_to_cluster_cb_packet_extension_c>([this, bytes](packet_cptr const &packet, int64_t timestamp_offset) {
69       mxwarn_tid(m_ti.m_fname, m_ti.m_id,
70                  fmt::format("{0} {1}\n",
71                              fmt::format(NY("This audio track contains {0} byte of invalid data which was skipped before timestamp {1}.",
72                                             "This audio track contains {0} bytes of invalid data which were skipped before timestamp {1}.", bytes),
73                                          bytes, mtx::string::format_timestamp(packet->assigned_timestamp - timestamp_offset)),
74                              Y("The audio/video synchronization may have been lost.")));
75     }));
76 }
77 
78 memory_cptr
get_mp3_packet(mp3_header_t * mp3header)79 mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) {
80   if (m_byte_buffer.get_size() == 0)
81     return {};
82 
83   int pos;
84   while (1) {
85     auto buf  = m_byte_buffer.get_buffer();
86     auto size = m_byte_buffer.get_size();
87     pos       = find_mp3_header(buf, size);
88 
89     if (0 > pos)
90       return nullptr;
91 
92     decode_mp3_header(&buf[pos], mp3header);
93 
94     if ((pos + mp3header->framesize) > size)
95       return nullptr;
96 
97     if (!mp3header->is_tag)
98       break;
99 
100     m_byte_buffer.remove(mp3header->framesize + pos);
101   }
102 
103   // Try to be smart. We might get fed trash before the very first MP3
104   // header. And now a user has presented streams in which the trash
105   // contains valid MP3 headers before the 'real' ones...
106   // Screw the guys who program apps that use _random_ _trash_ for filling
107   // gaps. Screw those who try to use AVI no matter the 'cost'!
108   bool track_headers_changed = false;
109   if (!m_valid_headers_found) {
110     pos = find_consecutive_mp3_headers(m_byte_buffer.get_buffer(), m_byte_buffer.get_size(), 5);
111     if (0 > pos)
112       return nullptr;
113 
114     // Great, we have found five consecutive identical headers. Be happy
115     // with those!
116     decode_mp3_header(m_byte_buffer.get_buffer() + pos, mp3header);
117 
118     set_audio_channels(mp3header->channels);
119     set_audio_sampling_freq(mp3header->sampling_frequency);
120 
121     m_samples_per_sec      = mp3header->sampling_frequency;
122     track_headers_changed  = true;
123     m_valid_headers_found  = true;
124     m_bytes_skipped       += pos;
125     if (0 < m_bytes_skipped)
126       handle_garbage(m_bytes_skipped);
127     m_byte_buffer.remove(pos);
128 
129     pos             = 0;
130     m_bytes_skipped = 0;
131   }
132 
133   m_bytes_skipped += pos;
134   if (0 < m_bytes_skipped)
135     handle_garbage(m_bytes_skipped);
136   m_byte_buffer.remove(pos);
137   pos             = 0;
138   m_bytes_skipped = 0;
139 
140   if (m_first_packet) {
141     m_samples_per_frame  = mp3header->samples_per_channel;
142     m_packet_duration    = m_timestamp_calculator.get_duration(m_samples_per_frame).to_ns();
143     std::string codec_id = MKV_A_MP3;
144     codec_id[codec_id.length() - 1] = (char)(mp3header->layer + '0');
145     set_codec_id(codec_id.c_str());
146 
147     m_timestamp_calculator.set_samples_per_second(m_samples_per_sec);
148     set_track_default_duration(m_packet_duration);
149 
150     track_headers_changed = true;
151   }
152 
153   if (track_headers_changed)
154     rerender_track_headers();
155 
156   if (mp3header->framesize > m_byte_buffer.get_size())
157     return nullptr;
158 
159   auto buf = memory_c::clone(m_byte_buffer.get_buffer(), mp3header->framesize);
160 
161   m_byte_buffer.remove(mp3header->framesize);
162 
163   return buf;
164 }
165 
166 void
set_headers()167 mp3_packetizer_c::set_headers() {
168   if (!m_codec_id_set) {
169     set_codec_id(MKV_A_MP3);
170     m_codec_id_set = true;
171   }
172   set_audio_sampling_freq(m_samples_per_sec);
173   set_audio_channels(m_channels);
174 
175   generic_packetizer_c::set_headers();
176 }
177 
178 void
process_impl(packet_cptr const & packet)179 mp3_packetizer_c::process_impl(packet_cptr const &packet) {
180   m_timestamp_calculator.add_timestamp(packet);
181   m_discard_padding.add_maybe(packet->discard_padding);
182   m_byte_buffer.add(packet->data->get_buffer(), packet->data->get_size());
183 
184   flush_packets();
185 }
186 
187 void
flush_packets()188 mp3_packetizer_c::flush_packets() {
189   mp3_header_t mp3header;
190   memory_cptr mp3_packet;
191 
192   while ((mp3_packet = get_mp3_packet(&mp3header))) {
193     auto new_timestamp = m_timestamp_calculator.get_next_timestamp(m_samples_per_frame);
194     auto packet        = std::make_shared<packet_t>(mp3_packet, new_timestamp.to_ns(), m_packet_duration);
195 
196     packet->add_extensions(m_packet_extensions);
197     packet->discard_padding = m_discard_padding.get_next().value_or(timestamp_c{});
198 
199     add_packet(packet);
200 
201     m_first_packet = false;
202     m_packet_extensions.clear();
203   }
204 }
205 
206 connection_result_e
can_connect_to(generic_packetizer_c * src,std::string & error_message)207 mp3_packetizer_c::can_connect_to(generic_packetizer_c *src,
208                                  std::string &error_message) {
209   mp3_packetizer_c *msrc = dynamic_cast<mp3_packetizer_c *>(src);
210   if (!msrc)
211     return CAN_CONNECT_NO_FORMAT;
212 
213   connect_check_a_samplerate(m_samples_per_sec, msrc->m_samples_per_sec);
214   connect_check_a_channels(m_channels, msrc->m_channels);
215 
216   return CAN_CONNECT_YES;
217 }
218