Skip to content

Commit 4053bce

Browse files
committed
opus: Handle large comment headers
A comment header may span multiple pages and in such situations we need to delay parsing until we have read all pages which belong to the comment header packet. The final page in the comment header packet is marked by setting "granule position" to zero. It's common to have comment headers spanning multiple pages when album art is embedded in an opus file. A single page can contain 65025 bytes so if tags + picture needs more space then additional pages are needed.
1 parent c186ef0 commit 4053bce

4 files changed

Lines changed: 38 additions & 2 deletions

File tree

Changes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ Revision history for Audio::Scan
22

33
Note: Bug numbers refer to bugs at http://bugs.slimdevices.com
44

5+
Upcoming release
6+
- opus: Fix parsing large comment headers (such as large embedded images)
7+
58
1.05 2021-09-10
69
- ID3: Fix v2.4 extended header handling.
710
- WavPack DSD: fix song_length_ms calculation (Kimmo Taskinen).

src/opus.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,17 @@ _opus_parse(PerlIO *infile, char *file, HV *info, HV *tags, uint8_t seeking)
194194
// Copy page into vorbis buffer
195195
buffer_append( &vorbis_buf, buffer_ptr(&ogg_buf), pagelen );
196196
DEBUG_TRACE(" Read %d into vorbis buffer\n", pagelen);
197-
197+
198+
if ( granule_pos != 0 ) {
199+
// RFC7845: The granule position MUST be zero for the ID header
200+
// page and the page where the comment header
201+
// completes.
202+
//
203+
// So, we need to read more pages
204+
buffer_consume( &ogg_buf, pagelen );
205+
continue;
206+
}
207+
198208
// Process vorbis packet
199209
TOC_byte = buffer_get_char(&vorbis_buf);
200210
if ( TOC_byte == 'O' ) {

t/opus.t

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use strict;
22

33
use File::Spec::Functions;
44
use FindBin ();
5-
use Test::More tests => 74;
5+
use Test::More tests => 88;
66

77
use Audio::Scan;
88

@@ -47,6 +47,29 @@ eval {
4747
is($info->{audio_md5}, 'bebb6f0f0a90ce4e4e90635a3c7408d0', 'Audio MD5 ok' );
4848
}
4949

50+
{
51+
local $ENV{AUDIO_SCAN_NO_ARTWORK} = 1;
52+
my $s = Audio::Scan->scan( _f('large_embedded_picture.opus'), { md5_size => 4096 } );
53+
54+
my $info = $s->{info};
55+
is($info->{bitrate_average}, 72640, 'Bitrate ok');
56+
is($info->{channels}, 1, 'Channels ok');
57+
is($info->{file_size}, 205606, 'File size ok');
58+
is($info->{stereo}, 0, 'Stereo ok');
59+
is($info->{samplerate}, 48000, 'Sample Rate ok');
60+
is($info->{input_samplerate}, 44100, 'Input Sample Rate ok');
61+
is($info->{song_length_ms}, 100, 'Song length ok');
62+
is($info->{audio_offset}, 204698, 'Audio offset ok');
63+
is($info->{audio_size}, 908, 'Audio size ok');
64+
is($info->{audio_md5}, 'c050e1584b6284c230708b8b0fb2f31e', 'Audio MD5 ok');
65+
66+
my $tags = $s->{tags};
67+
is($tags->{ALLPICTURES}[0]{image_data}, 152291, 'Image size ok');
68+
is($tags->{ALLPICTURES}[0]{width}, 500, 'Image width ok');
69+
is($tags->{ALLPICTURES}[0]{height}, 500, 'Image height ok');
70+
is($tags->{ALLPICTURES}[0]{mime_type}, 'image/png', 'Image type ok');
71+
}
72+
5073
## A few of the official Opus test files from https://people.xiph.org/~greg/opus_testvectors/
5174

5275
{

t/opus/large_embedded_picture.opus

201 KB
Binary file not shown.

0 commit comments

Comments
 (0)