File Coverage

src/mp4.c
Criterion Covered Total %
statement 719 940 76.4
branch 613 1008 60.8
condition n/a
subroutine n/a
pod n/a
total 1332 1948 68.3


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             off_t
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           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           uint32_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           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           if (
103 4 50         !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           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 0           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           uint32_t song_length_ms = SvIV(*entry);
516 4           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 11           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 0           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 0           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 0           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 0           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 0           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 4           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, 8, 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 598           mp4->hsize = 8;
682             // Check for 64-bit size
683 598 50         if (size == 1) {
684 0 0         if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
685 0           return 0;
686             }
687 0           size = buffer_get_int64(mp4->buf);
688 0           mp4->hsize = 16;
689             }
690              
691 598 50         if (size == 0) {
692             // XXX: box extends to end of file
693             /*nothing to do*/ ; // rsize=size=0
694             }
695 598 50         else if (size < mp4->hsize) {
696 0           PerlIO_printf(PerlIO_stderr(), "Invalid box size in: %s\n", mp4->file);
697 0           return 0;
698             }
699             else {
700             // set size of the remainder of the box
701 598           mp4->rsize = size - mp4->hsize;
702             }
703              
704 598           mp4->size = size;
705              
706             DEBUG_TRACE("%s size %llu\n", type, size);
707              
708 598 100         if (size == mp4->hsize) {
709 1           PerlIO_printf(PerlIO_stderr(), "Ignoring empty box of type %s in: %s\n", type, mp4->file);
710 1           return size;
711             }
712              
713 597 100         if (mp4->seekhdr) {
714             // Copy and adjust header if seeking
715             char tmp_size[4];
716              
717 103           if (
718 103 100         FOURCC_EQ(type, "moov")
    100          
    50          
    50          
    50          
719 99 100         || FOURCC_EQ(type, "trak")
    100          
    50          
    50          
    50          
720 95 100         || FOURCC_EQ(type, "mdia")
    100          
    50          
    50          
    100          
721 91 100         || FOURCC_EQ(type, "minf")
    100          
    50          
    50          
    50          
722 87 100         || FOURCC_EQ(type, "stbl")
    100          
    50          
    50          
    100          
723             ) {
724             // Container box, adjust size
725 20           put_u32(tmp_size, size - (mp4->old_st_size - mp4->new_st_size));
726             DEBUG_TRACE(" Box is parent of st*, changed size to %llu\n", size - (mp4->old_st_size - mp4->new_st_size));
727 20           sv_catpvn( mp4->seekhdr, tmp_size, 4 );
728 20           sv_catpvn( mp4->seekhdr, type, 4 );
729             }
730             // Replace st* boxes with our new versions
731 83 100         else if ( FOURCC_EQ(type, "stts") ) {
    100          
    50          
    50          
    100          
732             DEBUG_TRACE("adding new stts of size %ld\n", sv_len(mp4->new_stts));
733 4           sv_catsv( mp4->seekhdr, mp4->new_stts );
734             }
735 79 100         else if ( FOURCC_EQ(type, "stsc") ) {
    100          
    50          
    50          
    100          
736             DEBUG_TRACE("adding new stsc of size %ld\n", sv_len(mp4->new_stsc));
737 4           sv_catsv( mp4->seekhdr, mp4->new_stsc );
738             }
739 75 100         else if ( FOURCC_EQ(type, "stsz") ) {
    100          
    50          
    50          
    100          
740             DEBUG_TRACE("adding new stsz of size %ld\n", sv_len(mp4->new_stsz));
741 4           sv_catsv( mp4->seekhdr, mp4->new_stsz );
742             }
743 71 100         else if ( FOURCC_EQ(type, "stco") ) {
    100          
    50          
    50          
    100          
744             DEBUG_TRACE("adding new stco of size %ld\n", sv_len(mp4->new_stco));
745 4           sv_catsv( mp4->seekhdr, mp4->new_stco );
746             }
747             else {
748             // Normal box, copy it
749 67           put_u32(tmp_size, size);
750 67           sv_catpvn( mp4->seekhdr, tmp_size, 4 );
751 67           sv_catpvn( mp4->seekhdr, type, 4 );
752              
753             // stsd is special and contains real bytes and is also a container
754 67 100         if ( FOURCC_EQ(type, "stsd") ) {
    100          
    50          
    50          
    50          
755 4           sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), 8 );
756             }
757              
758             // mp4a is special, ugh
759 63 100         else if ( FOURCC_EQ(type, "mp4a") ) {
    100          
    50          
    50          
    50          
760 3           sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), 28 );
761             }
762              
763             // and so is meta
764 60 100         else if ( FOURCC_EQ(type, "meta") ) {
    100          
    50          
    50          
    50          
765 4           sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), mp4->meta_size );
766             }
767              
768             // Copy contents unless it's a container
769 56           else if (
770 56 100         !FOURCC_EQ(type, "edts")
    50          
    0          
    0          
    0          
771 56 100         && !FOURCC_EQ(type, "dinf")
    100          
    50          
    50          
    50          
772 52 100         && !FOURCC_EQ(type, "udta")
    50          
    50          
    50          
    50          
773 48 100         && !FOURCC_EQ(type, "mdat")
    100          
    50          
    50          
    100          
774             ) {
775 44 50         if ( !_check_buf(mp4->infile, mp4->buf, size - 8, MP4_BLOCK_SIZE) ) {
776 0           return 0;
777             }
778              
779             // XXX find a way to skip udta completely when rewriting seek header
780             // to avoid useless copying of artwork. Will require adjusting offsets
781             // differently.
782              
783 44           sv_catpvn( mp4->seekhdr, (char *)buffer_ptr(mp4->buf), size - 8 );
784             }
785             }
786              
787             // XXX should probably return size here and avoid reading info a second time
788             // or move the header copying code to somewhere else
789             }
790              
791 597 100         if ( FOURCC_EQ(type, "ftyp") ) {
    100          
    50          
    50          
    50          
792 21 50         if ( !_mp4_parse_ftyp(mp4) ) {
793 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad ftyp box): %s\n", mp4->file);
794 0           return 0;
795             }
796             }
797 576           else if (
798 576 100         FOURCC_EQ(type, "moov")
    100          
    50          
    50          
    50          
799 555 100         || FOURCC_EQ(type, "edts")
    50          
    0          
    0          
    0          
800 555 100         || FOURCC_EQ(type, "mdia")
    100          
    50          
    50          
    100          
801 531 100         || FOURCC_EQ(type, "minf")
    100          
    50          
    50          
    50          
802 507 100         || FOURCC_EQ(type, "dinf")
    100          
    50          
    50          
    50          
803 483 100         || FOURCC_EQ(type, "stbl")
    100          
    50          
    50          
    100          
804 459 100         || FOURCC_EQ(type, "udta")
    50          
    50          
    50          
    50          
805 438 100         || FOURCC_EQ(type, "trak")
    100          
    50          
    50          
    100          
806             ) {
807             // These boxes are containers for nested boxes, return only the fact that
808             // we read the header size of the container. Read the nested box the next call to this fn.
809 162           size = mp4->hsize;
810              
811 162 100         if ( FOURCC_EQ(type, "trak") ) {
    50          
    50          
    50          
    50          
812             // Also a container, but we need to increment track_count too
813 24           mp4->track_count++;
814             }
815             }
816 414 100         else if ( FOURCC_EQ(type, "mvhd") ) {
    100          
    50          
    50          
    50          
817 21           mp4->seen_moov = 1;
818              
819 21 50         if ( !_mp4_parse_mvhd(mp4) ) {
820 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mvhd box): %s\n", mp4->file);
821 0           return 0;
822             }
823             }
824 393 100         else if ( FOURCC_EQ(type, "tkhd") ) {
    100          
    50          
    50          
    50          
825 24 50         if ( !_mp4_parse_tkhd(mp4) ) {
826 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad tkhd box): %s\n", mp4->file);
827 0           return 0;
828             }
829             }
830 369 100         else if ( FOURCC_EQ(type, "mdhd") ) {
    100          
    50          
    50          
    100          
831 24 50         if ( !_mp4_parse_mdhd(mp4) ) {
832 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mdhd box): %s\n", mp4->file);
833 0           return 0;
834             }
835             }
836 345 100         else if ( FOURCC_EQ(type, "hdlr") ) {
    100          
    50          
    50          
    50          
837 24 50         if ( !_mp4_parse_hdlr(mp4) ) {
838 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad hdlr box): %s\n", mp4->file);
839 0           return 0;
840             }
841             }
842 321 100         else if ( FOURCC_EQ(type, "stsd") ) {
    100          
    50          
    50          
    100          
843 24 50         if ( !_mp4_parse_stsd(mp4) ) {
844 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsd box): %s\n", mp4->file);
845 0           return 0;
846             }
847              
848             // stsd is a special real box + container, count only the real bytes (8)
849 24           size = 8 + mp4->hsize;
850             }
851 297 100         else if ( FOURCC_EQ(type, "mp4a") ) {
    100          
    50          
    50          
    50          
852 20 50         if ( !_mp4_parse_mp4a(mp4) ) {
853 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad mp4a box): %s\n", mp4->file);
854 0           return 0;
855             }
856              
857             // mp4a is a special real box + container, count only the real bytes (28)
858 20           size = 28 + mp4->hsize;
859             }
860 277 100         else if ( FOURCC_EQ(type, "alac") ) {
    50          
    50          
    50          
    50          
861 3 50         if ( !_mp4_parse_alac(mp4) ) {
862 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad alac box): %s\n", mp4->file);
863 0           return 0;
864             }
865              
866             // skip rest (alac description)
867 3           mp4->rsize -= 28;
868 3           skip = 1;
869             }
870 274 100         else if ( FOURCC_EQ(type, "drms") ) {
    50          
    50          
    50          
    50          
871             // Mark encoding
872 0           HV *trackinfo = _mp4_get_current_trackinfo(mp4);
873              
874 0           my_hv_store( trackinfo, "encoding", newSVpvn("drms", 4) );
875              
876             // Skip rest
877 0           skip = 1;
878             }
879 274 100         else if ( FOURCC_EQ(type, "esds") ) {
    50          
    50          
    50          
    50          
880 20 50         if ( !_mp4_parse_esds(mp4) ) {
881 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad esds box): %s\n", mp4->file);
882 0           return 0;
883             }
884             }
885 254 100         else if ( FOURCC_EQ(type, "stts") ) {
    100          
    50          
    50          
    100          
886 24 100         if ( mp4->seeking && mp4->track_count == 1 ) {
    100          
887 9 50         if ( !_mp4_parse_stts(mp4) ) {
888 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stts box): %s\n", mp4->file);
889 0           return 0;
890             }
891 9           mp4->old_st_size += size;
892             }
893             else {
894 15           skip = 1;
895             }
896             }
897 230 100         else if ( FOURCC_EQ(type, "stsc") ) {
    100          
    50          
    50          
    100          
898 24 100         if ( mp4->seeking && mp4->track_count == 1 ) {
    100          
899 9 50         if ( !_mp4_parse_stsc(mp4) ) {
900 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsc box): %s\n", mp4->file);
901 0           return 0;
902             }
903 9           mp4->old_st_size += size;
904             }
905             else {
906 15           skip = 1;
907             }
908             }
909 206 100         else if ( FOURCC_EQ(type, "stsz") ) {
    100          
    50          
    50          
    100          
910 24 100         if ( mp4->seeking && mp4->track_count == 1 ) {
    100          
911 9 50         if ( !_mp4_parse_stsz(mp4) ) {
912 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stsz box): %s\n", mp4->file);
913 0           return 0;
914             }
915 9           mp4->old_st_size += size;
916             }
917             else {
918 15           skip = 1;
919             }
920             }
921 182 100         else if ( FOURCC_EQ(type, "stco") ) {
    100          
    50          
    50          
    50          
922 24 100         if ( mp4->seeking && mp4->track_count == 1 ) {
    100          
923 9 50         if ( !_mp4_parse_stco(mp4) ) {
924 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad stco box): %s\n", mp4->file);
925 0           return 0;
926             }
927 9           mp4->old_st_size += size;
928             }
929             else {
930 15           skip = 1;
931             }
932             }
933 158 100         else if ( FOURCC_EQ(type, "meta") ) {
    100          
    50          
    50          
    50          
934 20           uint8_t meta_size = _mp4_parse_meta(mp4);
935 20 50         if ( !meta_size ) {
936 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad meta box): %s\n", mp4->file);
937 0           return 0;
938             }
939              
940 20           mp4->meta_size = meta_size;
941              
942             // meta is a special real box + container, count only the real bytes
943 20           size = meta_size + mp4->hsize;
944             }
945 138 100         else if ( FOURCC_EQ(type, "ilst") ) {
    100          
    50          
    50          
    50          
946 20 50         if ( !_mp4_parse_ilst(mp4) ) {
947 0           PerlIO_printf(PerlIO_stderr(), "Invalid MP4 file (bad ilst box): %s\n", mp4->file);
948 0           return 0;
949             }
950             }
951 118 100         else if ( FOURCC_EQ(type, "mdat") ) {
    50          
    50          
    50          
    50          
952             // Audio data here, there may be boxes after mdat, so we have to skip it
953 20           skip = 1;
954              
955             // If we haven't seen moov yet, set a flag so we can print a warning
956             // or handle it some other way
957 20 100         if ( !mp4->seen_moov ) {
958 2           my_hv_store( mp4->info, "leading_mdat", newSVuv(1) );
959 2           mp4->dlna_invalid = 1; // DLNA 8.6.34.8, moov must be before mdat
960             }
961              
962             // Record audio offset and length
963 20           my_hv_store( mp4->info, "audio_offset", newSVuv(mp4->audio_offset) );
964 20           my_hv_store( mp4->info, "audio_size", newSVuv(size) );
965 20           mp4->audio_size = size;
966             }
967             else {
968             DEBUG_TRACE(" Unhandled box, skipping\n");
969 98           skip = 1;
970             }
971              
972 597 100         if (skip) {
973 181           _mp4_skip(mp4, mp4->rsize);
974             }
975              
976 597           return size;
977             }
978              
979             uint8_t
980 21           _mp4_parse_ftyp(mp4info *mp4)
981             {
982 21           AV *compatible_brands = newAV();
983              
984 21 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
985 0           return 0;
986             }
987              
988 21           my_hv_store( mp4->info, "major_brand", newSVpvn( buffer_ptr(mp4->buf), 4 ) );
989 21           buffer_consume(mp4->buf, 4);
990              
991 21           my_hv_store( mp4->info, "minor_version", newSVuv( buffer_get_int(mp4->buf) ) );
992              
993 21           mp4->rsize -= 8;
994              
995 21 50         if (mp4->rsize % 4) {
996             // invalid ftyp
997 0           return 0;
998             }
999              
1000 95 100         while (mp4->rsize > 0) {
1001 74           av_push( compatible_brands, newSVpvn( buffer_ptr(mp4->buf), 4 ) );
1002 74           buffer_consume(mp4->buf, 4);
1003 74           mp4->rsize -= 4;
1004             }
1005              
1006 21           my_hv_store( mp4->info, "compatible_brands", newRV_noinc( (SV *)compatible_brands ) );
1007              
1008 21           return 1;
1009             }
1010              
1011             uint8_t
1012 21           _mp4_parse_mvhd(mp4info *mp4)
1013             {
1014             uint32_t timescale;
1015             uint8_t version;
1016              
1017 21 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1018 0           return 0;
1019             }
1020              
1021 21           version = buffer_get_char(mp4->buf);
1022 21           buffer_consume(mp4->buf, 3); // flags
1023              
1024 21 50         if (version == 0) { // 32-bit values
1025             // Skip ctime and mtime
1026 21           buffer_consume(mp4->buf, 8);
1027              
1028 21           timescale = buffer_get_int(mp4->buf);
1029 21           my_hv_store( mp4->info, "mv_timescale", newSVuv(timescale) );
1030              
1031 21           my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1032             }
1033 0 0         else if (version == 1) { // 64-bit values
1034             // Skip ctime and mtime
1035 0           buffer_consume(mp4->buf, 16);
1036              
1037 0           timescale = buffer_get_int(mp4->buf);
1038 0           my_hv_store( mp4->info, "mv_timescale", newSVuv(timescale) );
1039              
1040 0           my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1041             }
1042             else {
1043 0           return 0;
1044             }
1045              
1046             // Skip rest
1047 21           buffer_consume(mp4->buf, 80);
1048              
1049 21           return 1;
1050             }
1051              
1052             uint8_t
1053 24           _mp4_parse_tkhd(mp4info *mp4)
1054             {
1055 24           AV *tracks = (AV *)SvRV( *(my_hv_fetch(mp4->info, "tracks")) );
1056 24           HV *trackinfo = newHV();
1057             uint32_t id;
1058             double width;
1059             double height;
1060             uint8_t version;
1061              
1062 24           uint32_t timescale = SvIV( *(my_hv_fetch(mp4->info, "mv_timescale")) );
1063              
1064 24 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1065 0           return 0;
1066             }
1067              
1068 24           version = buffer_get_char(mp4->buf);
1069 24           buffer_consume(mp4->buf, 3); // flags
1070              
1071             // XXX DLNA Requirement [8.6.34.5]: For the default audio track, "Track_enabled"
1072             // must be set to the value of 1 in the "flags" field of Track Header Box of the track.
1073              
1074 24 50         if (version == 0) { // 32-bit values
1075             // Skip ctime and mtime
1076 24           buffer_consume(mp4->buf, 8);
1077              
1078 24           id = buffer_get_int(mp4->buf);
1079              
1080 24           my_hv_store( trackinfo, "id", newSVuv(id) );
1081              
1082             // Skip reserved
1083 24           buffer_consume(mp4->buf, 4);
1084              
1085 24           my_hv_store( trackinfo, "duration", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1086             }
1087 0 0         else if (version == 1) { // 64-bit values
1088             // Skip ctime and mtime
1089 0           buffer_consume(mp4->buf, 16);
1090              
1091 0           id = buffer_get_int(mp4->buf);
1092              
1093 0           my_hv_store( trackinfo, "id", newSVuv(id) );
1094              
1095             // Skip reserved
1096 0           buffer_consume(mp4->buf, 4);
1097              
1098 0           my_hv_store( trackinfo, "duration", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1099             }
1100             else {
1101 0           return 0;
1102             }
1103              
1104             // Skip reserved, layer, alternate_group, volume, reserved, matrix
1105 24           buffer_consume(mp4->buf, 52);
1106              
1107             // width/height are fixed-point 16.16
1108 24           width = buffer_get_short(mp4->buf);
1109 24           width += buffer_get_short(mp4->buf) / 65536.;
1110 24 50         if (width > 0) {
1111 0           my_hv_store( trackinfo, "width", newSVnv(width) );
1112             }
1113              
1114 24           height = buffer_get_short(mp4->buf);
1115 24           height += buffer_get_short(mp4->buf) / 65536.;
1116 24 50         if (height > 0) {
1117 0           my_hv_store( trackinfo, "height", newSVnv(height) );
1118             }
1119              
1120 24           av_push( tracks, newRV_noinc( (SV *)trackinfo ) );
1121              
1122             // Remember the current track we're dealing with
1123 24           mp4->current_track = id;
1124              
1125 24           return 1;
1126             }
1127              
1128             uint8_t
1129 24           _mp4_parse_mdhd(mp4info *mp4)
1130             {
1131             uint32_t timescale;
1132             uint8_t version;
1133              
1134 24 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1135 0           return 0;
1136             }
1137              
1138 24           version = buffer_get_char(mp4->buf);
1139 24           buffer_consume(mp4->buf, 3); // flags
1140              
1141 24 50         if (version == 0) { // 32-bit values
1142             // Skip ctime and mtime
1143 24           buffer_consume(mp4->buf, 8);
1144              
1145 24           timescale = buffer_get_int(mp4->buf);
1146 24           my_hv_store( mp4->info, "samplerate", newSVuv(timescale) );
1147              
1148             // Skip duration, if have song_length_ms from mvhd
1149 24 50         if ( my_hv_exists( mp4->info, "song_length_ms" ) ) {
1150 24           buffer_consume(mp4->buf, 4);
1151             }
1152             else {
1153 0           my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1154             }
1155             }
1156 0 0         else if (version == 1) { // 64-bit values
1157             // Skip ctime and mtime
1158 0           buffer_consume(mp4->buf, 16);
1159              
1160 0           timescale = buffer_get_int(mp4->buf);
1161 0           my_hv_store( mp4->info, "samplerate", newSVuv(timescale) );
1162              
1163             // Skip duration, if have song_length_ms from mvhd
1164 0 0         if ( my_hv_exists( mp4->info, "song_length_ms" ) ) {
1165 0           buffer_consume(mp4->buf, 8);
1166             }
1167             else {
1168 0           my_hv_store( mp4->info, "song_length_ms", newSVuv( (buffer_get_int64(mp4->buf) * 1.0 / timescale ) * 1000 ) );
1169             }
1170             }
1171             else {
1172 0           return 0;
1173             }
1174              
1175 24           mp4->samplerate = timescale;
1176              
1177             // Skip rest
1178 24           buffer_consume(mp4->buf, 4);
1179              
1180 24           return 1;
1181             }
1182              
1183             uint8_t
1184 24           _mp4_parse_hdlr(mp4info *mp4)
1185             {
1186 24           HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1187             SV *handler_name;
1188              
1189 24 50         if (!trackinfo) {
1190 0           return 0;
1191             }
1192              
1193 24 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1194 0           return 0;
1195             }
1196              
1197             // Skip version, flags, pre_defined
1198 24           buffer_consume(mp4->buf, 8);
1199              
1200 24           my_hv_store( trackinfo, "handler_type", newSVpvn( buffer_ptr(mp4->buf), 4 ) );
1201 24           buffer_consume(mp4->buf, 4);
1202              
1203             // Skip reserved
1204 24           buffer_consume(mp4->buf, 12);
1205              
1206 24           handler_name = newSVpv( buffer_ptr(mp4->buf), 0 );
1207 24           sv_utf8_decode(handler_name);
1208 24           my_hv_store( trackinfo, "handler_name", handler_name );
1209              
1210 24           buffer_consume(mp4->buf, mp4->rsize - 24);
1211              
1212 24           return 1;
1213             }
1214              
1215             uint8_t
1216 24           _mp4_parse_stsd(mp4info *mp4)
1217             {
1218             uint32_t entry_count;
1219              
1220 24 50         if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1221 0           return 0;
1222             }
1223              
1224             // Skip version/flags
1225 24           buffer_consume(mp4->buf, 4);
1226              
1227 24           entry_count = buffer_get_int(mp4->buf);
1228              
1229 24           return 1;
1230             }
1231              
1232             uint8_t
1233 20           _mp4_parse_mp4a(mp4info *mp4)
1234             {
1235 20           HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1236              
1237 20 50         if ( !_check_buf(mp4->infile, mp4->buf, 28, MP4_BLOCK_SIZE) ) {
1238 0           return 0;
1239             }
1240              
1241 20           my_hv_store( trackinfo, "encoding", newSVpvn("mp4a", 4) );
1242              
1243             // Skip reserved
1244 20           buffer_consume(mp4->buf, 16);
1245              
1246 20           mp4->channels = buffer_get_short(mp4->buf);
1247 20           my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1248 20           my_hv_store( trackinfo, "bits_per_sample", newSVuv( buffer_get_short(mp4->buf) ) );
1249              
1250             // Skip reserved
1251 20           buffer_consume(mp4->buf, 4);
1252              
1253             // Skip bogus samplerate
1254 20           buffer_consume(mp4->buf, 2);
1255              
1256             // Skip reserved
1257 20           buffer_consume(mp4->buf, 2);
1258              
1259 20           return 1;
1260             }
1261              
1262             uint8_t
1263 20           _mp4_parse_esds(mp4info *mp4)
1264             {
1265 20           HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1266 20           uint32_t len = 0;
1267             uint32_t avg_bitrate;
1268              
1269 20 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1270 0           return 0;
1271             }
1272              
1273             // Skip version/flags
1274 20           buffer_consume(mp4->buf, 4);
1275              
1276             // Public docs on esds are hard to find, this is based on faad
1277             // and http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
1278              
1279             // verify ES_DescrTag
1280 20 50         if (buffer_get_char(mp4->buf) == 0x03) {
1281             // read length
1282 20 50         if ( _mp4_descr_length(mp4->buf) < 5 + 15 ) {
1283 0           return 0;
1284             }
1285              
1286             // skip 3 bytes
1287 20           buffer_consume(mp4->buf, 3);
1288             }
1289             else {
1290             // skip 2 bytes
1291 0           buffer_consume(mp4->buf, 2);
1292             }
1293              
1294             // verify DecoderConfigDescrTab
1295 20 50         if (buffer_get_char(mp4->buf) != 0x04) {
1296 0           return 0;
1297             }
1298              
1299             // read length
1300 20 50         if ( _mp4_descr_length(mp4->buf) < 13 ) {
1301 0           return 0;
1302             }
1303              
1304             // XXX: map to string
1305 20           my_hv_store( trackinfo, "audio_type", newSVuv( buffer_get_char(mp4->buf) ) );
1306              
1307 20           buffer_consume(mp4->buf, 4);
1308              
1309 20           my_hv_store( trackinfo, "max_bitrate", newSVuv( buffer_get_int(mp4->buf) ) );
1310              
1311 20           avg_bitrate = buffer_get_int(mp4->buf);
1312 20 100         if (avg_bitrate) {
1313 16 100         if ( my_hv_exists(mp4->info, "avg_bitrate") ) {
1314             // If there are multiple tracks, just add up the bitrates
1315 3           avg_bitrate += SvIV(*(my_hv_fetch(mp4->info, "avg_bitrate")));
1316             }
1317 16           my_hv_store( mp4->info, "avg_bitrate", newSVuv(avg_bitrate) );
1318 16           mp4->bitrate = avg_bitrate;
1319             }
1320              
1321             // verify DecSpecificInfoTag
1322 20 50         if (buffer_get_char(mp4->buf) != 0x05) {
1323 0           return 0;
1324             }
1325              
1326             // Read audio object type
1327             // 5 bits, if 0x1F, read 6 more bits
1328 20           len = _mp4_descr_length(mp4->buf);
1329 20 50         if (len > 0) {
1330             uint32_t aot;
1331              
1332 20           len *= 8; // count the number of bits left
1333              
1334 20           aot = buffer_get_bits(mp4->buf, 5);
1335 20           len -= 5;
1336              
1337 20 100         if ( aot == 0x1F ) {
1338 2           aot = 32 + buffer_get_bits(mp4->buf, 6);
1339 2           len -= 6;
1340             }
1341              
1342             // samplerate: 4 bits
1343             // if 0xF, samplerate is next 24 bits
1344             // else lookup in samplerate table
1345             {
1346 20           uint32_t samplerate = buffer_get_bits(mp4->buf, 4);
1347 20           len -= 4;
1348              
1349 20 50         if (samplerate == 0xF) { // XXX need test file with 24-bit samplerate field
1350 0           samplerate = buffer_get_bits(mp4->buf, 24);
1351 0           len -= 24;
1352             }
1353             else {
1354 20           samplerate = samplerate_table[samplerate];
1355             }
1356              
1357             // Channel configuration (4 bits)
1358             // XXX This is sometimes wrong (1 when it should be 2)
1359 20           mp4->channels = buffer_get_bits(mp4->buf, 4);
1360 20           my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1361 20           len -= 4;
1362              
1363 20 100         if (aot == AAC_SLS) {
1364             // Read some SLS-specific config
1365             // bits per sample (3 bits) { 8, 16, 20, 24 }
1366 2           uint8_t bps = buffer_get_bits(mp4->buf, 3);
1367 2           len -= 3;
1368              
1369 2           my_hv_store( trackinfo, "bits_per_sample", newSVuv( bps_table[bps] ) );
1370             }
1371 18 100         else if (aot == AAC_HE || aot == AAC_PS) {
    50          
1372             // Read extended samplerate info
1373 1           samplerate = buffer_get_bits(mp4->buf, 4);
1374 1           len -= 4;
1375 1 50         if (samplerate == 0xF) { // XXX need test file with 24-bit samplerate field
1376 0           samplerate = buffer_get_bits(mp4->buf, 24);
1377 0           len -= 24;
1378             }
1379             else {
1380 1           samplerate = samplerate_table[samplerate];
1381             }
1382             }
1383              
1384 20           my_hv_store( trackinfo, "samplerate", newSVuv(samplerate) );
1385 20           mp4->samplerate = samplerate;
1386             }
1387              
1388 20           my_hv_store( trackinfo, "audio_object_type", newSVuv(aot) );
1389 20           mp4->audio_object_type = aot;
1390              
1391             // Skip rest of box
1392 20           buffer_get_bits(mp4->buf, len);
1393             }
1394              
1395             // verify SL config descriptor type tag
1396 20 50         if (buffer_get_char(mp4->buf) != 0x06) {
1397 0           return 0;
1398             }
1399              
1400 20           _mp4_descr_length(mp4->buf);
1401              
1402             // verify SL value
1403 20 50         if (buffer_get_char(mp4->buf) != 0x02) {
1404 0           return 0;
1405             }
1406              
1407 20           return 1;
1408             }
1409              
1410             uint8_t
1411 3           _mp4_parse_alac(mp4info *mp4)
1412             {
1413 3           HV *trackinfo = _mp4_get_current_trackinfo(mp4);
1414              
1415 3 50         if ( !_check_buf(mp4->infile, mp4->buf, 28, MP4_BLOCK_SIZE) ) {
1416 0           return 0;
1417             }
1418              
1419 3           my_hv_store( trackinfo, "encoding", newSVpvn("alac", 4) );
1420              
1421             // Skip reserved
1422 3           buffer_consume(mp4->buf, 16);
1423              
1424 3           mp4->channels = buffer_get_short(mp4->buf);
1425 3           my_hv_store( trackinfo, "channels", newSVuv(mp4->channels) );
1426 3           my_hv_store( trackinfo, "bits_per_sample", newSVuv( buffer_get_short(mp4->buf) ) );
1427              
1428             // Skip reserved
1429 3           buffer_consume(mp4->buf, 4);
1430              
1431             // Skip bogus samplerate
1432 3           buffer_consume(mp4->buf, 2);
1433              
1434             // Skip reserved
1435 3           buffer_consume(mp4->buf, 2);
1436              
1437 3           return 1;
1438             }
1439              
1440             uint8_t
1441 9           _mp4_parse_stts(mp4info *mp4)
1442             {
1443             int i;
1444              
1445 9 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1446 0           return 0;
1447             }
1448              
1449             // Skip version/flags
1450 9           buffer_consume(mp4->buf, 4);
1451              
1452 9           mp4->num_time_to_samples = buffer_get_int(mp4->buf);
1453             DEBUG_TRACE(" num_time_to_samples %d\n", mp4->num_time_to_samples);
1454              
1455 9 50         New(0,
1456             mp4->time_to_sample,
1457             mp4->num_time_to_samples * sizeof(*mp4->time_to_sample),
1458             struct tts
1459             );
1460              
1461 9 50         if ( !mp4->time_to_sample ) {
1462 0           PerlIO_printf(PerlIO_stderr(), "Unable to parse stts: too large\n");
1463 0           return 0;
1464             }
1465              
1466 20 100         for (i = 0; i < mp4->num_time_to_samples; i++) {
1467 11           mp4->time_to_sample[i].sample_count = buffer_get_int(mp4->buf);
1468 11           mp4->time_to_sample[i].sample_duration = buffer_get_int(mp4->buf);
1469              
1470             DEBUG_TRACE(
1471             " sample_count %d sample_duration %d\n",
1472             mp4->time_to_sample[i].sample_count,
1473             mp4->time_to_sample[i].sample_duration
1474             );
1475             }
1476              
1477 9           return 1;
1478             }
1479              
1480             uint8_t
1481 9           _mp4_parse_stsc(mp4info *mp4)
1482             {
1483             int i;
1484              
1485 9 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1486 0           return 0;
1487             }
1488              
1489             // Skip version/flags
1490 9           buffer_consume(mp4->buf, 4);
1491              
1492 9           mp4->num_sample_to_chunks = buffer_get_int(mp4->buf);
1493             DEBUG_TRACE(" num_sample_to_chunks %d\n", mp4->num_sample_to_chunks);
1494              
1495 9 50         New(0,
1496             mp4->sample_to_chunk,
1497             mp4->num_sample_to_chunks * sizeof(*mp4->sample_to_chunk),
1498             struct stc
1499             );
1500              
1501 9 50         if ( !mp4->sample_to_chunk ) {
1502 0           PerlIO_printf(PerlIO_stderr(), "Unable to parse stsc: too large\n");
1503 0           return 0;
1504             }
1505              
1506 20 100         for (i = 0; i < mp4->num_sample_to_chunks; i++) {
1507 11           mp4->sample_to_chunk[i].first_chunk = buffer_get_int(mp4->buf);
1508 11           mp4->sample_to_chunk[i].samples_per_chunk = buffer_get_int(mp4->buf);
1509              
1510             // Skip sample desc index
1511 11           buffer_consume(mp4->buf, 4);
1512              
1513             DEBUG_TRACE(" first_chunk %d samples_per_chunk %d\n",
1514             mp4->sample_to_chunk[i].first_chunk,
1515             mp4->sample_to_chunk[i].samples_per_chunk
1516             );
1517             }
1518              
1519 9           return 1;
1520             }
1521              
1522             uint8_t
1523 9           _mp4_parse_stsz(mp4info *mp4)
1524             {
1525             int i;
1526              
1527 9 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1528 0           return 0;
1529             }
1530              
1531             // Skip version/flags
1532 9           buffer_consume(mp4->buf, 4);
1533              
1534             // Check sample size is 0
1535 9 50         if ( buffer_get_int(mp4->buf) != 0 ) {
1536             DEBUG_TRACE(" stsz uses fixed sample size\n");
1537 0           buffer_consume(mp4->buf, 4);
1538 0           return 1;
1539             }
1540              
1541 9           mp4->num_sample_byte_sizes = buffer_get_int(mp4->buf);
1542              
1543             DEBUG_TRACE(" num_sample_byte_sizes %d\n", mp4->num_sample_byte_sizes);
1544              
1545 9 50         New(0,
1546             mp4->sample_byte_size,
1547             mp4->num_sample_byte_sizes * sizeof(*mp4->sample_byte_size),
1548             uint16_t
1549             );
1550              
1551 9 50         if ( !mp4->sample_byte_size ) {
1552 0           PerlIO_printf(PerlIO_stderr(), "Unable to parse stsz: too large\n");
1553 0           return 0;
1554             }
1555              
1556 32519 100         for (i = 0; i < mp4->num_sample_byte_sizes; i++) {
1557 32510           uint32_t v = buffer_get_int(mp4->buf);
1558              
1559 32510 50         if (v > 0x0000ffff) {
1560             DEBUG_TRACE("stsz[%d] > 65 kB (%ld)\n", i, (long)v);
1561 0           return 0;
1562             }
1563              
1564 32510           mp4->sample_byte_size[i] = v;
1565              
1566             //DEBUG_TRACE(" sample_byte_size %d\n", v);
1567             }
1568              
1569 9           return 1;
1570             }
1571              
1572             uint8_t
1573 9           _mp4_parse_stco(mp4info *mp4)
1574             {
1575             int i;
1576              
1577 9 50         if ( !_check_buf(mp4->infile, mp4->buf, mp4->rsize, MP4_BLOCK_SIZE) ) {
1578 0           return 0;
1579             }
1580              
1581             // Skip version/flags
1582 9           buffer_consume(mp4->buf, 4);
1583              
1584 9           mp4->num_chunk_offsets = buffer_get_int(mp4->buf);
1585             DEBUG_TRACE(" num_chunk_offsets %d\n", mp4->num_chunk_offsets);
1586              
1587 9 50         New(0,
1588             mp4->chunk_offset,
1589             mp4->num_chunk_offsets * sizeof(*mp4->chunk_offset),
1590             uint32_t
1591             );
1592              
1593 9 50         if ( !mp4->chunk_offset ) {
1594 0           PerlIO_printf(PerlIO_stderr(), "Unable to parse stco: too large\n");
1595 0           return 0;
1596             }
1597              
1598 21857 100         for (i = 0; i < mp4->num_chunk_offsets; i++) {
1599 21848           mp4->chunk_offset[i] = buffer_get_int(mp4->buf);
1600              
1601             //DEBUG_TRACE(" chunk_offset %d\n", mp4->chunk_offset[i]);
1602             }
1603              
1604 9           return 1;
1605             }
1606              
1607             uint8_t
1608 20           _mp4_parse_meta(mp4info *mp4)
1609             {
1610             uint32_t hdlr_size;
1611             char type[5];
1612              
1613 20 50         if ( !_check_buf(mp4->infile, mp4->buf, 12, MP4_BLOCK_SIZE) ) {
1614 0           return 0;
1615             }
1616              
1617             // Skip version/flags
1618 20           buffer_consume(mp4->buf, 4);
1619              
1620             // Parse/skip meta version of hdlr
1621 20           hdlr_size = buffer_get_int(mp4->buf);
1622 20           strncpy( type, (char *)buffer_ptr(mp4->buf), 4 );
1623 20           type[4] = '\0';
1624 20           buffer_consume(mp4->buf, 4);
1625              
1626 20 50         if ( !FOURCC_EQ(type, "hdlr") ) {
    50          
    50          
    50          
    50          
1627 0           return 0;
1628             }
1629              
1630             // Skip rest of hdlr
1631 20 50         if ( !_check_buf(mp4->infile, mp4->buf, hdlr_size - 8, MP4_BLOCK_SIZE) ) {
1632 0           return 0;
1633             }
1634              
1635 20           buffer_consume(mp4->buf, hdlr_size - 8);
1636              
1637 20           return 12 + hdlr_size - 8;
1638             }
1639              
1640             uint8_t
1641 20           _mp4_parse_ilst(mp4info *mp4)
1642             {
1643 416 100         while (mp4->rsize) {
1644             uint32_t size;
1645             char key[5];
1646              
1647 396 50         if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1648 0           return 0;
1649             }
1650              
1651             DEBUG_TRACE(" ilst rsize %llu\n", mp4->rsize);
1652              
1653             // Read Apple annotation box
1654 396           size = buffer_get_int(mp4->buf);
1655 396           strncpy( key, (char *)buffer_ptr(mp4->buf), 4 );
1656 396           key[4] = '\0';
1657 396           buffer_consume(mp4->buf, 4);
1658              
1659             DEBUG_TRACE(" %s size %d\n", key, size);
1660              
1661             // Note: extra _check_buf calls in this function and other ilst functions
1662             // are to avoid reading in the full size of ilst in the case of large artwork
1663              
1664 396           upcase(key);
1665              
1666 396 100         if ( FOURCC_EQ(key, "----") ) {
    50          
    50          
    50          
    50          
1667             // user-specified key/value pair
1668 67 50         if ( !_mp4_parse_ilst_custom(mp4, size - 8) ) {
1669 0           return 0;
1670             }
1671             }
1672             else {
1673             uint32_t bsize;
1674              
1675             // Ensure we have 8 bytes
1676 329 50         if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1677 0           return 0;
1678             }
1679              
1680             // Verify data box
1681 329           bsize = buffer_get_int(mp4->buf);
1682              
1683             DEBUG_TRACE(" box size %d\n", bsize);
1684              
1685             // Sanity check for bad data size
1686 329 50         if ( bsize <= size - 8 ) {
1687             SV *skey;
1688              
1689 329           char *bptr = buffer_ptr(mp4->buf);
1690 329 50         if ( !FOURCC_EQ(bptr, "data") ) {
    50          
    50          
    50          
    50          
1691 0           return 0;
1692             }
1693              
1694 329           buffer_consume(mp4->buf, 4);
1695              
1696 329           skey = newSVpv(key, 0);
1697              
1698 329 50         if ( !_mp4_parse_ilst_data(mp4, bsize - 8, skey) ) {
1699 0           SvREFCNT_dec(skey);
1700 0           return 0;
1701             }
1702              
1703 329           SvREFCNT_dec(skey);
1704              
1705             // XXX: bug 14476, files with multiple COVR images aren't handled here, just skipped for now
1706 329 100         if ( bsize < size - 8 ) {
1707             DEBUG_TRACE(" skipping rest of box, %d\n", size - 8 - bsize );
1708 2           _mp4_skip(mp4, size - 8 - bsize);
1709             }
1710             }
1711             else {
1712             DEBUG_TRACE(" invalid data size %d, skipping value\n", bsize);
1713 0           _mp4_skip(mp4, size - 12);
1714             }
1715             }
1716              
1717 396           mp4->rsize -= size;
1718             }
1719              
1720 20           return 1;
1721             }
1722              
1723             uint8_t
1724 400           _mp4_parse_ilst_data(mp4info *mp4, uint32_t size, SV *key)
1725             {
1726             uint32_t flags;
1727             unsigned char *ckey;
1728             SV *value;
1729              
1730 400           ckey = (unsigned char *)SvPVX(key);
1731 400 100         if ( FOURCC_EQ(ckey, "COVR") && _env_true("AUDIO_SCAN_NO_ARTWORK") ) {
    100          
    50          
    50          
    100          
    100          
1732             // Skip artwork if requested and avoid the memory cost
1733 1           value = newSVuv(size - 8);
1734              
1735 1           my_hv_store( mp4->tags, "COVR_offset", newSVuv(mp4->audio_offset + (mp4->size - mp4->rsize) + 24) );
1736              
1737 1           _mp4_skip(mp4, size);
1738             }
1739             else {
1740             // Read the full ilst value
1741 399 50         if ( !_check_buf(mp4->infile, mp4->buf, size, MP4_BLOCK_SIZE) ) {
1742 0           return 0;
1743             }
1744              
1745             // Version(0) + Flags
1746 399           flags = buffer_get_int(mp4->buf);
1747              
1748             // Skip reserved
1749 399           buffer_consume(mp4->buf, 4);
1750              
1751             DEBUG_TRACE(" flags %d\n", flags);
1752              
1753 399 100         if ( !flags || flags == 21 ) {
    100          
1754 108 100         if ( FOURCC_EQ( SvPVX(key), "TRKN" ) || FOURCC_EQ( SvPVX(key), "DISK" ) ) {
    100          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
1755             // Special case trkn, disk (pair of 16-bit ints)
1756 28           uint16_t num = 0;
1757 28           uint16_t total = 0;
1758              
1759 28           buffer_consume(mp4->buf, 2); // padding
1760              
1761 28           num = buffer_get_short(mp4->buf);
1762              
1763             // Total may not always be present
1764 28 100         if (size > 12) {
1765 27           total = buffer_get_short(mp4->buf);
1766 27           buffer_consume(mp4->buf, size - 14); // optional padding
1767             }
1768              
1769             DEBUG_TRACE(" %d/%d\n", num, total);
1770              
1771 28 100         if (total) {
1772 26           my_hv_store_ent( mp4->tags, key, newSVpvf( "%d/%d", num, total ) );
1773             }
1774 2 100         else if (num) {
1775 1           my_hv_store_ent( mp4->tags, key, newSVuv(num) );
1776             }
1777              
1778 28           return 1;
1779             }
1780 80 100         else if ( FOURCC_EQ( SvPVX(key), "GNRE" ) ) {
    50          
    50          
    50          
    50          
1781             // Special case genre, 16-bit int as id3 genre code
1782             char const *genre_string;
1783 9           uint16_t genre_num = buffer_get_short(mp4->buf);
1784              
1785 9 50         if (genre_num > 0 && genre_num < NGENRES + 1) {
    50          
1786 9           genre_string = _id3_genre_index(genre_num - 1);
1787 9           my_hv_store_ent( mp4->tags, key, newSVpv( genre_string, 0 ) );
1788             }
1789              
1790 9           return 1;
1791             }
1792             else {
1793             // Other binary type, try to guess type based on size
1794 71           uint32_t dsize = size - 8;
1795              
1796 71 100         if (dsize == 1) {
1797 27           value = newSVuv( buffer_get_char(mp4->buf) );
1798             }
1799 44 100         else if (dsize == 2) {
1800 15           value = newSVuv( buffer_get_short(mp4->buf) );
1801             }
1802 29 100         else if (dsize == 4) {
1803 18           value = newSVuv( buffer_get_int(mp4->buf) );
1804             }
1805 11 100         else if (dsize == 8) {
1806 1           value = newSVuv( buffer_get_int64(mp4->buf) );
1807             }
1808             else {
1809 10           value = newSVpvn( buffer_ptr(mp4->buf), dsize );
1810 10           buffer_consume(mp4->buf, dsize);
1811             }
1812             }
1813             }
1814             else { // text data
1815 291           value = newSVpvn( buffer_ptr(mp4->buf), size - 8 );
1816 291           sv_utf8_decode(value);
1817              
1818             // strip copyright symbol 0xA9 out of key
1819 291 100         if ( ckey[0] == 0xA9 ) {
1820 124           ckey++;
1821             }
1822              
1823             DEBUG_TRACE(" %s = %s\n", ckey, SvPVX(value));
1824              
1825 291           buffer_consume(mp4->buf, size - 8);
1826             }
1827             }
1828              
1829             // if key exists, create array
1830 363 100         if ( my_hv_exists( mp4->tags, (char *)ckey ) ) {
1831 100           SV **entry = my_hv_fetch( mp4->tags, (char *)ckey );
1832 100 50         if (entry != NULL) {
1833 100 100         if ( SvROK(*entry) && SvTYPE(SvRV(*entry)) == SVt_PVAV ) {
    50          
1834 7           av_push( (AV *)SvRV(*entry), value );
1835             }
1836             else {
1837             // A non-array entry, convert to array.
1838 93           AV *ref = newAV();
1839 93           av_push( ref, newSVsv(*entry) );
1840 93           av_push( ref, value );
1841 93           my_hv_store( mp4->tags, (char *)ckey, newRV_noinc( (SV*)ref ) );
1842             }
1843             }
1844             }
1845             else {
1846 263           my_hv_store( mp4->tags, (char *)ckey, value );
1847             }
1848              
1849 363           return 1;
1850             }
1851              
1852             uint8_t
1853 67           _mp4_parse_ilst_custom(mp4info *mp4, uint32_t size)
1854             {
1855 67           SV *key = NULL;
1856              
1857 272 100         while (size) {
1858             char type[5];
1859             uint32_t bsize;
1860              
1861             // Ensure we have 8 bytes to get the size and type
1862 205 50         if ( !_check_buf(mp4->infile, mp4->buf, 8, MP4_BLOCK_SIZE) ) {
1863 0           return 0;
1864             }
1865              
1866             // Read box
1867 205           bsize = buffer_get_int(mp4->buf);
1868 205           strncpy( type, (char *)buffer_ptr(mp4->buf), 4 );
1869 205           type[4] = '\0';
1870 205           buffer_consume(mp4->buf, 4);
1871              
1872             DEBUG_TRACE(" %s size %d\n", type, bsize);
1873              
1874 205 100         if ( FOURCC_EQ(type, "name") ) {
    50          
    50          
    50          
    50          
1875             // Ensure we have bsize bytes
1876 67 50         if ( !_check_buf(mp4->infile, mp4->buf, bsize, MP4_BLOCK_SIZE) ) {
1877 0           return 0;
1878             }
1879              
1880 67           buffer_consume(mp4->buf, 4); // padding
1881 67           key = newSVpvn( buffer_ptr(mp4->buf), bsize - 12);
1882 67           sv_utf8_decode(key);
1883 67           upcase(SvPVX(key));
1884 67           buffer_consume(mp4->buf, bsize - 12);
1885              
1886             DEBUG_TRACE(" %s\n", SvPVX(key));
1887             }
1888 138 100         else if ( FOURCC_EQ(type, "data") ) {
    50          
    50          
    50          
    50          
1889 71 50         if (!key) {
1890             // No key yet, data is out of order
1891 0           return 0;
1892             }
1893              
1894 71 50         if ( !_mp4_parse_ilst_data(mp4, bsize - 8, key) ) {
1895 0           SvREFCNT_dec(key);
1896 0           return 0;
1897             }
1898             }
1899             else {
1900             // skip (mean, or other boxes)
1901 67 50         if ( !_check_buf(mp4->infile, mp4->buf, bsize - 8, MP4_BLOCK_SIZE) ) {
1902 0           return 0;
1903             }
1904              
1905 67           buffer_consume(mp4->buf, bsize - 8);
1906             }
1907              
1908 205           size -= bsize;
1909             }
1910              
1911 67           SvREFCNT_dec(key);
1912              
1913 67           return 1;
1914             }
1915              
1916             HV *
1917 67           _mp4_get_current_trackinfo(mp4info *mp4)
1918             {
1919             // Return the trackinfo hash for track id == mp4->current_track
1920             AV *tracks;
1921             HV *trackinfo;
1922             int i;
1923              
1924 67           SV **entry = my_hv_fetch(mp4->info, "tracks");
1925 67 50         if (entry != NULL) {
1926 67           tracks = (AV *)SvRV(*entry);
1927             }
1928             else {
1929 0           return NULL;
1930             }
1931              
1932             // Find entry for this stream number
1933 74 50         for (i = 0; av_len(tracks) >= 0 && i <= av_len(tracks); i++) {
    50          
1934 74           SV **info = av_fetch(tracks, i, 0);
1935 74 50         if (info != NULL) {
1936             SV **tid;
1937              
1938 74           trackinfo = (HV *)SvRV(*info);
1939 74           tid = my_hv_fetch( trackinfo, "id" );
1940 74 50         if (tid != NULL) {
1941 74 100         if ( SvIV(*tid) == mp4->current_track ) {
1942 67           return trackinfo;
1943             }
1944             }
1945             }
1946             }
1947              
1948 0           return NULL;
1949             }
1950              
1951             uint32_t
1952 80           _mp4_descr_length(Buffer *buf)
1953             {
1954             uint8_t b;
1955 80           uint8_t num_bytes = 0;
1956 80           uint32_t length = 0;
1957              
1958             do {
1959 308           b = buffer_get_char(buf);
1960 308           num_bytes++;
1961 308           length = (length << 7) | (b & 0x7f);
1962 308 100         } while ( (b & 0x80) && num_bytes < 4 );
    50          
1963              
1964 80           return length;
1965             }
1966              
1967             void
1968 184           _mp4_skip(mp4info *mp4, uint32_t size)
1969             {
1970 184 100         if ( buffer_len(mp4->buf) >= size ) {
1971             //buffer_dump(mp4->buf, size);
1972 153           buffer_consume(mp4->buf, size);
1973              
1974             DEBUG_TRACE(" skipped buffer data size %d\n", size);
1975             }
1976             else {
1977 31           PerlIO_seek(mp4->infile, size - buffer_len(mp4->buf), SEEK_CUR);
1978 31           buffer_clear(mp4->buf);
1979              
1980             DEBUG_TRACE(" seeked past %d bytes to %d\n", size, (int)PerlIO_tell(mp4->infile));
1981             }
1982 184           }
1983              
1984             uint32_t
1985 1271           _mp4_samples_in_chunk(mp4info *mp4, uint32_t chunk)
1986             {
1987             int i;
1988              
1989 2538 50         for (i = mp4->num_sample_to_chunks - 1; i >= 0; i--) {
1990 2538 100         if (mp4->sample_to_chunk[i].first_chunk <= chunk) {
1991 1271           return mp4->sample_to_chunk[i].samples_per_chunk;
1992             }
1993             }
1994              
1995 0           return mp4->sample_to_chunk[0].samples_per_chunk;
1996             }
1997              
1998             uint32_t
1999 4           _mp4_total_samples(mp4info *mp4)
2000             {
2001             int i;
2002 4           uint32_t total = 0;
2003              
2004 9 100         for (i = 0; i < mp4->num_time_to_samples; i++) {
2005 5           total += mp4->time_to_sample[i].sample_count;
2006             }
2007              
2008 4           return total;
2009             }
2010              
2011             uint32_t
2012 6341           _mp4_get_sample_duration(mp4info *mp4, uint32_t sample)
2013             {
2014             int i;
2015 6341           uint32_t co = 0;
2016              
2017 6342 50         for (i = 0; i < mp4->num_time_to_samples; i++) {
2018 6342           uint32_t delta = mp4->time_to_sample[i].sample_count;
2019 6342 100         if (sample < co + delta) {
2020 6341           return mp4->time_to_sample[i].sample_duration;
2021             }
2022              
2023 1           co += delta;
2024             }
2025              
2026 0           return 0;
2027             }