File Coverage

src/mp4.c
Criterion Covered Total %
statement 717 929 77.1
branch 614 1020 60.2
condition n/a
subroutine n/a
pod n/a
total 1331 1949 68.2


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