You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1083 lines
29 KiB
1083 lines
29 KiB
7 years ago
|
/*
|
||
|
parse.c - compact version of famous library mpg123
|
||
|
Copyright (C) 2017 Uncle Mike
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#include "mpeghead.h"
|
||
|
#include "mpg123.h"
|
||
|
#include "getbits.h"
|
||
|
#include <limits.h>
|
||
|
|
||
|
#define TRACK_MAX_FRAMES (ULONG_MAX / 4 / 1152)
|
||
|
#define FORGET_INTERVAL 1024 // used by callers to set forget flag each <n> bytes.
|
||
|
|
||
|
// use 4 bytes from buf to construct 28bit uint value and return 1; return 0 if bytes are not synchsafe
|
||
|
#define synchsafe_to_long( buf, res ) \
|
||
|
((((buf)[0]|(buf)[1]|(buf)[2]|(buf)[3]) & 0x80) ? \
|
||
|
0 : (res = (((ulong)(buf)[0]) << 21) | (((ulong)(buf)[1]) << 14)|(((ulong)(buf)[2]) << 7)|((ulong)(buf)[3]), 1 ))
|
||
|
|
||
|
#define check_bytes_left( n ) if( fr->framesize < lame_offset + n ) \
|
||
|
goto check_lame_tag_yes
|
||
|
|
||
|
// PARSE_GOOD and PARSE_BAD have to be 1 and 0 (TRUE and FALSE), others can vary.
|
||
|
enum parse_codes
|
||
|
{
|
||
|
PARSE_MORE = MPG123_NEED_MORE,
|
||
|
PARSE_ERR = MPG123_ERR,
|
||
|
PARSE_END = 10, /* No more audio data to find. */
|
||
|
PARSE_GOOD = 1, /* Everything's fine. */
|
||
|
PARSE_BAD = 0, /* Not fine (invalid data). */
|
||
|
PARSE_RESYNC = 2, /* Header not good, go into resync. */
|
||
|
PARSE_AGAIN = 3, /* Really start over, throw away and read a new header, again. */
|
||
|
};
|
||
|
|
||
|
// bitrates for [mpeg1/2][layer]
|
||
|
static const int tabsel_123[2][3][16] =
|
||
|
{
|
||
|
{
|
||
|
{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
|
||
|
{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
|
||
|
{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,}
|
||
|
},
|
||
|
{
|
||
|
{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
|
||
|
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
|
||
|
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };
|
||
|
|
||
|
void set_pointer( mpg123_handle_t *fr, long backstep )
|
||
|
{
|
||
|
fr->wordpointer = fr->bsbuf + fr->ssize - backstep;
|
||
|
|
||
|
if( backstep )
|
||
|
memcpy( fr->wordpointer, fr->bsbufold + fr->fsizeold - backstep, backstep );
|
||
|
|
||
|
fr->bitindex = 0;
|
||
|
}
|
||
|
|
||
|
int frame_bitrate( mpg123_handle_t *fr )
|
||
|
{
|
||
|
return tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index];
|
||
|
}
|
||
|
|
||
|
long frame_freq( mpg123_handle_t *fr )
|
||
|
{
|
||
|
return freqs[fr->sampling_frequency];
|
||
|
}
|
||
|
|
||
|
double compute_bpf( mpg123_handle_t *fr )
|
||
|
{
|
||
|
double bpf;
|
||
|
|
||
|
switch( fr->lay )
|
||
|
{
|
||
|
case 1:
|
||
|
bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
|
||
|
bpf *= 12000.0 * 4.0;
|
||
|
bpf /= freqs[fr->sampling_frequency] << (fr->lsf);
|
||
|
break;
|
||
|
case 2:
|
||
|
case 3:
|
||
|
bpf = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index];
|
||
|
bpf *= 144000;
|
||
|
bpf /= freqs[fr->sampling_frequency] << (fr->lsf);
|
||
|
break;
|
||
|
default:
|
||
|
bpf = 1.0;
|
||
|
}
|
||
|
|
||
|
return bpf;
|
||
|
}
|
||
|
|
||
|
int mpg123_spf( mpg123_handle_t *mh )
|
||
|
{
|
||
|
if( mh == NULL )
|
||
|
return MPG123_ERR;
|
||
|
|
||
|
return mh->firsthead ? mh->spf : MPG123_ERR;
|
||
|
}
|
||
|
|
||
|
double mpg123_tpf( mpg123_handle_t *fr )
|
||
|
{
|
||
|
static int bs[4] = { 0,384, 1152, 1152 };
|
||
|
double tpf;
|
||
|
|
||
|
if( fr == NULL || !fr->firsthead )
|
||
|
return MPG123_ERR;
|
||
|
|
||
|
tpf = (double)bs[fr->lay];
|
||
|
tpf /= freqs[fr->sampling_frequency] << (fr->lsf);
|
||
|
|
||
|
return tpf;
|
||
|
}
|
||
|
|
||
|
int get_songlen( mpg123_handle_t *fr, int no )
|
||
|
{
|
||
|
double tpf;
|
||
|
|
||
|
if( !fr ) return 0;
|
||
|
|
||
|
if( no < 0 )
|
||
|
{
|
||
|
if( !fr->rd || fr->rdat.filelen < 0 )
|
||
|
return 0;
|
||
|
|
||
|
no = (int)((double)fr->rdat.filelen / compute_bpf( fr ));
|
||
|
}
|
||
|
|
||
|
tpf = mpg123_tpf( fr );
|
||
|
return (int)(no * tpf);
|
||
|
}
|
||
|
|
||
|
// just tell if the header is some mono.
|
||
|
static int header_mono( ulong newhead )
|
||
|
{
|
||
|
return HDR_CHANNEL_VAL( newhead ) == MPG_MD_MONO ? TRUE : FALSE;
|
||
|
}
|
||
|
|
||
|
static int head_check(ulong head)
|
||
|
{
|
||
|
if((( head & HDR_SYNC ) != HDR_SYNC ) || (!(HDR_LAYER_VAL(head))) || (HDR_BITRATE_VAL(head) == 0xf) || (HDR_SAMPLERATE_VAL(head) == 0x3 ))
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// true if the two headers will work with the same decoding routines
|
||
|
static int head_compatible( ulong fred, ulong bret )
|
||
|
{
|
||
|
return (( fred & HDR_CMPMASK ) == ( bret & HDR_CMPMASK ) && header_mono( fred ) == header_mono( bret ));
|
||
|
}
|
||
|
|
||
|
// this is moderately sized buffers. Int offset is enough.
|
||
|
static ulong bit_read_long( byte *buf, int *offset )
|
||
|
{
|
||
|
ulong val = (((ulong)buf[*offset]) << 24) | (((ulong) buf[*offset+1]) << 16) | (((ulong) buf[*offset+2]) << 8) | ((ulong)buf[*offset+3]);
|
||
|
*offset += 4;
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static word bit_read_short( byte *buf, int *offset )
|
||
|
{
|
||
|
word val = (((word) buf[*offset] ) << 8) | ((word) buf[*offset+1]);
|
||
|
*offset += 2;
|
||
|
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static int check_lame_tag( mpg123_handle_t *fr )
|
||
|
{
|
||
|
int lame_offset = (fr->stereo == 2) ? (fr->lsf ? 17 : 32) : (fr->lsf ? 9 : 17);
|
||
|
ulong xing_flags;
|
||
|
ulong long_tmp;
|
||
|
int i;
|
||
|
|
||
|
// going to look for Xing or Info at some position after the header
|
||
|
// MPEG 1 MPEG 2/2.5 (LSF)
|
||
|
// Stereo, Joint Stereo, Dual Channel 32 17
|
||
|
// Mono 17 9
|
||
|
|
||
|
if( fr->p.flags & MPG123_IGNORE_INFOFRAME )
|
||
|
goto check_lame_tag_no;
|
||
|
|
||
|
// note: CRC or not, that does not matter here.
|
||
|
// but, there is any combination of Xing flags in the wild. There are headers
|
||
|
// without the search index table! I cannot assume a reasonable minimal size
|
||
|
// for the actual data, have to check if each byte of information is present.
|
||
|
// but: 4 B Info/Xing + 4 B flags is bare minimum.
|
||
|
if( fr->framesize < lame_offset + 8 )
|
||
|
goto check_lame_tag_no;
|
||
|
|
||
|
// only search for tag when all zero before it (apart from checksum)
|
||
|
for( i = 2; i < lame_offset; ++i )
|
||
|
{
|
||
|
if( fr->bsbuf[i] != 0 )
|
||
|
goto check_lame_tag_no;
|
||
|
}
|
||
|
|
||
|
if((fr->bsbuf[lame_offset] == 'I') && (fr->bsbuf[lame_offset+1] == 'n') && (fr->bsbuf[lame_offset+2] == 'f') && (fr->bsbuf[lame_offset+3] == 'o'))
|
||
|
{
|
||
|
// we still have to see what there is
|
||
|
}
|
||
|
else if((fr->bsbuf[lame_offset] == 'X') && (fr->bsbuf[lame_offset+1] == 'i') && (fr->bsbuf[lame_offset+2] == 'n') && (fr->bsbuf[lame_offset+3] == 'g'))
|
||
|
{
|
||
|
// Xing header means always VBR
|
||
|
fr->vbr = MPG123_VBR;
|
||
|
}
|
||
|
else goto check_lame_tag_no;
|
||
|
|
||
|
lame_offset += 4;
|
||
|
xing_flags = bit_read_long( fr->bsbuf, &lame_offset );
|
||
|
|
||
|
// from now on, I have to carefully check if the announced data is actually
|
||
|
// there! I'm always returning 'yes', though.
|
||
|
if( xing_flags & 1 )
|
||
|
{
|
||
|
// total bitstream frames
|
||
|
check_bytes_left( 4 );
|
||
|
long_tmp = bit_read_long( fr->bsbuf, &lame_offset );
|
||
|
if( fr->p.flags & MPG123_IGNORE_STREAMLENGTH )
|
||
|
{
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// check for endless stream, but: TRACK_MAX_FRAMES sensible at all?
|
||
|
fr->track_frames = long_tmp > TRACK_MAX_FRAMES ? 0 : (mpg_off_t)long_tmp;
|
||
|
|
||
|
// all or nothing: Only if encoder delay/padding is known, we'll cut
|
||
|
// samples for gapless.
|
||
|
if( fr->p.flags & MPG123_GAPLESS )
|
||
|
frame_gapless_init( fr, fr->track_frames, 0, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( xing_flags & 0x2 )
|
||
|
{
|
||
|
// total bitstream bytes
|
||
|
check_bytes_left( 4 );
|
||
|
long_tmp = bit_read_long( fr->bsbuf, &lame_offset );
|
||
|
|
||
|
if( fr->p.flags & MPG123_IGNORE_STREAMLENGTH )
|
||
|
{
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the Xing bitstream length, at least as interpreted by the Lame
|
||
|
// encoder, encompasses all data from the Xing header frame on,
|
||
|
// ignoring leading ID3v2 data. Trailing tags (ID3v1) seem to be
|
||
|
// included, though.
|
||
|
if( fr->rdat.filelen < 1 )
|
||
|
{
|
||
|
fr->rdat.filelen = (mpg_off_t)long_tmp + fr->audio_start; // Overflow?
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( xing_flags & 0x4 ) // TOC
|
||
|
{
|
||
|
check_bytes_left( 100 );
|
||
|
frame_fill_toc( fr, fr->bsbuf + lame_offset );
|
||
|
lame_offset += 100;
|
||
|
}
|
||
|
|
||
|
// VBR quality
|
||
|
if( xing_flags & 0x8 )
|
||
|
{
|
||
|
check_bytes_left( 4 );
|
||
|
long_tmp = bit_read_long( fr->bsbuf, &lame_offset );
|
||
|
}
|
||
|
|
||
|
// either zeros/nothing, or:
|
||
|
// 0-8: LAME3.90a
|
||
|
// 9: revision/VBR method
|
||
|
// 10: lowpass
|
||
|
// 11-18: ReplayGain
|
||
|
// 19: encoder flags
|
||
|
// 20: ABR
|
||
|
// 21-23: encoder delays
|
||
|
check_bytes_left( 24 ); // I'm interested in 24 B of extra info.
|
||
|
|
||
|
if( fr->bsbuf[lame_offset] != 0 )
|
||
|
{
|
||
|
byte lame_vbr;
|
||
|
float replay_gain[2] = { 0, 0 };
|
||
|
float peak = 0;
|
||
|
float gain_offset = 0; // going to be +6 for old lame that used 83dB
|
||
|
char nb[10];
|
||
|
mpg_off_t pad_in;
|
||
|
mpg_off_t pad_out;
|
||
|
|
||
|
memcpy( nb, fr->bsbuf + lame_offset, 9 );
|
||
|
nb[9] = 0;
|
||
|
|
||
|
if(!strncmp( "LAME", nb, 4 ))
|
||
|
{
|
||
|
uint major, minor;
|
||
|
char rest[6];
|
||
|
|
||
|
rest[0] = 0;
|
||
|
|
||
|
// Lame versions before 3.95.1 used 83 dB reference level, later
|
||
|
// versions 89 dB. We stick with 89 dB as being "normal", adding 6 dB.
|
||
|
if( sscanf( nb + 4, "%u.%u%s", &major, &minor, rest ) >= 2 )
|
||
|
{
|
||
|
// We cannot detect LAME 3.95 reliably (same version string as
|
||
|
// 3.95.1), so this is a blind spot. Everything < 3.95 is safe, though.
|
||
|
if( major < 3 || ( major == 3 && minor < 95 ))
|
||
|
gain_offset = 6;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lame_offset += 9; // 9 in
|
||
|
|
||
|
// the 4 big bits are tag revision, the small bits vbr method.
|
||
|
lame_vbr = fr->bsbuf[lame_offset] & 15;
|
||
|
lame_offset += 1; // 10 in
|
||
|
|
||
|
// from rev1 proposal... not sure if all good in practice
|
||
|
switch( lame_vbr )
|
||
|
{
|
||
|
case 1:
|
||
|
case 8: fr->vbr = MPG123_CBR; break;
|
||
|
case 2:
|
||
|
case 9: fr->vbr = MPG123_ABR; break;
|
||
|
default: fr->vbr = MPG123_VBR; break; // 00 ==unknown is taken as VBR
|
||
|
}
|
||
|
|
||
|
lame_offset += 1; // 11 in, skipping lowpass filter value
|
||
|
peak = 0; // until better times arrived
|
||
|
lame_offset += 4; // 15 in
|
||
|
|
||
|
// ReplayGain values - lame only writes radio mode gain...
|
||
|
// 16bit gain, 3 bits name, 3 bits originator, sign (1=-, 0=+),
|
||
|
// dB value * 10 in 9 bits (fixed point) ignore the setting if name or
|
||
|
// originator == 000!
|
||
|
// radio 0 0 1 0 1 1 1 0 0 1 1 1 1 1 0 1
|
||
|
// audiophile 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0
|
||
|
for( i = 0; i < 2; ++i )
|
||
|
{
|
||
|
byte gt = fr->bsbuf[lame_offset] >> 5;
|
||
|
byte origin = (fr->bsbuf[lame_offset] >> 2) & 0x7;
|
||
|
float factor = (fr->bsbuf[lame_offset] & 0x2) ? -0.1f : 0.1f;
|
||
|
word gain = bit_read_short( fr->bsbuf, &lame_offset ) & 0x1ff; // 19 in (2 cycles)
|
||
|
|
||
|
if( origin == 0 || gt < 1 || gt > 2 )
|
||
|
continue;
|
||
|
|
||
|
gt--;
|
||
|
replay_gain[gt] = factor * (float)gain;
|
||
|
|
||
|
// apply gain offset for automatic origin.
|
||
|
if( origin == 3 ) replay_gain[gt] += gain_offset;
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < 2; ++i )
|
||
|
{
|
||
|
if( fr->rva.level[i] <= 0 )
|
||
|
{
|
||
|
fr->rva.peak[i] = 0; // TODO: use parsed peak?
|
||
|
fr->rva.gain[i] = replay_gain[i];
|
||
|
fr->rva.level[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lame_offset += 1; // 20 in, skipping encoding flags byte
|
||
|
|
||
|
// ABR rate
|
||
|
if( fr->vbr == MPG123_ABR )
|
||
|
fr->abr_rate = fr->bsbuf[lame_offset];
|
||
|
lame_offset += 1; // 21 in
|
||
|
|
||
|
// Encoder delay and padding, two 12 bit values
|
||
|
// ... lame does write them from int.
|
||
|
pad_in = ((((int) fr->bsbuf[lame_offset]) << 4) | (((int) fr->bsbuf[lame_offset+1]) >> 4));
|
||
|
pad_out = ((((int) fr->bsbuf[lame_offset+1]) << 8) | ((int) fr->bsbuf[lame_offset+2])) & 0xfff;
|
||
|
lame_offset += 3; // 24 in
|
||
|
|
||
|
if( fr->p.flags & MPG123_GAPLESS )
|
||
|
frame_gapless_init( fr, fr->track_frames, pad_in, pad_out );
|
||
|
// final: 24 B LAME data
|
||
|
}
|
||
|
|
||
|
check_lame_tag_yes:
|
||
|
// switch buffer back ...
|
||
|
fr->bsbuf = fr->bsspace[fr->bsnum] + 512;
|
||
|
fr->bsnum = (fr->bsnum + 1) & 1;
|
||
|
|
||
|
return 1;
|
||
|
|
||
|
check_lame_tag_no:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// first attempt of read ahead check to find the real first header; cannot believe what junk is out there!
|
||
|
static int do_readahead( mpg123_handle_t *fr, ulong newhead )
|
||
|
{
|
||
|
ulong nexthead = 0;
|
||
|
int hd = 0;
|
||
|
mpg_off_t start, oret;
|
||
|
int ret;
|
||
|
|
||
|
if(!( !fr->firsthead && fr->rdat.flags & ( READER_SEEKABLE|READER_BUFFERED )))
|
||
|
return PARSE_GOOD;
|
||
|
|
||
|
start = fr->rd->tell( fr );
|
||
|
|
||
|
// step framesize bytes forward and read next possible header
|
||
|
if((oret = fr->rd->skip_bytes( fr, fr->framesize )) < 0 )
|
||
|
return oret == MPG123_NEED_MORE ? PARSE_MORE : PARSE_ERR;
|
||
|
|
||
|
// read header, seek back.
|
||
|
hd = fr->rd->head_read( fr, &nexthead );
|
||
|
|
||
|
if( fr->rd->back_bytes( fr, fr->rd->tell( fr ) - start ) < 0 )
|
||
|
return PARSE_ERR;
|
||
|
|
||
|
if( hd == MPG123_NEED_MORE )
|
||
|
return PARSE_MORE;
|
||
|
|
||
|
if( !hd ) return PARSE_END;
|
||
|
|
||
|
if( !head_check( nexthead ) || !head_compatible( newhead, nexthead ))
|
||
|
{
|
||
|
fr->oldhead = 0; // start over
|
||
|
|
||
|
// try next byte for valid header
|
||
|
if(( ret = fr->rd->back_bytes( fr, 3 )) < 0 )
|
||
|
return PARSE_ERR;
|
||
|
return PARSE_AGAIN;
|
||
|
}
|
||
|
|
||
|
return PARSE_GOOD;
|
||
|
}
|
||
|
|
||
|
static void halfspeed_prepare( mpg123_handle_t *fr )
|
||
|
{
|
||
|
if( fr->p.halfspeed && fr->lay == 3 )
|
||
|
memcpy( fr->ssave, fr->bsbuf, fr->ssize );
|
||
|
}
|
||
|
|
||
|
// if this returns 1, the next frame is the repetition.
|
||
|
static int halfspeed_do( mpg123_handle_t *fr )
|
||
|
{
|
||
|
// speed-down hack: Play it again, Sam (the frame, I mean).
|
||
|
if( fr->p.halfspeed )
|
||
|
{
|
||
|
if( fr->halfphase ) // repeat last frame
|
||
|
{
|
||
|
fr->to_decode = fr->to_ignore = TRUE;
|
||
|
fr->halfphase--;
|
||
|
fr->bitindex = 0;
|
||
|
fr->wordpointer = (byte *)fr->bsbuf;
|
||
|
|
||
|
if( fr->lay == 3 )
|
||
|
memcpy( fr->bsbuf, fr->ssave, fr->ssize );
|
||
|
|
||
|
if( fr->error_protection )
|
||
|
fr->crc = getbits( fr, 16 ); // skip crc
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fr->halfphase = fr->p.halfspeed - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// read ahead and find the next MPEG header, to guess framesize
|
||
|
// return value: success code
|
||
|
// PARSE_GOOD: found a valid frame size (stored in the handle).
|
||
|
// < 0: error codes, possibly from feeder buffer (NEED_MORE)
|
||
|
// PARSE_BAD: cannot get the framesize for some reason and shall silentry try the next possible header (if this is no free format stream after all...)
|
||
|
static int guess_freeformat_framesize( mpg123_handle_t *fr, ulong oldhead )
|
||
|
{
|
||
|
ulong head;
|
||
|
int ret;
|
||
|
long i;
|
||
|
|
||
|
if(!( fr->rdat.flags & ( READER_SEEKABLE|READER_BUFFERED )))
|
||
|
return PARSE_BAD;
|
||
|
|
||
|
if(( ret = fr->rd->head_read( fr, &head )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
// we are already 4 bytes into it
|
||
|
for( i = 4; i < MAXFRAMESIZE + 4; i++ )
|
||
|
{
|
||
|
if(( ret = fr->rd->head_shift( fr, &head )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
// no head_check needed, the mask contains all relevant bits.
|
||
|
if(( head & HDR_SAMEMASK ) == ( oldhead & HDR_SAMEMASK ))
|
||
|
{
|
||
|
fr->rd->back_bytes( fr, i + 1 );
|
||
|
fr->framesize = i - 3;
|
||
|
return PARSE_GOOD; // success!
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fr->rd->back_bytes( fr, i );
|
||
|
|
||
|
return PARSE_BAD;
|
||
|
}
|
||
|
|
||
|
// decode a header and write the information
|
||
|
// into the frame structure
|
||
|
// return values are compatible with those of read_frame, namely:
|
||
|
// 1: success
|
||
|
// 0: no valid header
|
||
|
// <0: some error
|
||
|
// you are required to do a head_check() before calling!
|
||
|
static int decode_header( mpg123_handle_t *fr, ulong newhead, int *freeformat_count )
|
||
|
{
|
||
|
// for some reason, the layer and sampling freq settings used to be wrapped
|
||
|
// in a weird conditional including MPG123_NO_RESYNC. what was I thinking?
|
||
|
// this information has to be consistent.
|
||
|
fr->lay = 4 - HDR_LAYER_VAL( newhead );
|
||
|
|
||
|
if( HDR_VERSION_VAL( newhead ) & 0x2 )
|
||
|
{
|
||
|
fr->lsf = (HDR_VERSION_VAL( newhead ) & 0x1 ) ? 0 : 1;
|
||
|
fr->sampling_frequency = HDR_SAMPLERATE_VAL( newhead ) + (fr->lsf * 3);
|
||
|
fr->mpeg25 = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fr->sampling_frequency = 6 + HDR_SAMPLERATE_VAL( newhead );
|
||
|
fr->mpeg25 = 1;
|
||
|
fr->lsf = 1;
|
||
|
}
|
||
|
|
||
|
fr->error_protection = HDR_CRC_VAL( newhead ) ^ 0x1;
|
||
|
fr->bitrate_index = HDR_BITRATE_VAL( newhead );
|
||
|
fr->padding = HDR_PADDING_VAL( newhead );
|
||
|
fr->extension = HDR_PRIVATE_VAL( newhead );
|
||
|
fr->mode = HDR_CHANNEL_VAL( newhead );
|
||
|
fr->mode_ext = HDR_CHANEX_VAL( newhead );
|
||
|
fr->copyright = HDR_COPYRIGHT_VAL( newhead );
|
||
|
fr->original = HDR_ORIGINAL_VAL( newhead );
|
||
|
fr->emphasis = HDR_EMPHASIS_VAL( newhead );
|
||
|
fr->freeformat = !( newhead & HDR_BITRATE );
|
||
|
fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
|
||
|
|
||
|
// we can't use tabsel_123 for freeformat, so trying to guess framesize...
|
||
|
if( fr->freeformat )
|
||
|
{
|
||
|
// when we first encounter the frame with freeformat, guess framesize
|
||
|
if( fr->freeformat_framesize < 0 )
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
*freeformat_count += 1;
|
||
|
if( *freeformat_count > 5 )
|
||
|
return PARSE_BAD;
|
||
|
|
||
|
ret = guess_freeformat_framesize( fr, newhead );
|
||
|
|
||
|
if( ret == PARSE_GOOD )
|
||
|
{
|
||
|
fr->freeformat_framesize = fr->framesize - fr->padding;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// freeformat should be CBR, so the same framesize can be used at the 2nd reading or later
|
||
|
fr->framesize = fr->freeformat_framesize + fr->padding;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch( fr->lay )
|
||
|
{
|
||
|
case 3:
|
||
|
fr->spf = fr->lsf ? 576 : 1152; /* MPEG 2.5 implies LSF.*/
|
||
|
fr->do_layer = do_layer3;
|
||
|
if( fr->lsf ) fr->ssize = (fr->stereo == 1) ? 9 : 17;
|
||
|
else fr->ssize = (fr->stereo == 1) ? 17 : 32;
|
||
|
|
||
|
if( fr->error_protection )
|
||
|
fr->ssize += 2;
|
||
|
|
||
|
if( !fr->freeformat )
|
||
|
{
|
||
|
fr->framesize = (long)tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
|
||
|
fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
|
||
|
fr->framesize = fr->framesize + fr->padding - 4;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return PARSE_BAD;
|
||
|
}
|
||
|
|
||
|
if( fr->framesize > MAXFRAMESIZE )
|
||
|
return PARSE_BAD;
|
||
|
|
||
|
return PARSE_GOOD;
|
||
|
}
|
||
|
|
||
|
// advance a byte in stream to get next possible header and forget
|
||
|
// buffered data if possible (for feed reader).
|
||
|
static int forget_head_shift( mpg123_handle_t *fr, ulong *newheadp, int forget )
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if(( ret = fr->rd->head_shift( fr, newheadp )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
// try to forget buffered data as early as possible to speed up parsing where
|
||
|
// new data needs to be added for resync (and things would be re-parsed again
|
||
|
// and again because of the start from beginning after hitting end).
|
||
|
if( forget && fr->rd->forget != NULL )
|
||
|
{
|
||
|
// ensure that the last 4 bytes stay in buffers for reading the header anew.
|
||
|
if(!fr->rd->back_bytes( fr, 4 ))
|
||
|
{
|
||
|
fr->rd->forget( fr );
|
||
|
fr->rd->back_bytes( fr, -4 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret; // No surprise here, error already triggered early return.
|
||
|
}
|
||
|
|
||
|
// trying to parse ID3v2.3 and ID3v2.4 tags...
|
||
|
// returns: 0: bad or just unparseable tag
|
||
|
// 1: good, (possibly) new tag info
|
||
|
// <0: reader error (may need more data feed, try again)
|
||
|
int parse_new_id3( mpg123_handle_t *fr, ulong first4bytes )
|
||
|
{
|
||
|
byte buf[6];
|
||
|
ulong length=0;
|
||
|
byte flags = 0;
|
||
|
int ret = 1;
|
||
|
int ret2;
|
||
|
byte major = (byte)(first4bytes & 0xff);
|
||
|
|
||
|
if( major == 0xff )
|
||
|
return 0;
|
||
|
|
||
|
if(( ret2 = fr->rd->read_frame_body( fr, buf, 6 )) < 0 ) // read more header information
|
||
|
return ret2;
|
||
|
|
||
|
if( buf[0] == 0xff )
|
||
|
return 0; // revision, will never be 0xff.
|
||
|
|
||
|
// second new byte are some nice flags, if these are invalid skip the whole thing
|
||
|
flags = buf[1];
|
||
|
|
||
|
// length-10 or length-20 (footer present); 4 synchsafe integers == 28 bit number
|
||
|
// we have already read 10 bytes, so left are length or length+10 bytes belonging to tag
|
||
|
if( !synchsafe_to_long( buf + 2, length ))
|
||
|
return 0;
|
||
|
|
||
|
if(( ret2 = fr->rd->skip_bytes( fr, length )) < 0 ) // will not store data in backbuff!
|
||
|
ret = ret2;
|
||
|
|
||
|
// skip footer if present
|
||
|
if(( ret > 0 ) && ( flags & 16 ) && (( ret2 = fr->rd->skip_bytes( fr, length )) < 0 ))
|
||
|
ret = ret2;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int handle_id3v2( mpg123_handle_t *fr, ulong newhead )
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
fr->oldhead = 0; // think about that. Used to be present only for skipping of junk, not resync-style wetwork.
|
||
|
ret = parse_new_id3( fr, newhead );
|
||
|
if( ret < 0 ) return ret;
|
||
|
|
||
|
return PARSE_AGAIN;
|
||
|
}
|
||
|
|
||
|
// watch out for junk/tags on beginning of stream by invalid header
|
||
|
static int skip_junk( mpg123_handle_t *fr, ulong *newheadp, long *headcount )
|
||
|
{
|
||
|
int ret;
|
||
|
int freeformat_count = 0;
|
||
|
ulong newhead = *newheadp;
|
||
|
uint forgetcount = 0;
|
||
|
long limit = 65536;
|
||
|
|
||
|
// check for id3v2; first three bytes (of 4) are "ID3"
|
||
|
if(( newhead & (ulong)0xffffff00 ) == (ulong)0x49443300 )
|
||
|
{
|
||
|
return handle_id3v2( fr, newhead );
|
||
|
}
|
||
|
|
||
|
// I even saw RIFF headers at the beginning of MPEG streams ;(
|
||
|
if( newhead == ('R'<<24)+('I'<<16)+('F'<<8)+'F' )
|
||
|
{
|
||
|
if(( ret = fr->rd->head_read( fr, &newhead )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
while( newhead != ('d'<<24)+('a'<<16)+('t'<<8)+'a' )
|
||
|
{
|
||
|
if( ++forgetcount > FORGET_INTERVAL )
|
||
|
forgetcount = 0;
|
||
|
|
||
|
if(( ret = forget_head_shift( fr, &newhead, !forgetcount )) <= 0 )
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if(( ret = fr->rd->head_read( fr, &newhead )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
fr->oldhead = 0;
|
||
|
*newheadp = newhead;
|
||
|
|
||
|
return PARSE_AGAIN;
|
||
|
}
|
||
|
|
||
|
// unhandled junk... just continue search for a header, stepping in single bytes through next 64K.
|
||
|
// this is rather identical to the resync loop.
|
||
|
*newheadp = 0; // invalidate the external value.
|
||
|
ret = 0; // we will check the value after the loop.
|
||
|
|
||
|
// we prepare for at least the 64K bytes as usual, unless
|
||
|
// user explicitly wanted more (even infinity). Never less.
|
||
|
if( fr->p.resync_limit < 0 || fr->p.resync_limit > limit )
|
||
|
limit = fr->p.resync_limit;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
++(*headcount);
|
||
|
if( limit >= 0 && *headcount >= limit )
|
||
|
break;
|
||
|
|
||
|
if( ++forgetcount > FORGET_INTERVAL )
|
||
|
forgetcount = 0;
|
||
|
|
||
|
if(( ret = forget_head_shift( fr, &newhead, !forgetcount )) <= 0 )
|
||
|
return ret;
|
||
|
|
||
|
if( head_check( newhead ) && (ret = decode_header( fr, newhead, &freeformat_count )))
|
||
|
break;
|
||
|
} while( 1 );
|
||
|
|
||
|
if( ret < 0 )
|
||
|
return ret;
|
||
|
|
||
|
if( limit >= 0 && *headcount >= limit )
|
||
|
return PARSE_END;
|
||
|
|
||
|
// If the new header ist good, it is already decoded.
|
||
|
*newheadp = newhead;
|
||
|
|
||
|
return PARSE_GOOD;
|
||
|
}
|
||
|
|
||
|
// the newhead is bad, so let's check if it is something special, otherwise just resync.
|
||
|
static int wetwork( mpg123_handle_t *fr, ulong *newheadp )
|
||
|
{
|
||
|
int ret = PARSE_ERR;
|
||
|
ulong newhead = *newheadp;
|
||
|
|
||
|
*newheadp = 0;
|
||
|
|
||
|
// classic ID3 tags. Read, then start parsing again.
|
||
|
if(( newhead & 0xffffff00 ) == ( 'T'<<24 )+( 'A'<<16 )+( 'G'<<8 ))
|
||
|
{
|
||
|
fr->id3buf[0] = (byte)((newhead >> 24) & 0xff);
|
||
|
fr->id3buf[1] = (byte)((newhead >> 16) & 0xff);
|
||
|
fr->id3buf[2] = (byte)((newhead >> 8) & 0xff);
|
||
|
fr->id3buf[3] = (byte)( newhead & 0xff);
|
||
|
|
||
|
if(( ret = fr->rd->fullread( fr, fr->id3buf + 4, 124 )) < 0 )
|
||
|
return ret;
|
||
|
|
||
|
fr->metaflags |= MPG123_NEW_ID3|MPG123_ID3;
|
||
|
fr->rdat.flags |= READER_ID3TAG; // that marks id3v1
|
||
|
|
||
|
return PARSE_AGAIN;
|
||
|
}
|
||
|
|
||
|
// this is similar to initial junk skipping code...
|
||
|
// check for id3v2; first three bytes (of 4) are "ID3"
|
||
|
if(( newhead & (ulong)0xffffff00 ) == (ulong)0x49443300 )
|
||
|
{
|
||
|
return handle_id3v2( fr, newhead );
|
||
|
}
|
||
|
|
||
|
// now we got something bad at hand, try to recover.
|
||
|
if( !( fr->p.flags & MPG123_NO_RESYNC ))
|
||
|
{
|
||
|
long try = 0;
|
||
|
long limit = fr->p.resync_limit;
|
||
|
uint forgetcount = 0;
|
||
|
|
||
|
// if a resync is needed the bitreservoir of previous frames is no longer valid
|
||
|
fr->bitreservoir = 0;
|
||
|
|
||
|
do // ... shift the header with additional single bytes until be found something that could be a header.
|
||
|
{
|
||
|
try++;
|
||
|
|
||
|
if( limit >= 0 && try >= limit )
|
||
|
break;
|
||
|
|
||
|
if( ++forgetcount > FORGET_INTERVAL )
|
||
|
forgetcount = 0;
|
||
|
|
||
|
if(( ret = forget_head_shift( fr, &newhead, !forgetcount )) <= 0 )
|
||
|
{
|
||
|
*newheadp = newhead;
|
||
|
return ret ? ret : PARSE_END;
|
||
|
}
|
||
|
} while( !head_check( newhead ));
|
||
|
|
||
|
*newheadp = newhead;
|
||
|
|
||
|
// now we either got something that could be a header, or we gave up.
|
||
|
if( limit >= 0 && try >= limit )
|
||
|
{
|
||
|
fr->err = MPG123_RESYNC_FAIL;
|
||
|
return PARSE_ERR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fr->oldhead = 0;
|
||
|
return PARSE_RESYNC;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fr->err = MPG123_OUT_OF_SYNC;
|
||
|
return PARSE_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// that's a big one: read the next frame. 1 is success, <= 0 is some error
|
||
|
// special error READER_MORE means: Please feed more data and try again.
|
||
|
int read_frame( mpg123_handle_t *fr )
|
||
|
{
|
||
|
// TODO: rework this thing
|
||
|
int freeformat_count = 0;
|
||
|
int oldsize = fr->framesize;
|
||
|
int oldphase = fr->halfphase;
|
||
|
long headcount = 0;
|
||
|
byte *newbuf;
|
||
|
ulong newhead;
|
||
|
mpg_off_t framepos;
|
||
|
int ret;
|
||
|
|
||
|
fr->fsizeold = fr->framesize; // for Layer3
|
||
|
|
||
|
if( halfspeed_do( fr ) == 1 )
|
||
|
return 1;
|
||
|
|
||
|
read_again:
|
||
|
// in case we are looping to find a valid frame, discard any buffered data before the current position.
|
||
|
// this is essential to prevent endless looping, always going back to the beginning when feeder buffer is exhausted.
|
||
|
if( fr->rd->forget != NULL )
|
||
|
fr->rd->forget( fr );
|
||
|
|
||
|
if(( ret = fr->rd->head_read( fr, &newhead )) <= 0 )
|
||
|
goto read_frame_bad;
|
||
|
init_resync:
|
||
|
if( !fr->firsthead && !head_check( newhead ))
|
||
|
{
|
||
|
ret = skip_junk(fr, &newhead, &headcount);
|
||
|
|
||
|
if( ret < 0 )
|
||
|
goto read_frame_bad;
|
||
|
else if( ret == PARSE_AGAIN )
|
||
|
goto read_again;
|
||
|
else if( ret == PARSE_RESYNC )
|
||
|
goto init_resync;
|
||
|
else if( ret == PARSE_END )
|
||
|
{
|
||
|
ret = 0;
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = head_check( newhead );
|
||
|
if( ret ) ret = decode_header( fr, newhead, &freeformat_count );
|
||
|
|
||
|
if( ret < 0 )
|
||
|
goto read_frame_bad;
|
||
|
else if( ret == PARSE_AGAIN )
|
||
|
goto read_again;
|
||
|
else if( ret == PARSE_RESYNC )
|
||
|
goto init_resync;
|
||
|
else if( ret == PARSE_END )
|
||
|
{
|
||
|
ret = 0;
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
|
||
|
if( ret == PARSE_BAD )
|
||
|
{
|
||
|
// header was not good.
|
||
|
ret = wetwork( fr, &newhead ); // Messy stuff, handle junk, resync ...
|
||
|
|
||
|
if( ret < 0 )
|
||
|
goto read_frame_bad;
|
||
|
else if( ret == PARSE_AGAIN )
|
||
|
goto read_again;
|
||
|
else if( ret == PARSE_RESYNC )
|
||
|
goto init_resync;
|
||
|
else if( ret == PARSE_END )
|
||
|
{
|
||
|
ret = 0;
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
|
||
|
// normally, we jumped already.
|
||
|
// if for some reason everything's fine to continue, do continue.
|
||
|
if( ret != PARSE_GOOD )
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
|
||
|
if( !fr->firsthead )
|
||
|
{
|
||
|
ret = do_readahead( fr, newhead );
|
||
|
|
||
|
// readahead can fail mit NEED_MORE, in which case we must also make
|
||
|
// the just read header available again for next go
|
||
|
if( ret < 0 ) fr->rd->back_bytes( fr, 4 );
|
||
|
|
||
|
if( ret < 0 )
|
||
|
goto read_frame_bad;
|
||
|
else if( ret == PARSE_AGAIN )
|
||
|
goto read_again;
|
||
|
else if( ret == PARSE_RESYNC )
|
||
|
goto init_resync;
|
||
|
else if( ret == PARSE_END )
|
||
|
{
|
||
|
ret = 0;
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now we should have our valid header and proceed to reading the frame.
|
||
|
|
||
|
// if filepos is invalid, so is framepos
|
||
|
framepos = fr->rd->tell( fr ) - 4;
|
||
|
|
||
|
// flip/init buffer for Layer 3
|
||
|
newbuf = fr->bsspace[fr->bsnum] + 512;
|
||
|
|
||
|
// read main data into memory
|
||
|
if(( ret = fr->rd->read_frame_body( fr, newbuf, fr->framesize )) < 0 )
|
||
|
{
|
||
|
// if failed: flip back
|
||
|
goto read_frame_bad;
|
||
|
}
|
||
|
|
||
|
fr->bsbufold = fr->bsbuf;
|
||
|
fr->bsbuf = newbuf;
|
||
|
fr->bsnum = (fr->bsnum + 1) & 1;
|
||
|
|
||
|
if( !fr->firsthead )
|
||
|
{
|
||
|
fr->firsthead = newhead; // _now_ it's time to store it... the first real header */
|
||
|
// this is the first header of our current stream segment.
|
||
|
// it is only the actual first header of the whole stream when fr->num is still below zero!
|
||
|
// think of resyncs where firsthead has been reset for format flexibility.
|
||
|
if( fr->num < 0 )
|
||
|
{
|
||
|
fr->audio_start = framepos;
|
||
|
|
||
|
// only check for LAME tag at beginning of whole stream
|
||
|
// ... when there indeed is one in between, it's the user's problem.
|
||
|
if( fr->lay == 3 && check_lame_tag( fr ) == 1 )
|
||
|
{
|
||
|
// ...in practice, Xing/LAME tags are layer 3 only.
|
||
|
if( fr->rd->forget != NULL )
|
||
|
fr->rd->forget( fr );
|
||
|
|
||
|
fr->oldhead = 0;
|
||
|
goto read_again;
|
||
|
}
|
||
|
|
||
|
// now adjust volume
|
||
|
do_rva( fr );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fr->bitindex = 0;
|
||
|
fr->wordpointer = (byte *)fr->bsbuf;
|
||
|
|
||
|
// question: How bad does the floating point value get with repeated recomputation?
|
||
|
// also, considering that we can play the file or parts of many times.
|
||
|
if( ++fr->mean_frames != 0 )
|
||
|
fr->mean_framesize = ((fr->mean_frames - 1) * fr->mean_framesize + compute_bpf( fr )) / fr->mean_frames ;
|
||
|
|
||
|
fr->num++; // 0 for first frame!
|
||
|
|
||
|
if(!( fr->state_flags & FRAME_FRANKENSTEIN ) && (( fr->track_frames > 0 && fr->num >= fr->track_frames ) || ( fr->gapless_frames > 0 && fr->num >= fr->gapless_frames )))
|
||
|
fr->state_flags |= FRAME_FRANKENSTEIN;
|
||
|
|
||
|
halfspeed_prepare( fr );
|
||
|
|
||
|
// index the position
|
||
|
fr->input_offset = framepos;
|
||
|
|
||
|
// keep track of true frame positions in our frame index.
|
||
|
// but only do so when we are sure that the frame number is accurate...
|
||
|
if(( fr->state_flags & FRAME_ACCURATE ) && FI_NEXT( fr->index, fr->num ))
|
||
|
fi_add( &fr->index, framepos );
|
||
|
|
||
|
if( fr->silent_resync > 0 )
|
||
|
fr->silent_resync--;
|
||
|
|
||
|
if( fr->rd->forget != NULL )
|
||
|
fr->rd->forget( fr );
|
||
|
|
||
|
fr->to_decode = fr->to_ignore = TRUE;
|
||
|
if( fr->error_protection )
|
||
|
fr->crc = getbits( fr, 16 ); // skip crc
|
||
|
|
||
|
// let's check for header change after deciding that the new one is good
|
||
|
// and actually having read a frame.
|
||
|
// header_change > 1: decoder structure has to be updated
|
||
|
// preserve header_change value from previous runs if it is serious.
|
||
|
// If we still have a big change pending, it should be dealt with outside,
|
||
|
// fr->header_change set to zero afterwards.
|
||
|
if( fr->header_change < 2 )
|
||
|
{
|
||
|
fr->header_change = 2; // output format change is possible...
|
||
|
if( fr->oldhead ) // check a following header for change
|
||
|
{
|
||
|
if( fr->oldhead == newhead )
|
||
|
{
|
||
|
fr->header_change = 0;
|
||
|
}
|
||
|
else if( head_compatible( fr->oldhead, newhead ))
|
||
|
{
|
||
|
// headers that match in this test behave the same for the outside world.
|
||
|
// namely: same decoding routines, same amount of decoded data.
|
||
|
fr->header_change = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fr->state_flags |= FRAME_FRANKENSTEIN;
|
||
|
}
|
||
|
}
|
||
|
else if( fr->firsthead && !head_compatible( fr->firsthead, newhead ))
|
||
|
{
|
||
|
fr->state_flags |= FRAME_FRANKENSTEIN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fr->oldhead = newhead;
|
||
|
|
||
|
return 1;
|
||
|
read_frame_bad:
|
||
|
|
||
|
// also if we searched for valid data in vein, we can forget skipped data.
|
||
|
// otherwise, the feeder would hold every dead old byte in memory until the first valid frame!
|
||
|
if( fr->rd->forget != NULL )
|
||
|
fr->rd->forget( fr );
|
||
|
|
||
|
fr->silent_resync = 0;
|
||
|
if( fr->err == MPG123_OK )
|
||
|
fr->err = MPG123_ERR_READER;
|
||
|
fr->framesize = oldsize;
|
||
|
fr->halfphase = oldphase;
|
||
|
|
||
|
// that return code might be inherited from some feeder action, or reader error.
|
||
|
return ret;
|
||
|
}
|