~andreas-boettger/gmusicbrowser/master

« back to all changes in this revision

Viewing changes to oggheader.pm

  • Committer: Quentin Sculo
  • Date: 2021-02-09 18:10:21 UTC
  • Revision ID: git-v1:8f84c7cd552ad0c3e0c8d042eb8bf745e78e4438
ogg/opus files: various improvements

- safely abort writing the file if errors when reading pages
- fix multi-page comments pages using 0 instead of -1 as granulepos on pages
 with no completed packets
- reclaim empty comment space if too much (>=2048)
- small cleanups

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
# it under the terms of the GNU General Public License version 3, as
6
6
# published by the Free Software Foundation
7
7
 
8
 
#http://xiph.org/vorbis/doc/framing.html
9
 
#http://xiph.org/vorbis/doc/v-comment.html
 
8
# https://xiph.org/ogg/doc/framing.html
 
9
# https://xiph.org/vorbis/doc/v-comment.html
10
10
#opus:
11
11
# https://tools.ietf.org/html/rfc7845.html
12
12
# https://wiki.xiph.org/OggOpus
154
154
 
155
155
        # calculate bitrate, excluding the metadata packet
156
156
        my $audiosize= (-s $file) - $self->{commentpack_size};
157
 
        $self->{info}{bitrate_calculated}= 8*$audiosize / $self->{info}{seconds};
 
157
        $self->{info}{bitrate_calculated}= 8*$audiosize / $self->{info}{seconds} if $self->{info}{seconds};
158
158
    }
159
159
 
160
160
    $self->_close;
203
203
{       my $self=shift;
204
204
        my $newcom_packref=_PackComments($self);
205
205
        #warn "old size $self->{commentpack_size}, need : ".length($$newcom_packref)."\n";
206
 
        # 1. if enough room: write in-place
207
 
        if ( $self->{commentpack_size} >= length $$newcom_packref)
 
206
        # case 1. if enough room and not too much room: write in-place
 
207
        my $enough= $self->{commentpack_size} - length $$newcom_packref;
 
208
        if ($enough>=0 && $enough<2048)
208
209
        {       warn "in place editing.\n";
209
210
                my $left=length $$newcom_packref;
210
211
                my $offset2=0;
211
212
                my $fh=$self->_openw or return;
212
 
                _read_packet($self,PACKET_INFO);        #skip first page
 
213
                my $towrite;
 
214
                my $error;
 
215
 
 
216
                unless (_read_packet($self,PACKET_INFO)) #skip first page
 
217
                {       $error="Can't find info packet"; $left=0;
 
218
                }
 
219
                my $startwrite= tell $fh;
 
220
                # read previous comments pages, replace the comments
213
221
                while ($left)
214
 
                { my $pos=tell $fh;
215
 
                  my ($pageref,$offset,$size)=_ReadPage($self);
216
 
                  seek $fh,$pos,0;
217
 
                  if ($left<$size) {$size=$left; $left=0;}
218
 
                  else             {$left-=$size}
219
 
                  substr $$pageref,$offset,$size,substr($$newcom_packref,$offset2,$size);
220
 
                  $offset2+=$size;
221
 
                  _recompute_page_crc($pageref);
222
 
                  print $fh $$pageref or warn $!;
 
222
                {       my ($pageref,$offset,$size)=_ReadPage($self);
 
223
                        unless ($pageref) { $error="error reading comment page"; last; }
 
224
                        if ($left<$size) {$size=$left; $left=0;}
 
225
                        else             {$left-=$size}
 
226
                        substr $$pageref,$offset,$size,substr($$newcom_packref,$offset2,$size);
 
227
                        $offset2+=$size;
 
228
                        _recompute_page_crc($pageref);
 
229
                        $towrite.= $$pageref;
223
230
                }
 
231
                # write new comments
 
232
                seek $fh,$startwrite,0;
 
233
                unless ($error) { print $fh $towrite or $error=$!; }
224
234
                $self->_close;
225
 
                return;
 
235
                if ($error)
 
236
                {       warn "oggheader: error writing tag in $self->{filename}: $error\n";
 
237
                        return 0;
 
238
                }
 
239
                return 1;
226
240
        }
227
241
 
228
 
        # 2. if not enough room: create new file and replace old file
 
242
        # case 2. if not enough room: create new file and replace old file
229
243
        my $INfh=$self->_open or return;
230
244
        my $OUTfh=$self->_openw(1) or return;   #open .TEMP file
231
245
 
271
285
                #warn unpack('C*',$segments),"\n";
272
286
                #warn "$size ",length($data)-$data_offset,"\n";
273
287
                warn "writing page $pagenb\n" if $::debug;
274
 
                my $page=pack('a4aa x8 a4 V x4 C','OggS',$version,$continued,$serial,$pagenb++,$nbseg).$segments.substr($data,$data_offset,$size);
 
288
                my $granule= $seg==255 ? "\xff"x8 : "\x00"x8; # 0 if packet ends on this page, -1 if not
 
289
                my $page=pack('a4aa a8 a4 V x4 C','OggS',$version,$continued,$granule,$serial,$pagenb++,$nbseg).$segments.substr($data,$data_offset,$size);
275
290
                _recompute_page_crc(\$page);
276
291
                print $OUTfh $page or warn $!;
277
292
                $data_offset+=$size;
285
300
        my $pos=tell $INfh; read $INfh,$data,27; seek $INfh,$pos,0;
286
301
        #warn "first audio data on page ".unpack('x18V',$data)."\n";
287
302
        # fast raw copy by 1M chunks if page numbers haven't changed
288
 
        if ( substr($data,0,4) eq 'OggS' && unpack('x18V',$data) eq $pagenb)
289
 
                { my $buffer;
290
 
                  print $OUTfh $buffer  or warn $! while read $INfh,$buffer,1048576;
291
 
                }
 
303
        if (substr($data,0,4) eq 'OggS' && unpack('x18V',$data) == $pagenb)
 
304
        {       my $buffer;
 
305
                print $OUTfh $buffer  or warn $! while read $INfh,$buffer,1048576;
 
306
        }
292
307
 
293
308
        # __SLOW__ copy if page number must be changed -> and crc recomputed
294
309
        else
298
313
                        _recompute_page_crc($pageref);  #recompute crc
299
314
                        print $OUTfh $$pageref or warn $!;      #write page
300
315
                }
 
316
                # if we didn't reach end of file, something went wrong, abort
 
317
                unless (eof($self->{fileHandle}))
 
318
                {       warn "oggheader: error reading pages of $self->{filename} while copying audio data, aborting write\n";
 
319
                        $self->_close;
 
320
                        close $OUTfh;
 
321
                        unlink $self->{filename}.'.TEMP';
 
322
                        %$self=(); #destroy the object to make sure it is not reused as many of its data are now invalid
 
323
                        return 0;
 
324
                }
301
325
        }
302
326
 
303
327
        $self->_close;
546
570
        my $buf;
547
571
        my $r=read $fh,$buf,27;
548
572
        return 0 unless $r==27;
549
 
        #http://www.xiph.org/ogg/vorbis/doc/framing.html
 
573
        # https://xiph.org/ogg/doc/framing.html
550
574
        # 'OggS' 4 bytes        capture_pattern                 0
551
575
        # 0x00   1 byte         stream_structure_version        1
552
576
        #        1 byte         header_type_flag                2