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