1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17 #include "mp4.h"
18
19 static int
get_mp4tags(PerlIO * infile,char * file,HV * info,HV * tags)20 get_mp4tags(PerlIO *infile, char *file, HV *info, HV *tags)
21 {
22 mp4info *mp4 = _mp4_parse(infile, file, info, tags, 0);
23
24 Safefree(mp4);
25
26 return 0;
27 }
28
29 // wrapper to return just the file offset
30 int
mp4_find_frame(PerlIO * infile,char * file,int offset)31 mp4_find_frame(PerlIO *infile, char *file, int offset)
32 {
33 HV *info = newHV();
34 int frame_offset = -1;
35
36 mp4_find_frame_return_info(infile, file, offset, info);
37
38 if ( my_hv_exists(info, "seek_offset") ) {
39 frame_offset = SvIV( *(my_hv_fetch(info, "seek_offset") ) );
40 }
41
42 SvREFCNT_dec(info);
43
44 return frame_offset;
45 }
46
47 // offset is in ms
48 // This is based on code from Rockbox
49 int
mp4_find_frame_return_info(PerlIO * infile,char * file,int offset,HV * info)50 mp4_find_frame_return_info(PerlIO *infile, char *file, int offset, HV *info)
51 {
52 int ret = 1;
53 uint16_t samplerate = 0;
54 uint32_t sound_sample_loc;
55 uint32_t i = 0;
56 uint32_t j = 0;
57 uint32_t new_sample = 0;
58 uint32_t new_sound_sample = 0;
59
60 uint32_t chunk = 1;
61 uint32_t range_samples = 0;
62 uint32_t total_samples = 0;
63 uint32_t skipped_samples = 0;
64 uint32_t chunk_sample;
65 uint32_t prev_chunk;
66 uint32_t prev_chunk_samples;
67 uint32_t file_offset;
68 uint32_t chunk_offset;
69
70 uint32_t box_size = 0;
71 Buffer tmp_buf;
72 char tmp_size[4];
73
74 // We need to read all info first to get some data we need to calculate
75 HV *tags = newHV();
76 mp4info *mp4 = _mp4_parse(infile, file, info, tags, 1);
77
78 // Init seek buffer
79 // Newz(0, &tmp_buf, sizeof(Buffer), Buffer);
80 buffer_init(&tmp_buf, MP4_BLOCK_SIZE);
81
82 // Seeking not yet supported for files with multiple tracks
83 if (mp4->track_count > 1) {
84 ret = -1;
85 goto out;
86 }
87
88 if ( !my_hv_exists(info, "samplerate") ) {
89 PerlIO_printf(PerlIO_stderr(), "find_frame: unknown sample rate\n");
90 ret = -1;
91 goto out;
92 }
93
94 // Pull out the samplerate
95 samplerate = SvIV( *( my_hv_fetch( info, "samplerate" ) ) );
96
97 // convert offset to sound_sample_loc
98 sound_sample_loc = (offset / 10) * (samplerate / 100);
99 DEBUG_TRACE("Looking for target sample %u\n", sound_sample_loc);
100
101 // Make sure we have the necessary metadata
102 if (
103 !mp4->num_time_to_samples
104 || !mp4->num_sample_byte_sizes
105 || !mp4->num_sample_to_chunks
106 || !mp4->num_chunk_offsets
107 ) {
108 PerlIO_printf(PerlIO_stderr(), "find_frame: File does not contain seek metadata: %s\n", file);
109 ret = -1;
110 goto out;
111 }
112
113 // Find the destination block from time_to_sample array
114 while ( (i < mp4->num_time_to_samples) &&
115 (new_sound_sample < sound_sample_loc)
116 ) {
117 j = (sound_sample_loc - new_sound_sample) / mp4->time_to_sample[i].sample_duration;
118
119 DEBUG_TRACE(
120 "i = %d / j = %d, sample_count[i]: %d, sample_duration[i]: %d\n",
121 i, j,
122 mp4->time_to_sample[i].sample_count,
123 mp4->time_to_sample[i].sample_duration
124 );
125
126 if (j <= mp4->time_to_sample[i].sample_count) {
127 new_sample += j;
128 new_sound_sample += j * mp4->time_to_sample[i].sample_duration;
129 break;
130 }
131 else {
132 // XXX need test for this bit of code (variable stts)
133 new_sound_sample += (mp4->time_to_sample[i].sample_duration
134 * mp4->time_to_sample[i].sample_count);
135 new_sample += mp4->time_to_sample[i].sample_count;
136 i++;
137 }
138 }
139
140 if ( new_sample >= mp4->num_sample_byte_sizes ) {
141 PerlIO_printf(PerlIO_stderr(), "find_frame: Offset out of range (%d >= %d)\n", new_sample, mp4->num_sample_byte_sizes);
142 ret = -1;
143 goto out;
144 }
145
146 DEBUG_TRACE("new_sample: %d, new_sound_sample: %d\n", new_sample, new_sound_sample);
147
148 // Write new stts box
149 {
150 int i;
151 uint32_t total_sample_count = _mp4_total_samples(mp4);
152 uint32_t stts_entries = total_sample_count - new_sample;
153 uint32_t cur_duration = 0;
154 struct tts *stts;
155 int32_t stts_index = -1;
156
157 Newz(0, stts, stts_entries * sizeof(*stts), struct tts);
158
159 for (i = new_sample; i < total_sample_count; i++) {
160 uint32_t duration = _mp4_get_sample_duration(mp4, i);
161
162 if (cur_duration && cur_duration == duration) {
163 // same as previous entry, combine together
164 stts_entries--;
165 stts[stts_index].sample_count++;
166 }
167 else {
168 stts_index++;
169 stts[stts_index].sample_count = 1;
170 stts[stts_index].sample_duration = duration;
171 cur_duration = duration;
172 }
173 }
174
175 DEBUG_TRACE("Writing new stts (entries: %d)\n", stts_entries);
176 buffer_put_int(&tmp_buf, stts_entries);
177
178 for (i = 0; i < stts_entries; i++) {
179 DEBUG_TRACE(" sample_count %d, sample_duration %d\n", stts[i].sample_count, stts[i].sample_duration);
180 buffer_put_int(&tmp_buf, stts[i].sample_count);
181 buffer_put_int(&tmp_buf, stts[i].sample_duration);
182 }
183
184 mp4->new_stts = newSVpv("", 0);
185 put_u32( tmp_size, buffer_len(&tmp_buf) + 12 );
186 sv_catpvn( mp4->new_stts, tmp_size, 4 );
187 sv_catpvn( mp4->new_stts, "stts", 4 );
188 sv_catpvn( mp4->new_stts, "\0\0\0\0", 4 );
189 sv_catpvn( mp4->new_stts, (char *)buffer_ptr(&tmp_buf), buffer_len(&tmp_buf) );
190 //buffer_dump(&tmp_buf, 0);
191 buffer_clear(&tmp_buf);
192
193 Safefree(stts);
194 }
195
196 // We know the new block, now calculate the file position
197
198 /* Locate the chunk containing the sample */
199 prev_chunk = mp4->sample_to_chunk[0].first_chunk;
200 prev_chunk_samples = mp4->sample_to_chunk[0].samples_per_chunk;
201
202 for (i = 1; i < mp4->num_sample_to_chunks; i++) {
203 chunk = mp4->sample_to_chunk[i].first_chunk;
204 range_samples = (chunk - prev_chunk) * prev_chunk_samples;
205
206 DEBUG_TRACE("prev_chunk: %d, prev_chunk_samples: %d, chunk: %d, range_samples: %d\n",
207 prev_chunk, prev_chunk_samples, chunk, range_samples);
208
209 if (new_sample < total_samples + range_samples)
210 break;
211
212 total_samples += range_samples;
213 prev_chunk = mp4->sample_to_chunk[i].first_chunk;
214 prev_chunk_samples = mp4->sample_to_chunk[i].samples_per_chunk;
215 }
216
217 DEBUG_TRACE("prev_chunk: %d, prev_chunk_samples: %d, total_samples: %d\n", prev_chunk, prev_chunk_samples, total_samples);
218
219 if (new_sample >= mp4->sample_to_chunk[0].samples_per_chunk) {
220 chunk = prev_chunk + (new_sample - total_samples) / prev_chunk_samples;
221 }
222 else {
223 chunk = 1;
224 }
225
226 DEBUG_TRACE("chunk: %d\n", chunk);
227
228 /* Get sample of the first sample in the chunk */
229 chunk_sample = total_samples + (chunk - prev_chunk) * prev_chunk_samples;
230
231 DEBUG_TRACE("chunk_sample: %d\n", chunk_sample);
232
233 /* Get offset in file */
234
235 if (chunk > mp4->num_chunk_offsets) {
236 file_offset = mp4->chunk_offset[mp4->num_chunk_offsets - 1];
237 }
238 else {
239 file_offset = mp4->chunk_offset[chunk - 1];
240 }
241
242 DEBUG_TRACE("file_offset: %d\n", file_offset);
243
244 if (chunk_sample > new_sample) {
245 PerlIO_printf(PerlIO_stderr(), "find_frame: sample out of range (%d > %d)\n", chunk_sample, new_sample);
246 ret = -1;
247 goto out;
248 }
249
250 // Move offset within the chunk to the correct sample range
251 for (i = chunk_sample; i < new_sample; i++) {
252 file_offset += mp4->sample_byte_size[i];
253 skipped_samples++;
254 DEBUG_TRACE(" file_offset + %d: %d\n", mp4->sample_byte_size[i], file_offset);
255 }
256
257 if (file_offset > mp4->audio_offset + mp4->audio_size) {
258 PerlIO_printf(PerlIO_stderr(), "find_frame: file offset out of range (%d > %lld)\n", file_offset, mp4->audio_offset + mp4->audio_size);
259 ret = -1;
260 goto out;
261 }
262
263 // Write new stsc box
264 {
265 int i;
266 uint32_t stsc_entries = mp4->num_chunk_offsets - chunk + 1;
267 uint32_t cur_samples_per_chunk = 0;
268 struct stc *stsc;
269 int32_t stsc_index = -1;
270 uint32_t chunk_delta = 1;
271 j = 1;
272
273 Newz(0, stsc, stsc_entries * sizeof(*stsc), struct stc);
274
275 for (i = chunk; i <= mp4->num_chunk_offsets; i++) {
276 // Find the number of samples in chunk i
277 uint32_t samples_in_chunk = _mp4_samples_in_chunk(mp4, i);
278
279 if (cur_samples_per_chunk && cur_samples_per_chunk == samples_in_chunk) {
280 // same as previous entry, combine together
281 stsc_entries--;
282 }
283 else {
284 stsc_index++;
285
286 stsc[stsc_index].first_chunk = chunk_delta;
287
288 if (j == 1) {
289 // The first chunk may have less samples in it due to seeking within a chunk
290 stsc[stsc_index].samples_per_chunk = samples_in_chunk - skipped_samples;
291 cur_samples_per_chunk = samples_in_chunk - skipped_samples;
292 j++;
293 }
294 else {
295 stsc[stsc_index].samples_per_chunk = samples_in_chunk;
296 cur_samples_per_chunk = samples_in_chunk;
297 }
298 }
299
300 chunk_delta++;
301 }
302
303 DEBUG_TRACE("Writing new stsc (entries: %d)\n", stsc_entries);
304 buffer_put_int(&tmp_buf, stsc_entries);
305
306 for (i = 0; i < stsc_entries; i++) {
307 DEBUG_TRACE(" first_chunk %d, samples_per_chunk %d\n", stsc[i].first_chunk, stsc[i].samples_per_chunk);
308 buffer_put_int(&tmp_buf, stsc[i].first_chunk);
309 buffer_put_int(&tmp_buf, stsc[i].samples_per_chunk);
310 buffer_put_int(&tmp_buf, 1); // XXX sample description index, is this OK?
311 }
312
313 mp4->new_stsc = newSVpv("", 0);
314 put_u32( tmp_size, buffer_len(&tmp_buf) + 12 );
315 sv_catpvn( mp4->new_stsc, tmp_size, 4 );
316 sv_catpvn( mp4->new_stsc, "stsc", 4 );
317 sv_catpvn( mp4->new_stsc, "\0\0\0\0", 4 );
318 sv_catpvn( mp4->new_stsc, (char *)buffer_ptr(&tmp_buf), buffer_len(&tmp_buf) );
319 DEBUG_TRACE("Created new stsc\n");
320 //buffer_dump(&tmp_buf, 0);
321 buffer_clear(&tmp_buf);
322
323 Safefree(stsc);
324 }
325
326 // Write new stsz box, num_sample_byte_sizes -= $new_sample, skip $new_sample items
327 buffer_put_int(&tmp_buf, 0);
328 buffer_put_int(&tmp_buf, mp4->num_sample_byte_sizes - new_sample);
329 DEBUG_TRACE("Writing new stsz: %d items\n", mp4->num_sample_byte_sizes - new_sample);
330 j = 1;
331 for (i = new_sample; i < mp4->num_sample_byte_sizes; i++) {
332 DEBUG_TRACE(" sample %d sample_byte_size %d\n", j++, mp4->sample_byte_size[i]);
333 buffer_put_int(&tmp_buf, mp4->sample_byte_size[i]);
334 }
335
336 mp4->new_stsz = newSVpv("", 0);
337 put_u32( tmp_size, buffer_len(&tmp_buf) + 12 );
338 sv_catpvn( mp4->new_stsz, tmp_size, 4 );
339 sv_catpvn( mp4->new_stsz, "stsz", 4 );
340 sv_catpvn( mp4->new_stsz, "\0\0\0\0", 4 );
341 sv_catpvn( mp4->new_stsz, (char *)buffer_ptr(&tmp_buf), buffer_len(&tmp_buf) );
342 DEBUG_TRACE("Created new stsz\n");
343 //buffer_dump(&tmp_buf, 0);
344 buffer_clear(&tmp_buf);
345
346 // Total up size of 4 new st* boxes
347 // stco is calculated directly since we can't write it without offsets
348 mp4->new_st_size
349 = sv_len(mp4->new_stts)
350 + sv_len(mp4->new_stsc)
351 + sv_len(mp4->new_stsz)
352 + 12 + ( 4 * (mp4->num_chunk_offsets - chunk + 2) ); // stco size
353
354 DEBUG_TRACE("new_st_size: %d, old_st_size: %d\n", mp4->new_st_size, mp4->old_st_size);
355
356 // Calculate offset for each chunk
357 chunk_offset = SvIV( *( my_hv_fetch(info, "audio_offset") ) );
358 chunk_offset -= ( mp4->old_st_size - mp4->new_st_size );
359 chunk_offset += 8; // mdat size + fourcc
360
361 DEBUG_TRACE("chunk_offset: %d\n", chunk_offset);
362
363 // Write new stco box, num_chunk_offsets -= $chunk, skip $chunk items
364 buffer_put_int(&tmp_buf, mp4->num_chunk_offsets - chunk + 1);
365 DEBUG_TRACE("Writing new stco: %d items\n", mp4->num_chunk_offsets - chunk + 1);
366 for (i = chunk - 1; i < mp4->num_chunk_offsets; i++) {
367 if (i == chunk - 1) {
368 // The first chunk offset is the start of mdat (chunk_offset)
369 buffer_put_int( &tmp_buf, chunk_offset );
370 DEBUG_TRACE( " offset %d (orig %d)\n", chunk_offset, mp4->chunk_offset[i] );
371 }
372 else {
373 buffer_put_int( &tmp_buf, mp4->chunk_offset[i] - file_offset + chunk_offset );
374 DEBUG_TRACE( " offset %d (orig %d)\n", mp4->chunk_offset[i] - file_offset + chunk_offset, mp4->chunk_offset[i] );
375 }
376 }
377
378 mp4->new_stco = newSVpv("", 0);
379 put_u32( tmp_size, buffer_len(&tmp_buf) + 12 );
380 sv_catpvn( mp4->new_stco, tmp_size, 4 );
381 sv_catpvn( mp4->new_stco, "stco", 4 );
382 sv_catpvn( mp4->new_stco, "\0\0\0\0", 4 );
383 sv_catpvn( mp4->new_stco, (char *)buffer_ptr(&tmp_buf), buffer_len(&tmp_buf) );
384 DEBUG_TRACE("Created new stco\n");
385 //buffer_dump(&tmp_buf, 0);
386 buffer_clear(&tmp_buf);
387
388 DEBUG_TRACE("real st size: %ld\n",
389 sv_len(mp4->new_stts)
390 + sv_len(mp4->new_stsc)
391 + sv_len(mp4->new_stsz)
392 + sv_len(mp4->new_stco)
393 );
394
395 // Make second pass through header, reducing size of all parent boxes by st* size difference
396 // Copy all boxes, replacing st* boxes with new ones
397 mp4->seekhdr = newSVpv("", 0);
398
399 PerlIO_seek(mp4->infile, 0, SEEK_SET);
400
401 // XXX this is ugly, because we are reading a second time we have to reset
402 // various things in the mp4 struct
403 Newz(0, mp4->buf, sizeof(Buffer), Buffer);
404 buffer_init(mp4->buf, MP4_BLOCK_SIZE);
405
406 mp4->audio_offset = 0;
407 mp4->current_track = 0;
408 mp4->track_count = 0;
409
410 // free seek structs because we will be reading them a second time
411 if (mp4->time_to_sample) Safefree(mp4->time_to_sample);
412 if (mp4->sample_to_chunk) Safefree(mp4->sample_to_chunk);
413 if (mp4->sample_byte_size) Safefree(mp4->sample_byte_size);
414 if (mp4->chunk_offset) Safefree(mp4->chunk_offset);
415
416 mp4->time_to_sample = NULL;
417 mp4->sample_to_chunk = NULL;
418 mp4->sample_byte_size = NULL;
419 mp4->chunk_offset = NULL;
420
421 while ( (box_size = _mp4_read_box(mp4)) > 0 ) {
422 mp4->audio_offset += box_size;
423 DEBUG_TRACE("seek pass 2: read box of size %d\n", box_size);
424
425 if (mp4->audio_offset >= mp4->file_size)
426 break;
427 }
428
429 my_hv_store( info, "seek_offset", newSVuv(file_offset) );
430 my_hv_store( info, "seek_header", mp4->seekhdr );
431
432 if (mp4->buf) {
433 buffer_free(mp4->buf);
434 Safefree(mp4->buf);
435 }
436
437 out:
438 // Don't leak
439 SvREFCNT_dec(tags);
440
441 if (mp4->new_stts) SvREFCNT_dec(mp4->new_stts);
442 if (mp4->new_stsc) SvREFCNT_dec(mp4->new_stsc);
443 if (mp4->new_stsz) SvREFCNT_dec(mp4->new_stsz);
444 if (mp4->new_stco) SvREFCNT_dec(mp4->new_stco);
445
446 // free seek structs
447 if (mp4->time_to_sample) Safefree(mp4->time_to_sample);
448 if (mp4->sample_to_chunk) Safefree(mp4->sample_to_chunk);
449 if (mp4->sample_byte_size) Safefree(mp4->sample_byte_size);
450 if (mp4->chunk_offset) Safefree(mp4->chunk_offset);
451
452 // free seek buffer
453 buffer_free(&tmp_buf);
454
455 Safefree(mp4);
456
457 if (ret == -1) {
458 my_hv_store( info, "seek_offset", newSViv(-1) );
459 }
460
461 return ret;
462 }
463
464 mp4info *
_mp4_parse(PerlIO * infile,char * file,HV * info,HV * tags,uint8_t seeking)465 _mp4_parse(PerlIO *infile, char *file, HV *info, HV *tags, uint8_t seeking)
466 {
467 off_t file_size;
468 uint32_t box_size = 0;
469
470 mp4info *mp4;
471 Newz(0, mp4, sizeof(mp4info), mp4info);
472 Newz(0, mp4->buf, sizeof(Buffer), Buffer);
473
474 mp4->audio_offset = 0;
475 mp4->infile = infile;
476 mp4->file = file;
477 mp4->info = info;
478 mp4->tags = tags;
479 mp4->current_track = 0;
480 mp4->track_count = 0;
481 mp4->seen_moov = 0;
482 mp4->seeking = seeking ? 1 : 0;
483
484 mp4->time_to_sample = NULL;
485 mp4->sample_to_chunk = NULL;
486 mp4->sample_byte_size = NULL;
487 mp4->chunk_offset = NULL;
488
489 buffer_init(mp4->buf, MP4_BLOCK_SIZE);
490
491 file_size = _file_size(infile);
492 mp4->file_size = file_size;
493
494 my_hv_store( info, "file_size", newSVuv(file_size) );
495
496 // Create empty tracks array
497 my_hv_store( info, "tracks", newRV_noinc( (SV *)newAV() ) );
498
499 while ( (box_size = _mp4_read_box(mp4)) > 0 ) {
500 mp4->audio_offset += box_size;
501 DEBUG_TRACE("read box of size %d / audio_offset %llu\n", box_size, mp4->audio_offset);
502
503 if (mp4->audio_offset >= file_size)
504 break;
505 }
506
507 // XXX: if no ftyp was found, assume it is brand 'mp41'
508
509 // if no bitrate was found (i.e. ALAC), calculate based on file_size/song_length_ms
510 if ( !my_hv_exists(info, "avg_bitrate") ) {
511 SV **entry = my_hv_fetch(info, "song_length_ms");
512 if (entry) {
513 SV **audio_offset = my_hv_fetch(info, "audio_offset");
514 if (audio_offset) {
515 uint32_t song_length_ms = SvIV(*entry);
516 uint32_t bitrate = _bitrate(file_size - SvIV(*audio_offset), song_length_ms);
517
518 my_hv_store( info, "avg_bitrate", newSVuv(bitrate) );
519 mp4->bitrate = bitrate;
520 }
521 }
522 }
523
524 // DLNA detection, based on code from libdlna
525 if (!mp4->dlna_invalid && mp4->samplerate && mp4->bitrate && mp4->channels) {
526 switch (mp4->audio_object_type) {
527 case AAC_LC:
528 case AAC_LC_ER:
529 {
530 if (mp4->samplerate < 8000 || mp4->samplerate > 48000)
531 break;
532
533 if (mp4->channels <= 2) {
534 if (mp4->bitrate <= 192000)
535 my_hv_store( info, "dlna_profile", newSVpv("AAC_ISO_192", 0) );
536 else if (mp4->bitrate <= 320000)
537 my_hv_store( info, "dlna_profile", newSVpv("AAC_ISO_320", 0) );
538 else if (mp4->bitrate <= 576000)
539 my_hv_store( info, "dlna_profile", newSVpv("AAC_ISO", 0) );
540 }
541 else if (mp4->channels <= 6) {
542 if (mp4->bitrate <= 1440000)
543 my_hv_store( info, "dlna_profile", newSVpv("AAC_MULT5_ISO", 0) );
544 }
545
546 break;
547 }
548
549 case AAC_LTP:
550 case AAC_LTP_ER:
551 {
552 if (mp4->samplerate < 8000)
553 break;
554
555 if (mp4->samplerate <= 48000) {
556 if (mp4->channels <= 2 && mp4->bitrate <= 576000)
557 my_hv_store( info, "dlna_profile", newSVpv("AAC_LTP_ISO", 0) );
558 }
559 else if (mp4->samplerate <= 96000) {
560 if (mp4->channels <= 6 && mp4->bitrate <= 2880000)
561 my_hv_store( info, "dlna_profile", newSVpv("AAC_LTP_MULT5_ISO", 0) );
562 else if (mp4->channels <= 8 && mp4->bitrate <= 4032000)
563 my_hv_store( info, "dlna_profile", newSVpv("AAC_LTP_MULT7_ISO", 0) );
564 }
565
566 break;
567 }
568
569 case AAC_HE:
570 {
571 if (mp4->samplerate < 8000)
572 break;
573
574 if (mp4->samplerate <= 24000) {
575 if (mp4->channels > 2)
576 break;
577
578 if (mp4->bitrate <= 128000)
579 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ISO_128", 0) );
580 else if (mp4->bitrate <= 320000)
581 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ISO_320", 0) );
582 else if (mp4->bitrate <= 576000)
583 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L2_ISO", 0) );
584 }
585 else if (mp4->samplerate <= 48000) {
586 if (mp4->channels <= 2 && mp4->bitrate <= 576000)
587 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_L3_ISO", 0) );
588 else if (mp4->channels <= 6 && mp4->bitrate <= 1440000)
589 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_MULT5_ISO", 0) );
590 else if (mp4->channels <= 8 && mp4->bitrate <= 4032000)
591 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_MULT7", 0) );
592 }
593 else if (mp4->samplerate <= 96000) {
594 if (mp4->channels <= 8 && mp4->bitrate <= 4032000)
595 my_hv_store( info, "dlna_profile", newSVpv("HEAAC_MULT7", 0) );
596 }
597
598 break;
599 }
600
601 case AAC_PARAM_ER:
602 case AAC_PS:
603 {
604 if (mp4->samplerate < 8000)
605 break;
606
607 if (mp4->samplerate <= 24000) {
608 if (mp4->channels > 2)
609 break;
610
611 if (mp4->bitrate <= 128000)
612 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_L2_128", 0) );
613 else if (mp4->bitrate <= 320000)
614 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_L2_320", 0) );
615 else if (mp4->bitrate <= 576000)
616 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_L2", 0) );
617 }
618 else if (mp4->samplerate <= 48000) {
619 if (mp4->channels <= 2 && mp4->bitrate <= 576000)
620 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_L3", 0) );
621 else if (mp4->channels <= 6 && mp4->bitrate <= 1440000)
622 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_L4", 0) );
623 else if (mp4->channels <= 6 && mp4->bitrate <= 2880000)
624 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_MULT5", 0) );
625 else if (mp4->channels <= 8 && mp4->bitrate <= 4032000)
626 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_MULT7", 0) );
627 }
628 else if (mp4->samplerate <= 96000) {
629 if (mp4->channels <= 8 && mp4->bitrate <= 4032000)
630 my_hv_store( info, "dlna_profile", newSVpv("HEAACv2_MULT7", 0) );
631 }
632
633 break;
634 }
635
636 case AAC_BSAC_ER:
637 {
638 if (mp4->samplerate < 16000 || mp4->samplerate > 48000)
639 break;
640
641 if (mp4->bitrate > 128000)
642 break;
643
644 if (mp4->channels <= 2)
645 my_hv_store( info, "dlna_profile", newSVpv("BSAC_ISO", 0) );
646 else if (mp4->channels <= 6)
647 my_hv_store( info, "dlna_profile", newSVpv("BSAC_MULT5_ISO", 0) );
648
649 break;
650 }
651
652 default:
653 break;
654 }
655 }
656
657 buffer_free(mp4->buf);
658 Safefree(mp4->buf);
659
660 return mp4;
661 }
662
663 int
_mp4_read_box(mp4info * mp4)664 _mp4_read_box(mp4info *mp4)
665 {
666 uint64_t size; // total size of box
667 char type[5];
668 uint8_t skip = 0;
669
670 mp4->rsize = 0; // remaining size in box
671
672 if ( !_check_buf(mp4->infile, mp4->buf, 16, MP4_BLOCK_SIZE) ) {
673 return 0;
674 }
675
676 size = buffer_get_int(mp4->buf);
677 strncpy( type, (char *)buffer_ptr(mp4->buf), 4 );
678 type[4] = '\0';
679 buffer_consume(mp4->buf, 4);
680
681 // Check for 64-bit size
682 if (size == 1) {
683 size = buffer_get_int64(mp4->buf);
684 mp4->hsize = 16;
685 }
686 else if (size == 0) {
687 // XXX: size extends to end of file
688 mp4->hsize = 8;
689 }
690 else {
691 mp4->hsize = 8;
692 }
693
694 if (size) {
695 mp4->rsize = size - mp4->hsize;
696 }
697
698 mp4->size = size;
699
700 DEBUG_TRACE("%s size %llu\n", type, size);
701
702 if (mp4->seekhdr) {
703 // Copy and adjust header if seeking
704 char tmp_size[4];
705
706 if (
707 FOURCC_EQ(type, "moov")
708 || FOURCC_EQ(type, "trak")
709 || FOURCC_EQ(type, "mdia")
710 || FOURCC_EQ(type, "minf")
711 || FOURCC_EQ(type, "stbl")
712 ) {
713 // Container box, adjust size
714 put_u32(tmp_size, size - (mp4->old_st_size - mp4->new_st_size));
715 DEBUG_TRACE(" Box is parent of st*, changed size to %llu\n", size - (mp4->old_st_size - mp4->new_st_size));
716 sv_catpvn( mp4->seekhdr, tmp_size, 4 );
717 sv_catpvn( mp4->seekhdr, type, 4 );
718 }
719 // Replace st* boxes with our new versions
720 else if ( FOURCC_EQ(type, "stts") ) {
721 DEBUG_TRACE("adding new stts of size %ld\n", sv_len(mp4->new_stts));
722 sv_catsv( mp4->seekhdr, mp4->new_stts );
723 }
724 else if ( FOURCC_EQ(type, "stsc") ) {
725 DEBUG_TRACE("adding new stsc of size %ld\n", sv_len(mp4->new_stsc));
726 sv_catsv( mp4->seekhdr, mp4->new_stsc );
727 }
728 else if ( FOURCC_EQ(type, "stsz") ) {
729 DEBUG_TRACE("adding new stsz of size %ld\n", sv_len(mp4->new_stsz));
730 sv_catsv( mp4->seekhdr, mp4->new_stsz );
731 }
732 else if ( FOURCC_EQ(type, "stco") ) {
733 DEBUG_TRACE("adding new stco of size %ld\n", sv_len(mp4->new_stco));
734 sv_catsv( mp4->seekhdr, mp4->new_stco );
735 }
736 else {
737 // Normal box, copy it
738 put_u32(tmp_size, size);
739 sv_catpvn( mp4->seekhdr, tmp_size, 4 );
740 sv_catpvn( mp4->seekhdr, type, 4 );
741
742 // stsd is special and contains real bytes and is also a container
743 if ( FOURCC_EQ(type, "stsd") ) {
744 sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), 8 );
745 }
746
747 // mp4a is special, ugh
748 else if ( FOURCC_EQ(type, "mp4a") ) {
749 sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), 28 );
750 }
751
752 // and so is meta
753 else if ( FOURCC_EQ(type, "meta") ) {
754 sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), mp4->meta_size );
755 }
756
757 // Copy contents unless it's a container
758 else if (
759 !FOURCC_EQ(type, "edts")
760 && !FOURCC_EQ(type, "dinf")
761 && !FOURCC_EQ(type, "udta")
762 && !FOURCC_EQ(type, "mdat")
763 ) {
764 if ( !_check_buf(mp4->infile, mp4->buf, size - 8, MP4_BLOCK_SIZE) ) {
765 return 0;
766 }
767
768 // XXX find a way to skip udta completely when rewriting seek header
769 // to avoid useless copying of artwork. Will require adjusting offsets
770 // differently.
771
772 sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), size - 8 );
773 }
774 }
775
776 // XXX should probably return size here and avoid reading info a second time
777 // or move the header copying code to somewhere else
778 }
779
780 if ( FOURCC_EQ(type, "ftyp") ) {
781 if ( !_mp4_parse_ftyp(mp4) ) {
782 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad ftyp box): %s\n", mp4->file);
783 return 0;
784 }
785 }
786 else if (
787 FOURCC_EQ(type, "moov")
788 || FOURCC_EQ(type, "edts")
789 || FOURCC_EQ(type, "mdia")
790 || FOURCC_EQ(type, "minf")
791 || FOURCC_EQ(type, "dinf")
792 || FOURCC_EQ(type, "stbl")
793 || FOURCC_EQ(type, "udta")
794 ) {
795 // These boxes are containers for nested boxes, return only the fact that
796 // we read the header size of the container
797 size = mp4->hsize;
798
799 if ( FOURCC_EQ(type, "trak") ) {
800 mp4->track_count++;
801 }
802 }
803 else if ( FOURCC_EQ(type, "trak") ) {
804 // Also a container, but we need to increment track_count too
805 size = mp4->hsize;
806 mp4->track_count++;
807 }
808 else if ( FOURCC_EQ(type, "mvhd") ) {
809 mp4->seen_moov = 1;
810
811 if ( !_mp4_parse_mvhd(mp4) ) {
812 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mvhd box): %s\n", mp4->file);
813 return 0;
814 }
815 }
816 else if ( FOURCC_EQ(type, "tkhd") ) {
817 if ( !_mp4_parse_tkhd(mp4) ) {
818 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad tkhd box): %s\n", mp4->file);
819 return 0;
820 }
821 }
822 else if ( FOURCC_EQ(type, "mdhd") ) {
823 if ( !_mp4_parse_mdhd(mp4) ) {
824 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mdhd box): %s\n", mp4->file);
825 return 0;
826 }
827 }
828 else if ( FOURCC_EQ(type, "hdlr") ) {
829 if ( !_mp4_parse_hdlr(mp4) ) {
830 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad hdlr box): %s\n", mp4->file);
831 return 0;
832 }
833 }
834 else if ( FOURCC_EQ(type, "stsd") ) {
835 if ( !_mp4_parse_stsd(mp4) ) {
836 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsd box): %s\n", mp4->file);
837 return 0;
838 }
839
840 // stsd is a special real box + container, count only the real bytes (8)
841 size = 8 + mp4->hsize;
842 }
843 else if ( FOURCC_EQ(type, "mp4a") ) {
844 if ( !_mp4_parse_mp4a(mp4) ) {
845 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mp4a box): %s\n", mp4->file);
846 return 0;
847 }
848
849 // mp4a is a special real box + container, count only the real bytes (28)
850 size = 28 + mp4->hsize;
851 }
852 else if ( FOURCC_EQ(type, "alac") ) {
853 if ( !_mp4_parse_alac(mp4) ) {
854 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad alac box): %s\n", mp4->file);
855 return 0;
856 }
857
858 // skip rest (alac description)
859 mp4->rsize -= 28;
860 skip = 1;
861 }
862 else if ( FOURCC_EQ(type, "drms") ) {
863 // Mark encoding
864 HV *trackinfo = _mp4_get_current_trackinfo(mp4);
865
866 my_hv_store( trackinfo, "encoding", newSVpvn("drms", 4) );
867
868 // Skip rest
869 skip = 1;
870 }
871 else if ( FOURCC_EQ(type, "esds") ) {
872 if ( !_mp4_parse_esds(mp4) ) {
873 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad esds box): %s\n", mp4->file);
874 return 0;
875 }
876 }
877 else if ( FOURCC_EQ(type, "stts") ) {
878 if ( mp4->seeking && mp4->track_count == 1 ) {
879 if ( !_mp4_parse_stts(mp4) ) {
880 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stts box): %s\n", mp4->file);
881 return 0;
882 }
883 mp4->old_st_size += size;
884 }
885 else {
886 skip = 1;
887 }
888 }
889 else if ( FOURCC_EQ(type, "stsc") ) {
890 if ( mp4->seeking && mp4->track_count == 1 ) {
891 if ( !_mp4_parse_stsc(mp4) ) {
892 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsc box): %s\n", mp4->file);
893 return 0;
894 }
895 mp4->old_st_size += size;
896 }
897 else {
898 skip = 1;
899 }
900 }
901 else if ( FOURCC_EQ(type, "stsz") ) {
902 if ( mp4->seeking && mp4->track_count == 1 ) {
903 if ( !_mp4_parse_stsz(mp4) ) {
904 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsz box): %s\n", mp4->file);
905 return 0;
906 }
907 mp4->old_st_size += size;
908 }
909 else {
910 skip = 1;
911 }
912 }
913 else if ( FOURCC_EQ(type, "stco") ) {
914 if ( mp4->seeking && mp4->track_count == 1 ) {
915 if ( !_mp4_parse_stco(mp4) ) {
916 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stco box): %s\n", mp4->file);
917 return 0;
918 }
919 mp4->old_st_size += size;
920 }
921 else {
922 skip = 1;
923 }
924 }
925 else if ( FOURCC_EQ(type, "meta") ) {
926 uint8_t meta_size = _mp4_parse_meta(mp4);
927 if ( !meta_size ) {
928 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad meta box): %s\n", mp4->file);
929 return 0;
930 }
931
932 mp4->meta_size = meta_size;
933
934 // meta is a special real box + container, count only the real bytes
935 size = meta_size + mp4->hsize;
936 }
937 else if ( FOURCC_EQ(type, "ilst") ) {
938 if ( !_mp4_parse_ilst(mp4) ) {
939 PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad ilst box): %s\n", mp4->file);
940 return 0;
941 }
942 }
943 else if ( FOURCC_EQ(type, "mdat") ) {
944 // Audio data here, there may be boxes after mdat, so we have to skip it
945 skip = 1;
946
947 // If we haven't seen moov yet, set a flag so we can print a warning
948 // or handle it some other way
949 if ( !mp4->seen_moov ) {
950 my_hv_store( mp4->info, "leading_mdat", newSVuv(1) );
951 mp4->dlna_invalid = 1; // DLNA 8.6.34.8, moov must be before mdat
952 }
953
954 // Record audio offset and length
955 my_hv_store( mp4->info, "audio_offset", newSVuv(mp4->audio_offset) );
956 my_hv_store( mp4->info, "audio_size", newSVuv(size) );
957 mp4->audio_size = size;
958 }
959 else {
960 DEBUG_TRACE(" Unhandled box, skipping\n");
961 skip = 1;
962 }
963
964 if (skip) {
965 _mp4_skip(mp4, mp4->rsize);
966 }
967
968 return size;
969 }
970
971 uint8_t
_mp4_parse_ftyp(mp4info * mp4)972 _mp4_parse_ftyp(mp4info *mp4)
973 {
974 AV *compatible_brands = newAV();
975
976 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
977 return 0;
978 }
979
980 my_hv_store( mp4->info, "major_brand", newSVpvn( buffer_ptr(mp4->buf), 4 ) );
981 buffer_consume(mp4->buf, 4);
982
983 my_hv_store( mp4->info, "minor_version", newSVuv( buffer_get_int(mp4->buf) ) );
984
985 mp4->rsize -= 8;
986
987 if (mp4->rsize % 4) {
988 // invalid ftyp
989 return 0;
990 }
991
992 while (mp4->rsize > 0) {
993 av_push( compatible_brands, newSVpvn( buffer_ptr(mp4->buf), 4 ) );
994 buffer_consume(mp4->buf, 4);
995 mp4->rsize -= 4;
996 }
997
998 my_hv_store( mp4->info, "compatible_brands", newRV_noinc( (SV *)compatible_brands ) );
999
1000 return 1;
1001 }
1002
1003 uint8_t
_mp4_parse_mvhd(mp4info * mp4)1004 _mp4_parse_mvhd(mp4info *mp4)
1005 {
1006 uint32_t timescale;
1007 uint8_t version;
1008
1009 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1010 return 0;
1011 }
1012
1013 version = buffer_get_char(mp4->buf);
1014 buffer_consume(mp4->buf, 3); // flags
1015
1016 if (version == 0) { // 32-bit values
1017 // Skip ctime and mtime
1018 buffer_consume(mp4->buf, 8);
1019
1020 timescale = buffer_get_int(mp4->buf);
1021 my_hv_store( mp4->info, "mv_timescale", newSVuv(timescale) );
1022
1023 my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1024 }
1025 else if (version == 1) { // 64-bit values
1026 // Skip ctime and mtime
1027 buffer_consume(mp4->buf, 16);
1028
1029 timescale = buffer_get_int(mp4->buf);
1030 my_hv_store( mp4->info, "mv_timescale", newSVuv(timescale) );
1031
1032 my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1033 }
1034 else {
1035 return 0;
1036 }
1037
1038 // Skip rest
1039 buffer_consume(mp4->buf, 80);
1040
1041 return 1;
1042 }
1043
1044 uint8_t
_mp4_parse_tkhd(mp4info * mp4)1045 _mp4_parse_tkhd(mp4info *mp4)
1046 {
1047 AV *tracks = (AV *)SvRV( *(my_hv_fetch(mp4->info, "tracks")) );
1048 HV *trackinfo = newHV();
1049 uint32_t id;
1050 double width;
1051 double height;
1052 uint8_t version;
1053
1054 uint32_t timescale = SvIV( *(my_hv_fetch(mp4->info, "mv_timescale")) );
1055
1056 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1057 return 0;
1058 }
1059
1060 version = buffer_get_char(mp4->buf);
1061 buffer_consume(mp4->buf, 3); // flags
1062
1063 // XXX DLNA Requirement [8.6.34.5]: For the default audio track, "Track_enabled"
1064 // must be set to the value of 1 in the "flags" field of Track Header Box of the track.
1065
1066 if (version == 0) { // 32-bit values
1067 // Skip ctime and mtime
1068 buffer_consume(mp4->buf, 8);
1069
1070 id = buffer_get_int(mp4->buf);
1071
1072 my_hv_store( trackinfo, "id", newSVuv(id) );
1073
1074 // Skip reserved
1075 buffer_consume(mp4->buf, 4);
1076
1077 my_hv_store( trackinfo, "duration", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1078 }
1079 else if (version == 1) { // 64-bit values
1080 // Skip ctime and mtime
1081 buffer_consume(mp4->buf, 16);
1082
1083 id = buffer_get_int(mp4->buf);
1084
1085 my_hv_store( trackinfo, "id", newSVuv(id) );
1086
1087 // Skip reserved
1088 buffer_consume(mp4->buf, 4);
1089
1090 my_hv_store( trackinfo, "duration", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1091 }
1092 else {
1093 return 0;
1094 }
1095
1096 // Skip reserved, layer, alternate_group, volume, reserved, matrix
1097 buffer_consume(mp4->buf, 52);
1098
1099 // width/height are fixed-point 16.16
1100 width = buffer_get_short(mp4->buf);
1101 width += buffer_get_short(mp4->buf) / 65536.;
1102 if (width > 0) {
1103 my_hv_store( trackinfo, "width", newSVnv(width) );
1104 }
1105
1106 height = buffer_get_short(mp4->buf);
1107 height += buffer_get_short(mp4->buf) / 65536.;
1108 if (height > 0) {
1109 my_hv_store( trackinfo, "height", newSVnv(height) );
1110 }
1111
1112 av_push( tracks, newRV_noinc( (SV *)trackinfo ) );
1113
1114 // Remember the current track we're dealing with
1115 mp4->current_track = id;
1116
1117 return 1;
1118 }
1119
1120 uint8_t
_mp4_parse_mdhd(mp4info * mp4)1121 _mp4_parse_mdhd(mp4info *mp4)
1122 {
1123 uint32_t timescale;
1124 uint8_t version;
1125
1126 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1127 return 0;
1128 }
1129
1130 version = buffer_get_char(mp4->buf);
1131 buffer_consume(mp4->buf, 3); // flags
1132
1133 if (version == 0) { // 32-bit values
1134 // Skip ctime and mtime
1135 buffer_consume(mp4->buf, 8);
1136
1137 timescale = buffer_get_int(mp4->buf);
1138 my_hv_store( mp4->info, "samplerate", newSVuv(timescale) );
1139
1140 // Skip duration, if have song_length_ms from mvhd
1141 if ( my_hv_exists( mp4->info, "song_length_ms" ) ) {
1142 buffer_consume(mp4->buf, 4);
1143 }
1144 else {
1145 my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1146 }
1147 }
1148 else if (version == 1) { // 64-bit values
1149 // Skip ctime and mtime
1150 buffer_consume(mp4->buf, 16);
1151
1152 timescale = buffer_get_int(mp4->buf);
1153 my_hv_store( mp4->info, "samplerate", newSVuv(timescale) );
1154
1155 // Skip duration, if have song_length_ms from mvhd
1156 if ( my_hv_exists( mp4->info, "song_length_ms" ) ) {
1157 buffer_consume(mp4->buf, 8);
1158 }
1159 else {
1160 my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1161 }
1162 }
1163 else {
1164 return 0;
1165 }
1166
1167 mp4->samplerate = timescale;
1168
1169 // Skip rest
1170 buffer_consume(mp4->buf, 4);
1171
1172 return 1;
1173 }
1174
1175 uint8_t
_mp4_parse_hdlr(mp4info * mp4)1176 _mp4_parse_hdlr(mp4info *mp4)
1177 {
1178 HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1179 SV *handler_name;
1180
1181 if (!trackinfo) {
1182 return 0;
1183 }
1184
1185 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1186 return 0;
1187 }
1188
1189 // Skip version, flags, pre_defined
1190 buffer_consume(mp4->buf, 8);
1191
1192 my_hv_store( trackinfo, "handler_type", newSVpvn( buffer_ptr(mp4->buf), 4 ) );
1193 buffer_consume(mp4->buf, 4);
1194
1195 // Skip reserved
1196 buffer_consume(mp4->buf, 12);
1197
1198 handler_name = newSVpv( buffer_ptr(mp4->buf), 0 );
1199 sv_utf8_decode(handler_name);
1200 my_hv_store( trackinfo, "handler_name", handler_name );
1201
1202 buffer_consume(mp4->buf, mp4->rsize - 24);
1203
1204 return 1;
1205 }
1206
1207 uint8_t
_mp4_parse_stsd(mp4info * mp4)1208 _mp4_parse_stsd(mp4info *mp4)
1209 {
1210 uint32_t entry_count;
1211
1212 if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1213 return 0;
1214 }
1215
1216 // Skip version/flags
1217 buffer_consume(mp4->buf, 4);
1218
1219 entry_count = buffer_get_int(mp4->buf);
1220
1221 return 1;
1222 }
1223
1224 uint8_t
_mp4_parse_mp4a(mp4info * mp4)1225 _mp4_parse_mp4a(mp4info *mp4)
1226 {
1227 HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1228
1229 if ( !_check_buf(mp4->infile, mp4->buf, 28, MP4_BLOCK_SIZE) ) {
1230 return 0;
1231 }
1232
1233 my_hv_store( trackinfo, "encoding", newSVpvn("mp4a", 4) );
1234
1235 // Skip reserved
1236 buffer_consume(mp4->buf, 16);
1237
1238 mp4->channels = buffer_get_short(mp4->buf);
1239 my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1240 my_hv_store( trackinfo, "bits_per_sample", newSVuv( buffer_get_short(mp4->buf) ) );
1241
1242 // Skip reserved
1243 buffer_consume(mp4->buf, 4);
1244
1245 // Skip bogus samplerate
1246 buffer_consume(mp4->buf, 2);
1247
1248 // Skip reserved
1249 buffer_consume(mp4->buf, 2);
1250
1251 return 1;
1252 }
1253
1254 uint8_t
_mp4_parse_esds(mp4info * mp4)1255 _mp4_parse_esds(mp4info *mp4)
1256 {
1257 HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1258 uint32_t len = 0;
1259 uint32_t avg_bitrate;
1260
1261 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1262 return 0;
1263 }
1264
1265 // Skip version/flags
1266 buffer_consume(mp4->buf, 4);
1267
1268 // Public docs on esds are hard to find, this is based on faad
1269 // and http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
1270
1271 // verify ES_DescrTag
1272 if (buffer_get_char(mp4->buf) == 0x03) {
1273 // read length
1274 if ( _mp4_descr_length(mp4->buf) < 5 + 15 ) {
1275 return 0;
1276 }
1277
1278 // skip 3 bytes
1279 buffer_consume(mp4->buf, 3);
1280 }
1281 else {
1282 // skip 2 bytes
1283 buffer_consume(mp4->buf, 2);
1284 }
1285
1286 // verify DecoderConfigDescrTab
1287 if (buffer_get_char(mp4->buf) != 0x04) {
1288 return 0;
1289 }
1290
1291 // read length
1292 if ( _mp4_descr_length(mp4->buf) < 13 ) {
1293 return 0;
1294 }
1295
1296 // XXX: map to string
1297 my_hv_store( trackinfo, "audio_type", newSVuv( buffer_get_char(mp4->buf) ) );
1298
1299 buffer_consume(mp4->buf, 4);
1300
1301 my_hv_store( trackinfo, "max_bitrate", newSVuv( buffer_get_int(mp4->buf) ) );
1302
1303 avg_bitrate = buffer_get_int(mp4->buf);
1304 if (avg_bitrate) {
1305 if ( my_hv_exists(mp4->info, "avg_bitrate") ) {
1306 // If there are multiple tracks, just add up the bitrates
1307 avg_bitrate += SvIV(*(my_hv_fetch(mp4->info, "avg_bitrate")));
1308 }
1309 my_hv_store( mp4->info, "avg_bitrate", newSVuv(avg_bitrate) );
1310 mp4->bitrate = avg_bitrate;
1311 }
1312
1313 // verify DecSpecificInfoTag
1314 if (buffer_get_char(mp4->buf) != 0x05) {
1315 return 0;
1316 }
1317
1318 // Read audio object type
1319 // 5 bits, if 0x1F, read 6 more bits
1320 len = _mp4_descr_length(mp4->buf);
1321 if (len > 0) {
1322 uint32_t aot;
1323
1324 len *= 8; // count the number of bits left
1325
1326 aot = buffer_get_bits(mp4->buf, 5);
1327 len -= 5;
1328
1329 if ( aot == 0x1F ) {
1330 aot = 32 + buffer_get_bits(mp4->buf, 6);
1331 len -= 6;
1332 }
1333
1334 // samplerate: 4 bits
1335 // if 0xF, samplerate is next 24 bits
1336 // else lookup in samplerate table
1337 {
1338 uint32_t samplerate = buffer_get_bits(mp4->buf, 4);
1339 len -= 4;
1340
1341 if (samplerate == 0xF) { // XXX need test file with 24-bit samplerate field
1342 samplerate = buffer_get_bits(mp4->buf, 24);
1343 len -= 24;
1344 }
1345 else {
1346 samplerate = samplerate_table[samplerate];
1347 }
1348
1349 // Channel configuration (4 bits)
1350 // XXX This is sometimes wrong (1 when it should be 2)
1351 mp4->channels = buffer_get_bits(mp4->buf, 4);
1352 my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1353 len -= 4;
1354
1355 if (aot == AAC_SLS) {
1356 // Read some SLS-specific config
1357 // bits per sample (3 bits) { 8, 16, 20, 24 }
1358 uint8_t bps = buffer_get_bits(mp4->buf, 3);
1359 len -= 3;
1360
1361 my_hv_store( trackinfo, "bits_per_sample", newSVuv( bps_table[bps] ) );
1362 }
1363 else if (aot == AAC_HE || aot == AAC_PS) {
1364 // Read extended samplerate info
1365 samplerate = buffer_get_bits(mp4->buf, 4);
1366 len -= 4;
1367 if (samplerate == 0xF) { // XXX need test file with 24-bit samplerate field
1368 samplerate = buffer_get_bits(mp4->buf, 24);
1369 len -= 24;
1370 }
1371 else {
1372 samplerate = samplerate_table[samplerate];
1373 }
1374 }
1375
1376 my_hv_store( trackinfo, "samplerate", newSVuv(samplerate) );
1377 mp4->samplerate = samplerate;
1378 }
1379
1380 my_hv_store( trackinfo, "audio_object_type", newSVuv(aot) );
1381 mp4->audio_object_type = aot;
1382
1383 // Skip rest of box
1384 buffer_get_bits(mp4->buf, len);
1385 }
1386
1387 // verify SL config descriptor type tag
1388 if (buffer_get_char(mp4->buf) != 0x06) {
1389 return 0;
1390 }
1391
1392 _mp4_descr_length(mp4->buf);
1393
1394 // verify SL value
1395 if (buffer_get_char(mp4->buf) != 0x02) {
1396 return 0;
1397 }
1398
1399 return 1;
1400 }
1401
1402 uint8_t
_mp4_parse_alac(mp4info * mp4)1403 _mp4_parse_alac(mp4info *mp4)
1404 {
1405 HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1406
1407 if ( !_check_buf(mp4->infile, mp4->buf, 28, MP4_BLOCK_SIZE) ) {
1408 return 0;
1409 }
1410
1411 my_hv_store( trackinfo, "encoding", newSVpvn("alac", 4) );
1412
1413 // Skip reserved
1414 buffer_consume(mp4->buf, 16);
1415
1416 mp4->channels = buffer_get_short(mp4->buf);
1417 my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1418 my_hv_store( trackinfo, "bits_per_sample", newSVuv( buffer_get_short(mp4->buf) ) );
1419
1420 // Skip reserved
1421 buffer_consume(mp4->buf, 4);
1422
1423 // Skip bogus samplerate
1424 buffer_consume(mp4->buf, 2);
1425
1426 // Skip reserved
1427 buffer_consume(mp4->buf, 2);
1428
1429 return 1;
1430 }
1431
1432 uint8_t
_mp4_parse_stts(mp4info * mp4)1433 _mp4_parse_stts(mp4info *mp4)
1434 {
1435 int i;
1436
1437 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1438 return 0;
1439 }
1440
1441 // Skip version/flags
1442 buffer_consume(mp4->buf, 4);
1443
1444 mp4->num_time_to_samples = buffer_get_int(mp4->buf);
1445 DEBUG_TRACE(" num_time_to_samples %d\n", mp4->num_time_to_samples);
1446
1447 New(0,
1448 mp4->time_to_sample,
1449 mp4->num_time_to_samples * sizeof(*mp4->time_to_sample),
1450 struct tts
1451 );
1452
1453 if ( !mp4->time_to_sample ) {
1454 PerlIO_printf(PerlIO_stderr(), "Unable to parse stts: too large\n");
1455 return 0;
1456 }
1457
1458 for (i = 0; i < mp4->num_time_to_samples; i++) {
1459 mp4->time_to_sample[i].sample_count = buffer_get_int(mp4->buf);
1460 mp4->time_to_sample[i].sample_duration = buffer_get_int(mp4->buf);
1461
1462 DEBUG_TRACE(
1463 " sample_count %d sample_duration %d\n",
1464 mp4->time_to_sample[i].sample_count,
1465 mp4->time_to_sample[i].sample_duration
1466 );
1467 }
1468
1469 return 1;
1470 }
1471
1472 uint8_t
_mp4_parse_stsc(mp4info * mp4)1473 _mp4_parse_stsc(mp4info *mp4)
1474 {
1475 int i;
1476
1477 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1478 return 0;
1479 }
1480
1481 // Skip version/flags
1482 buffer_consume(mp4->buf, 4);
1483
1484 mp4->num_sample_to_chunks = buffer_get_int(mp4->buf);
1485 DEBUG_TRACE(" num_sample_to_chunks %d\n", mp4->num_sample_to_chunks);
1486
1487 New(0,
1488 mp4->sample_to_chunk,
1489 mp4->num_sample_to_chunks * sizeof(*mp4->sample_to_chunk),
1490 struct stc
1491 );
1492
1493 if ( !mp4->sample_to_chunk ) {
1494 PerlIO_printf(PerlIO_stderr(), "Unable to parse stsc: too large\n");
1495 return 0;
1496 }
1497
1498 for (i = 0; i < mp4->num_sample_to_chunks; i++) {
1499 mp4->sample_to_chunk[i].first_chunk = buffer_get_int(mp4->buf);
1500 mp4->sample_to_chunk[i].samples_per_chunk = buffer_get_int(mp4->buf);
1501
1502 // Skip sample desc index
1503 buffer_consume(mp4->buf, 4);
1504
1505 DEBUG_TRACE(" first_chunk %d samples_per_chunk %d\n",
1506 mp4->sample_to_chunk[i].first_chunk,
1507 mp4->sample_to_chunk[i].samples_per_chunk
1508 );
1509 }
1510
1511 return 1;
1512 }
1513
1514 uint8_t
_mp4_parse_stsz(mp4info * mp4)1515 _mp4_parse_stsz(mp4info *mp4)
1516 {
1517 int i;
1518
1519 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1520 return 0;
1521 }
1522
1523 // Skip version/flags
1524 buffer_consume(mp4->buf, 4);
1525
1526 // Check sample size is 0
1527 if ( buffer_get_int(mp4->buf) != 0 ) {
1528 DEBUG_TRACE(" stsz uses fixed sample size\n");
1529 buffer_consume(mp4->buf, 4);
1530 return 1;
1531 }
1532
1533 mp4->num_sample_byte_sizes = buffer_get_int(mp4->buf);
1534
1535 DEBUG_TRACE(" num_sample_byte_sizes %d\n", mp4->num_sample_byte_sizes);
1536
1537 New(0,
1538 mp4->sample_byte_size,
1539 mp4->num_sample_byte_sizes * sizeof(*mp4->sample_byte_size),
1540 uint16_t
1541 );
1542
1543 if ( !mp4->sample_byte_size ) {
1544 PerlIO_printf(PerlIO_stderr(), "Unable to parse stsz: too large\n");
1545 return 0;
1546 }
1547
1548 for (i = 0; i < mp4->num_sample_byte_sizes; i++) {
1549 uint32_t v = buffer_get_int(mp4->buf);
1550
1551 if (v > 0x0000ffff) {
1552 DEBUG_TRACE("stsz[%d] > 65 kB (%ld)\n", i, (long)v);
1553 return 0;
1554 }
1555
1556 mp4->sample_byte_size[i] = v;
1557
1558 //DEBUG_TRACE(" sample_byte_size %d\n", v);
1559 }
1560
1561 return 1;
1562 }
1563
1564 uint8_t
_mp4_parse_stco(mp4info * mp4)1565 _mp4_parse_stco(mp4info *mp4)
1566 {
1567 int i;
1568
1569 if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1570 return 0;
1571 }
1572
1573 // Skip version/flags
1574 buffer_consume(mp4->buf, 4);
1575
1576 mp4->num_chunk_offsets = buffer_get_int(mp4->buf);
1577 DEBUG_TRACE(" num_chunk_offsets %d\n", mp4->num_chunk_offsets);
1578
1579 New(0,
1580 mp4->chunk_offset,
1581 mp4->num_chunk_offsets * sizeof(*mp4->chunk_offset),
1582 uint32_t
1583 );
1584
1585 if ( !mp4->chunk_offset ) {
1586 PerlIO_printf(PerlIO_stderr(), "Unable to parse stco: too large\n");
1587 return 0;
1588 }
1589
1590 for (i = 0; i < mp4->num_chunk_offsets; i++) {
1591 mp4->chunk_offset[i] = buffer_get_int(mp4->buf);
1592
1593 //DEBUG_TRACE(" chunk_offset %d\n", mp4->chunk_offset[i]);
1594 }
1595
1596 return 1;
1597 }
1598
1599 uint8_t
_mp4_parse_meta(mp4info * mp4)1600 _mp4_parse_meta(mp4info *mp4)
1601 {
1602 uint32_t hdlr_size;
1603 char type[5];
1604
1605 if ( !_check_buf(mp4->infile, mp4->buf, 12, MP4_BLOCK_SIZE) ) {
1606 return 0;
1607 }
1608
1609 // Skip version/flags
1610 buffer_consume(mp4->buf, 4);
1611
1612 // Parse/skip meta version of hdlr
1613 hdlr_size = buffer_get_int(mp4->buf);
1614 strncpy( type, (char *)buffer_ptr(mp4->buf), 4 );
1615 type[4] = '\0';
1616 buffer_consume(mp4->buf, 4);
1617
1618 if ( !FOURCC_EQ(type, "hdlr") ) {
1619 return 0;
1620 }
1621
1622 // Skip rest of hdlr
1623 if ( !_check_buf(mp4->infile, mp4->buf, hdlr_size - 8, MP4_BLOCK_SIZE) ) {
1624 return 0;
1625 }
1626
1627 buffer_consume(mp4->buf, hdlr_size - 8);
1628
1629 return 12 + hdlr_size - 8;
1630 }
1631
1632 uint8_t
_mp4_parse_ilst(mp4info * mp4)1633 _mp4_parse_ilst(mp4info *mp4)
1634 {
1635 while (mp4->rsize) {
1636 uint32_t size;
1637 char key[5];
1638
1639 if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1640 return 0;
1641 }
1642
1643 DEBUG_TRACE(" ilst rsize %llu\n", mp4->rsize);
1644
1645 // Read Apple annotation box
1646 size = buffer_get_int(mp4->buf);
1647 strncpy( key, (char *)buffer_ptr(mp4->buf), 4 );
1648 key[4] = '\0';
1649 buffer_consume(mp4->buf, 4);
1650
1651 DEBUG_TRACE(" %s size %d\n", key, size);
1652
1653 // Note: extra _check_buf calls in this function and other ilst functions
1654 // are to avoid reading in the full size of ilst in the case of large artwork
1655
1656 upcase(key);
1657
1658 if ( FOURCC_EQ(key, "----") ) {
1659 // user-specified key/value pair
1660 if ( !_mp4_parse_ilst_custom(mp4, size - 8) ) {
1661 return 0;
1662 }
1663 }
1664 else {
1665 uint32_t bsize;
1666
1667 // Ensure we have 8 bytes
1668 if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1669 return 0;
1670 }
1671
1672 // Verify data box
1673 bsize = buffer_get_int(mp4->buf);
1674
1675 DEBUG_TRACE(" box size %d\n", bsize);
1676
1677 // Sanity check for bad data size
1678 if ( bsize <= size - 8 ) {
1679 SV *skey;
1680
1681 char *bptr = buffer_ptr(mp4->buf);
1682 if ( !FOURCC_EQ(bptr, "data") ) {
1683 return 0;
1684 }
1685
1686 buffer_consume(mp4->buf, 4);
1687
1688 skey = newSVpv(key, 0);
1689
1690 if ( !_mp4_parse_ilst_data(mp4, bsize - 8, skey) ) {
1691 SvREFCNT_dec(skey);
1692 return 0;
1693 }
1694
1695 SvREFCNT_dec(skey);
1696
1697 // XXX: bug 14476, files with multiple COVR images aren't handled here, just skipped for now
1698 if ( bsize < size - 8 ) {
1699 DEBUG_TRACE(" skipping rest of box, %d\n", size - 8 - bsize );
1700 _mp4_skip(mp4, size - 8 - bsize);
1701 }
1702 }
1703 else {
1704 DEBUG_TRACE(" invalid data size %d, skipping value\n", bsize);
1705 _mp4_skip(mp4, size - 12);
1706 }
1707 }
1708
1709 mp4->rsize -= size;
1710 }
1711
1712 return 1;
1713 }
1714
1715 uint8_t
_mp4_parse_ilst_data(mp4info * mp4,uint32_t size,SV * key)1716 _mp4_parse_ilst_data(mp4info *mp4, uint32_t size, SV *key)
1717 {
1718 uint32_t flags;
1719 unsigned char *ckey;
1720 SV *value;
1721
1722 ckey = (unsigned char *)SvPVX(key);
1723 if ( FOURCC_EQ(ckey, "COVR") && _env_true("AUDIO_SCAN_NO_ARTWORK") ) {
1724 // Skip artwork if requested and avoid the memory cost
1725 value = newSVuv(size - 8);
1726
1727 my_hv_store( mp4->tags, "COVR_offset", newSVuv(mp4->audio_offset + (mp4->size - mp4->rsize) + 24) );
1728
1729 _mp4_skip(mp4, size);
1730 }
1731 else {
1732 // Read the full ilst value
1733 if ( !_check_buf(mp4->infile, mp4->buf, size, MP4_BLOCK_SIZE) ) {
1734 return 0;
1735 }
1736
1737 // Version(0) + Flags
1738 flags = buffer_get_int(mp4->buf);
1739
1740 // Skip reserved
1741 buffer_consume(mp4->buf, 4);
1742
1743 DEBUG_TRACE(" flags %d\n", flags);
1744
1745 if ( !flags || flags == 21 ) {
1746 if ( FOURCC_EQ( SvPVX(key), "TRKN" ) || FOURCC_EQ( SvPVX(key), "DISK" ) ) {
1747 // Special case trkn, disk (pair of 16-bit ints)
1748 uint16_t num = 0;
1749 uint16_t total = 0;
1750
1751 buffer_consume(mp4->buf, 2); // padding
1752
1753 num = buffer_get_short(mp4->buf);
1754
1755 // Total may not always be present
1756 if (size > 12) {
1757 total = buffer_get_short(mp4->buf);
1758 buffer_consume(mp4->buf, size - 14); // optional padding
1759 }
1760
1761 DEBUG_TRACE(" %d/%d\n", num, total);
1762
1763 if (total) {
1764 my_hv_store_ent( mp4->tags, key, newSVpvf( "%d/%d", num, total ) );
1765 }
1766 else if (num) {
1767 my_hv_store_ent( mp4->tags, key, newSVuv(num) );
1768 }
1769
1770 return 1;
1771 }
1772 else if ( FOURCC_EQ( SvPVX(key), "GNRE" ) ) {
1773 // Special case genre, 16-bit int as id3 genre code
1774 char const *genre_string;
1775 uint16_t genre_num = buffer_get_short(mp4->buf);
1776
1777 if (genre_num > 0 && genre_num < NGENRES + 1) {
1778 genre_string = _id3_genre_index(genre_num - 1);
1779 my_hv_store_ent( mp4->tags, key, newSVpv( genre_string, 0 ) );
1780 }
1781
1782 return 1;
1783 }
1784 else {
1785 // Other binary type, try to guess type based on size
1786 uint32_t dsize = size - 8;
1787
1788 if (dsize == 1) {
1789 value = newSVuv( buffer_get_char(mp4->buf) );
1790 }
1791 else if (dsize == 2) {
1792 value = newSVuv( buffer_get_short(mp4->buf) );
1793 }
1794 else if (dsize == 4) {
1795 value = newSVuv( buffer_get_int(mp4->buf) );
1796 }
1797 else if (dsize == 8) {
1798 value = newSVuv( buffer_get_int64(mp4->buf) );
1799 }
1800 else {
1801 value = newSVpvn( buffer_ptr(mp4->buf), dsize );
1802 buffer_consume(mp4->buf, dsize);
1803 }
1804 }
1805 }
1806 else { // text data
1807 value = newSVpvn( buffer_ptr(mp4->buf), size - 8 );
1808 sv_utf8_decode(value);
1809
1810 // strip copyright symbol 0xA9 out of key
1811 if ( ckey[0] == 0xA9 ) {
1812 ckey++;
1813 }
1814
1815 DEBUG_TRACE(" %s = %s\n", ckey, SvPVX(value));
1816
1817 buffer_consume(mp4->buf, size - 8);
1818 }
1819 }
1820
1821 // if key exists, create array
1822 if ( my_hv_exists( mp4->tags, (char *)ckey ) ) {
1823 SV **entry = my_hv_fetch( mp4->tags, (char *)ckey );
1824 if (entry != NULL) {
1825 if ( SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVAV ) {
1826 av_push( (AV *)SvRV(*entry), value );
1827 }
1828 else {
1829 // A non-array entry, convert to array.
1830 AV *ref = newAV();
1831 av_push( ref, newSVsv(*entry) );
1832 av_push( ref, value );
1833 my_hv_store( mp4->tags, (char *)ckey, newRV_noinc( (SV*)ref ) );
1834 }
1835 }
1836 }
1837 else {
1838 my_hv_store( mp4->tags, (char *)ckey, value );
1839 }
1840
1841 return 1;
1842 }
1843
1844 uint8_t
_mp4_parse_ilst_custom(mp4info * mp4,uint32_t size)1845 _mp4_parse_ilst_custom(mp4info *mp4, uint32_t size)
1846 {
1847 SV *key = NULL;
1848
1849 while (size) {
1850 char type[5];
1851 uint32_t bsize;
1852
1853 // Ensure we have 8 bytes to get the size and type
1854 if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1855 return 0;
1856 }
1857
1858 // Read box
1859 bsize = buffer_get_int(mp4->buf);
1860 strncpy( type, (char *)buffer_ptr(mp4->buf), 4 );
1861 type[4] = '\0';
1862 buffer_consume(mp4->buf, 4);
1863
1864 DEBUG_TRACE(" %s size %d\n", type, bsize);
1865
1866 if ( FOURCC_EQ(type, "name") ) {
1867 // Ensure we have bsize bytes
1868 if ( !_check_buf(mp4->infile, mp4->buf, bsize, MP4_BLOCK_SIZE) ) {
1869 return 0;
1870 }
1871
1872 buffer_consume(mp4->buf, 4); // padding
1873 key = newSVpvn( buffer_ptr(mp4->buf), bsize - 12);
1874 sv_utf8_decode(key);
1875 upcase(SvPVX(key));
1876 buffer_consume(mp4->buf, bsize - 12);
1877
1878 DEBUG_TRACE(" %s\n", SvPVX(key));
1879 }
1880 else if ( FOURCC_EQ(type, "data") ) {
1881 if (!key) {
1882 // No key yet, data is out of order
1883 return 0;
1884 }
1885
1886 if ( !_mp4_parse_ilst_data(mp4, bsize - 8, key) ) {
1887 SvREFCNT_dec(key);
1888 return 0;
1889 }
1890 }
1891 else {
1892 // skip (mean, or other boxes)
1893 if ( !_check_buf(mp4->infile, mp4->buf, bsize - 8, MP4_BLOCK_SIZE) ) {
1894 return 0;
1895 }
1896
1897 buffer_consume(mp4->buf, bsize - 8);
1898 }
1899
1900 size -= bsize;
1901 }
1902
1903 SvREFCNT_dec(key);
1904
1905 return 1;
1906 }
1907
1908 HV *
_mp4_get_current_trackinfo(mp4info * mp4)1909 _mp4_get_current_trackinfo(mp4info *mp4)
1910 {
1911 // Return the trackinfo hash for track id == mp4->current_track
1912 AV *tracks;
1913 HV *trackinfo;
1914 int i;
1915
1916 SV **entry = my_hv_fetch(mp4->info, "tracks");
1917 if (entry != NULL) {
1918 tracks = (AV *)SvRV(*entry);
1919 }
1920 else {
1921 return NULL;
1922 }
1923
1924 // Find entry for this stream number
1925 for (i = 0; av_len(tracks) >= 0 && i <= av_len(tracks); i++) {
1926 SV **info = av_fetch(tracks, i, 0);
1927 if (info != NULL) {
1928 SV **tid;
1929
1930 trackinfo = (HV *)SvRV(*info);
1931 tid = my_hv_fetch( trackinfo, "id" );
1932 if (tid != NULL) {
1933 if ( SvIV(*tid) == mp4->current_track ) {
1934 return trackinfo;
1935 }
1936 }
1937 }
1938 }
1939
1940 return NULL;
1941 }
1942
1943 uint32_t
_mp4_descr_length(Buffer * buf)1944 _mp4_descr_length(Buffer *buf)
1945 {
1946 uint8_t b;
1947 uint8_t num_bytes = 0;
1948 uint32_t length = 0;
1949
1950 do {
1951 b = buffer_get_char(buf);
1952 num_bytes++;
1953 length = (length << 7) | (b & 0x7f);
1954 } while ( (b & 0x80) && num_bytes < 4 );
1955
1956 return length;
1957 }
1958
1959 void
_mp4_skip(mp4info * mp4,uint32_t size)1960 _mp4_skip(mp4info *mp4, uint32_t size)
1961 {
1962 if ( buffer_len(mp4->buf) >= size ) {
1963 //buffer_dump(mp4->buf, size);
1964 buffer_consume(mp4->buf, size);
1965
1966 DEBUG_TRACE(" skipped buffer data size %d\n", size);
1967 }
1968 else {
1969 PerlIO_seek(mp4->infile, size - buffer_len(mp4->buf), SEEK_CUR);
1970 buffer_clear(mp4->buf);
1971
1972 DEBUG_TRACE(" seeked past %d bytes to %d\n", size, (int)PerlIO_tell(mp4->infile));
1973 }
1974 }
1975
1976 uint32_t
_mp4_samples_in_chunk(mp4info * mp4,uint32_t chunk)1977 _mp4_samples_in_chunk(mp4info *mp4, uint32_t chunk)
1978 {
1979 int i;
1980
1981 for (i = mp4->num_sample_to_chunks - 1; i >= 0; i--) {
1982 if (mp4->sample_to_chunk[i].first_chunk <= chunk) {
1983 return mp4->sample_to_chunk[i].samples_per_chunk;
1984 }
1985 }
1986
1987 return mp4->sample_to_chunk[0].samples_per_chunk;
1988 }
1989
1990 uint32_t
_mp4_total_samples(mp4info * mp4)1991 _mp4_total_samples(mp4info *mp4)
1992 {
1993 int i;
1994 uint32_t total = 0;
1995
1996 for (i = 0; i < mp4->num_time_to_samples; i++) {
1997 total += mp4->time_to_sample[i].sample_count;
1998 }
1999
2000 return total;
2001 }
2002
2003 uint32_t
_mp4_get_sample_duration(mp4info * mp4,uint32_t sample)2004 _mp4_get_sample_duration(mp4info *mp4, uint32_t sample)
2005 {
2006 int i;
2007 uint32_t co = 0;
2008
2009 for (i = 0; i < mp4->num_time_to_samples; i++) {
2010 uint32_t delta = mp4->time_to_sample[i].sample_count;
2011 if (sample < co + delta) {
2012 return mp4->time_to_sample[i].sample_duration;
2013 }
2014
2015 co += delta;
2016 }
2017
2018 return 0;
2019 }
2020