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