summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2021-06-12 21:06:55 -0400
committerSolomon Peachy <pizza@shaftnet.org>2021-06-13 12:13:03 -0400
commit756c0d2ac82515ea8389c69f5f87ca395daca63d (patch)
treeb835234ad7c6a084c4bda5c9d2db10bd0750c5d8
parentc067b344e8da0c9b6a9b785100661f598f64a5d3 (diff)
downloadrockbox-756c0d2ac82515ea8389c69f5f87ca395daca63d.tar.gz
rockbox-756c0d2ac82515ea8389c69f5f87ca395daca63d.zip
FS#13299: Simplify VBR frame parsing in the metadata decoder.
The old code would seek forward by the frame length, expecting to see a frame header there, perform a validity check, and then seek back to the current header. Unfortunately this doesn't handle situations where there is extra padding between the frames, leading us to potentially read garbage, causing the validity tests to fail and rejecting the file outright. Instead, keep track of the previous valid header/position, and if we find "valid" headers in a row return the first after seeking back to it. This change allows the file referenced in FS13299 to be properly parsed, but further work is needed to get the file to be playable. (file reports itself as layer 1, variable bit rate, variable sample rate!) Change-Id: I85f61a6360cc041a172db4b7a6b5516e5b60ceee
-rw-r--r--lib/rbcodec/metadata/mp3data.c55
1 files changed, 18 insertions, 37 deletions
diff --git a/lib/rbcodec/metadata/mp3data.c b/lib/rbcodec/metadata/mp3data.c
index 83605126d6..8c800c798a 100644
--- a/lib/rbcodec/metadata/mp3data.c
+++ b/lib/rbcodec/metadata/mp3data.c
@@ -212,27 +212,18 @@ static bool headers_have_same_type(unsigned long header1,
212 return header1 ? (header1 == header2) : true; 212 return header1 ? (header1 == header2) : true;
213} 213}
214 214
215/* Helper function to read 4-byte in big endian format. */
216static void read_uint32be_mp3data(int fd, unsigned long *data)
217{
218#ifdef ROCKBOX_BIG_ENDIAN
219 (void)read(fd, (char*)data, 4);
220#else
221 (void)read(fd, (char*)data, 4);
222 *data = betoh32(*data);
223#endif
224}
225
226static unsigned long __find_next_frame(int fd, long *offset, long max_offset, 215static unsigned long __find_next_frame(int fd, long *offset, long max_offset,
227 unsigned long reference_header, 216 unsigned long reference_header,
228 int(*getfunc)(int fd, unsigned char *c), 217 int(*getfunc)(int fd, unsigned char *c),
229 bool single_header) 218 bool single_header)
230{ 219{
231 unsigned long header=0; 220 uint32_t header=0;
221 uint32_t ref_header=0;
222 long ref_header_pos=0;
232 unsigned char tmp; 223 unsigned char tmp;
233 long pos = 0; 224 long pos = 0;
234 225
235 /* We will search until we find two consecutive MPEG frame headers with 226 /* We will search until we find two consecutive MPEG frame headers with
236 * the same MPEG version, layer and sampling frequency. The first header 227 * the same MPEG version, layer and sampling frequency. The first header
237 * of this pair is assumed to be the first valid MPEG frame header of the 228 * of this pair is assumed to be the first valid MPEG frame header of the
238 * whole stream. */ 229 * whole stream. */
@@ -243,43 +234,33 @@ static unsigned long __find_next_frame(int fd, long *offset, long max_offset,
243 return 0; 234 return 0;
244 header |= tmp; 235 header |= tmp;
245 pos++; 236 pos++;
246 237
247 /* Abort if max_offset is reached. Stop parsing. */ 238 /* Abort if max_offset is reached. Stop parsing. */
248 if (max_offset > 0 && pos > max_offset) 239 if (max_offset > 0 && pos > max_offset)
249 return 0; 240 return 0;
250 241
251 if (is_mp3frameheader(header)) { 242 if (is_mp3frameheader(header)) {
252 if (single_header) { 243 if (single_header) {
253 /* We search for one _single_ valid header that has the same 244 /* We search for one _single_ valid header that has the same
254 * type as the reference_header (if reference_header != 0). 245 * type as the reference_header (if reference_header != 0).
255 * In this case we are finished. */ 246 * In this case we are finished. */
256 if (headers_have_same_type(reference_header, header)) 247 if (headers_have_same_type(reference_header, header))
257 break; 248 break;
258 } else { 249 } else {
259 /* The current header is valid. Now gather the frame size, 250 /* The current header is valid. Compare it against the last
260 * seek to this byte position and check if there is another 251 one we found. NOTE: ref_header MUST come second! */
261 * valid MPEG frame header of the same type. */ 252 if (headers_have_same_type(header, ref_header)) {
262 struct mp3info info; 253 /* Found a match, return the header and offset of the FIRST */
263 254 header = ref_header;
264 /* Gather frame size from given header and seek to next 255 lseek(fd, ref_header_pos, SEEK_SET);
265 * frame header. */
266 mp3headerinfo(&info, header);
267 lseek(fd, info.frame_size-4, SEEK_CUR);
268
269 /* Read possible next frame header and seek back to last frame
270 * headers byte position. */
271 reference_header = 0;
272 read_uint32be_mp3data(fd, &reference_header);
273 //
274 lseek(fd, -info.frame_size, SEEK_CUR);
275
276 /* If the current header is of the same type as the previous
277 * header we are finished. */
278 if (headers_have_same_type(header, reference_header))
279 break; 256 break;
257 }
258 /* Otherwise look for another.. */
259 ref_header = header;
260 ref_header_pos = lseek(fd, 0, SEEK_CUR);
280 } 261 }
281 } 262 }
282 263
283 } while (true); 264 } while (true);
284 265
285 *offset = pos - 4; 266 *offset = pos - 4;