123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- /*
- * Xing VBR tagging for LAME.
- *
- * Copyright (c) 1999 A.L. Faber
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
- /* $Id: VbrTag.c,v 1.20 2001/02/27 09:59:16 robert Exp $ */
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include "machine.h"
- #if defined(__riscos__) && defined(FPA10)
- #include "ymath.h"
- #else
- #include <math.h>
- #endif
- #include "VbrTag.h"
- #include "version.h"
- #include "bitstream.h"
- #include "VbrTag.h"
- #include <assert.h>
- #ifdef WITH_DMALLOC
- #include <dmalloc.h>
- #endif
- #ifdef _DEBUG
- /* #define DEBUG_VBRTAG */
- #endif
- /*
- // 4 bytes for Header Tag
- // 4 bytes for Header Flags
- // 100 bytes for entry (NUMTOCENTRIES)
- // 4 bytes for FRAME SIZE
- // 4 bytes for STREAM_SIZE
- // 4 bytes for VBR SCALE. a VBR quality indicator: 0=best 100=worst
- // 20 bytes for LAME tag. for example, "LAME3.12 (beta 6)"
- // ___________
- // 140 bytes
- */
- #define VBRHEADERSIZE (NUMTOCENTRIES+4+4+4+4+4)
- /* the size of the Xing header (MPEG1 and MPEG2) in kbps */
- #define XING_BITRATE1 128
- #define XING_BITRATE2 64
- #define XING_BITRATE25 32
- const static char VBRTag[]={"Xing"};
- const int SizeOfEmptyFrame[2][2]=
- {
- {17,9},
- {32,17},
- };
- /***********************************************************************
- * Robert Hegemann 2001-01-17
- ***********************************************************************/
- void addVbr(VBR_seek_info_t * v, int bitrate)
- {
- int i;
- v->sum += bitrate;
- v->seen ++;
-
- if (v->seen < v->want) {
- return;
- }
- if (v->pos < v->size) {
- v->bag[v->pos] = v->sum;
- v->pos ++;
- v->seen = 0;
- }
- if (v->pos == v->size) {
- for (i = 1; i < v->size; i += 2) {
- v->bag[i/2] = v->bag[i];
- }
- v->want *= 2;
- v->pos /= 2;
- }
- }
- void Xing_seek_table(VBR_seek_info_t * v, unsigned char *t)
- {
- int i, index;
- int seek_point;
-
- if (v->pos <= 0)
- return;
-
- for (i = 1; i < NUMTOCENTRIES; ++i) {
- float j = i/(float)NUMTOCENTRIES, act, sum;
- index = (int)(floor(j * v->pos));
- if (index > v->pos-1)
- index = v->pos-1;
- act = v->bag[index];
- sum = v->sum;
- seek_point = (int)(256. * act / sum);
- if (seek_point > 255)
- seek_point = 255;
- t[i] = seek_point;
- }
- }
- #if 0
- void print_seeking(unsigned char *t)
- {
- int i;
-
- printf("seeking table ");
- for (i = 0; i < NUMTOCENTRIES; ++i) {
- printf(" %d ", t[i]);
- }
- printf("\n");
- }
- #endif
- /****************************************************************************
- * AddVbrFrame: Add VBR entry, used to fill the VBR the TOC entries
- * Paramters:
- * nStreamPos: how many bytes did we write to the bitstream so far
- * (in Bytes NOT Bits)
- ****************************************************************************
- */
- void AddVbrFrame(lame_global_flags *gfp)
- {
- lame_internal_flags *gfc = gfp->internal_flags;
- int kbps = bitrate_table[gfp->version][gfc->bitrate_index];
-
- if (gfc->VBR_seek_table.bag == NULL) {
- gfc->VBR_seek_table.sum = 0;
- gfc->VBR_seek_table.seen = 0;
- gfc->VBR_seek_table.want = 1;
- gfc->VBR_seek_table.pos = 0;
- gfc->VBR_seek_table.bag = malloc (400*sizeof(int));
- if (gfc->VBR_seek_table.bag != NULL) {
- gfc->VBR_seek_table.size = 400;
- }
- else {
- gfc->VBR_seek_table.size = 0;
- ERRORF (gfc,"Error: can't allocate VbrFrames buffer\n");
- return;
- }
- }
- addVbr(&gfc->VBR_seek_table, kbps);
- gfp->nVbrNumFrames++;
- }
- /*-------------------------------------------------------------*/
- static int ExtractI4(unsigned char *buf)
- {
- int x;
- /* big endian extract */
- x = buf[0];
- x <<= 8;
- x |= buf[1];
- x <<= 8;
- x |= buf[2];
- x <<= 8;
- x |= buf[3];
- return x;
- }
- void CreateI4(unsigned char *buf, int nValue)
- {
- /* big endian create */
- buf[0]=(nValue>>24)&0xff;
- buf[1]=(nValue>>16)&0xff;
- buf[2]=(nValue>> 8)&0xff;
- buf[3]=(nValue )&0xff;
- }
- /*-------------------------------------------------------------*/
- /* Same as GetVbrTag below, but only checks for the Xing tag.
- requires buf to contain only 40 bytes */
- /*-------------------------------------------------------------*/
- int CheckVbrTag(unsigned char *buf)
- {
- int h_id, h_mode, h_sr_index;
- /* get selected MPEG header data */
- h_id = (buf[1] >> 3) & 1;
- h_sr_index = (buf[2] >> 2) & 3;
- h_mode = (buf[3] >> 6) & 3;
- /* determine offset of header */
- if( h_id )
- {
- /* mpeg1 */
- if( h_mode != 3 ) buf+=(32+4);
- else buf+=(17+4);
- }
- else
- {
- /* mpeg2 */
- if( h_mode != 3 ) buf+=(17+4);
- else buf+=(9+4);
- }
- if( buf[0] != VBRTag[0] ) return 0; /* fail */
- if( buf[1] != VBRTag[1] ) return 0; /* header not found*/
- if( buf[2] != VBRTag[2] ) return 0;
- if( buf[3] != VBRTag[3] ) return 0;
- return 1;
- }
- int GetVbrTag(VBRTAGDATA *pTagData, unsigned char *buf)
- {
- int i, head_flags;
- int h_bitrate,h_id, h_mode, h_sr_index;
- /* get Vbr header data */
- pTagData->flags = 0;
- /* get selected MPEG header data */
- h_id = (buf[1] >> 3) & 1;
- h_sr_index = (buf[2] >> 2) & 3;
- h_mode = (buf[3] >> 6) & 3;
- h_bitrate = ((buf[2]>>4)&0xf);
- h_bitrate = bitrate_table[h_id][h_bitrate];
- /* determine offset of header */
- if( h_id )
- {
- /* mpeg1 */
- if( h_mode != 3 ) buf+=(32+4);
- else buf+=(17+4);
- }
- else
- {
- /* mpeg2 */
- if( h_mode != 3 ) buf+=(17+4);
- else buf+=(9+4);
- }
- if( buf[0] != VBRTag[0] ) return 0; /* fail */
- if( buf[1] != VBRTag[1] ) return 0; /* header not found*/
- if( buf[2] != VBRTag[2] ) return 0;
- if( buf[3] != VBRTag[3] ) return 0;
- buf+=4;
- pTagData->h_id = h_id;
- pTagData->samprate = samplerate_table[h_id][h_sr_index];
- if( h_id == 0 )
- pTagData->samprate >>= 1;
- head_flags = pTagData->flags = ExtractI4(buf); buf+=4; /* get flags */
- if( head_flags & FRAMES_FLAG )
- {
- pTagData->frames = ExtractI4(buf); buf+=4;
- }
- if( head_flags & BYTES_FLAG )
- {
- pTagData->bytes = ExtractI4(buf); buf+=4;
- }
- if( head_flags & TOC_FLAG )
- {
- if( pTagData->toc != NULL )
- {
- for(i=0;i<NUMTOCENTRIES;i++)
- pTagData->toc[i] = buf[i];
- }
- buf+=NUMTOCENTRIES;
- }
- pTagData->vbr_scale = -1;
- if( head_flags & VBR_SCALE_FLAG )
- {
- pTagData->vbr_scale = ExtractI4(buf); buf+=4;
- }
- pTagData->headersize =
- ((h_id+1)*72000*h_bitrate) / pTagData->samprate;
- #ifdef DEBUG_VBRTAG
- DEBUGF("\n\n********************* VBR TAG INFO *****************\n");
- DEBUGF("tag :%s\n",VBRTag);
- DEBUGF("head_flags :%d\n",head_flags);
- DEBUGF("bytes :%d\n",pTagData->bytes);
- DEBUGF("frames :%d\n",pTagData->frames);
- DEBUGF("VBR Scale :%d\n",pTagData->vbr_scale);
- DEBUGF("toc:\n");
- if( pTagData->toc != NULL )
- {
- for(i=0;i<NUMTOCENTRIES;i++)
- {
- if( (i%10) == 0 ) DEBUGF("\n");
- DEBUGF(" %3d", (int)(pTagData->toc[i]));
- }
- }
- DEBUGF("\n***************** END OF VBR TAG INFO ***************\n");
- #endif
- return 1; /* success */
- }
- /****************************************************************************
- * InitVbrTag: Initializes the header, and write empty frame to stream
- * Paramters:
- * fpStream: pointer to output file stream
- * nMode : Channel Mode: 0=STEREO 1=JS 2=DS 3=MONO
- ****************************************************************************
- */
- int InitVbrTag(lame_global_flags *gfp)
- {
- int nMode,SampIndex;
- lame_internal_flags *gfc = gfp->internal_flags;
- #define MAXFRAMESIZE 576
- // u_char pbtStreamBuffer[MAXFRAMESIZE];
- nMode = gfp->mode;
- SampIndex = gfc->samplerate_index;
- /* Clear Frame position array variables */
- gfp->pVbrFrames=NULL;
- gfp->nVbrNumFrames=0;
- gfp->nVbrFrameBufferSize=0;
- /* Clear stream buffer */
- // memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer));
- /* Reserve the proper amount of bytes */
- if (nMode==3)
- {
- gfp->nZeroStreamSize=SizeOfEmptyFrame[gfp->version][1]+4;
- }
- else
- {
- gfp->nZeroStreamSize=SizeOfEmptyFrame[gfp->version][0]+4;
- }
- /*
- // Xing VBR pretends to be a 48kbs layer III frame. (at 44.1kHz).
- // (at 48kHz they use 56kbs since 48kbs frame not big enough for
- // table of contents)
- // let's always embed Xing header inside a 64kbs layer III frame.
- // this gives us enough room for a LAME version string too.
- // size determined by sampling frequency (MPEG1)
- // 32kHz: 216 bytes@48kbs 288bytes@ 64kbs
- // 44.1kHz: 156 bytes 208bytes@64kbs (+1 if padding = 1)
- // 48kHz: 144 bytes 192
- //
- // MPEG 2 values are the same since the framesize and samplerate
- // are each reduced by a factor of 2.
- */
- {
- int i,bitrate,tot;
- if (1==gfp->version) {
- bitrate = XING_BITRATE1;
- } else {
- if (gfp->out_samplerate < 16000 )
- bitrate = XING_BITRATE25;
- else
- bitrate = XING_BITRATE2;
- }
- gfp->TotalFrameSize=
- ((gfp->version+1)*72000*bitrate) / gfp->out_samplerate;
- tot = (gfp->nZeroStreamSize+VBRHEADERSIZE);
- tot += 20; /* extra 20 bytes for LAME & version string */
- assert(gfp->TotalFrameSize >= tot );
- assert(gfp->TotalFrameSize <= MAXFRAMESIZE );
- for (i=0; i<gfp->TotalFrameSize; ++i)
- add_dummy_byte(gfp,0);
- }
- /* Success */
- return 0;
- }
- /****************************************************************************
- * PutVbrTag: Write final VBR tag to the file
- * Paramters:
- * lpszFileName: filename of MP3 bit stream
- * nVbrScale : encoder quality indicator (0..100)
- ****************************************************************************
- */
- int PutVbrTag(lame_global_flags *gfp,FILE *fpStream,int nVbrScale)
- {
- lame_internal_flags * gfc = gfp->internal_flags;
- long lFileSize;
- int nStreamIndex;
- char abyte,bbyte;
- u_char btToc[NUMTOCENTRIES];
- u_char pbtStreamBuffer[MAXFRAMESIZE];
- char str1[80];
- unsigned char id3v2Header[10];
- size_t id3v2TagSize;
- if (gfc->VBR_seek_table.pos <= 0)
- return -1;
- /* Clear stream buffer */
- memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer));
- /* Seek to end of file*/
- fseek(fpStream,0,SEEK_END);
- /* Get file size */
- lFileSize=ftell(fpStream);
- /* Abort if file has zero length. Yes, it can happen :) */
- if (lFileSize==0)
- return -1;
- /*
- * The VBR tag may NOT be located at the beginning of the stream.
- * If an ID3 version 2 tag was added, then it must be skipped to write
- * the VBR tag data.
- */
- /* seek to the beginning of the stream */
- fseek(fpStream,0,SEEK_SET);
- /* read 10 bytes in case there's an ID3 version 2 header here */
- fread(id3v2Header,1,sizeof id3v2Header,fpStream);
- /* does the stream begin with the ID3 version 2 file identifier? */
- if (!strncmp((char *)id3v2Header,"ID3",3)) {
- /* the tag size (minus the 10-byte header) is encoded into four
- * bytes where the most significant bit is clear in each byte */
- id3v2TagSize=(((id3v2Header[6] & 0x7f)<<21)
- | ((id3v2Header[7] & 0x7f)<<14)
- | ((id3v2Header[8] & 0x7f)<<7)
- | (id3v2Header[9] & 0x7f))
- + sizeof id3v2Header;
- } else {
- /* no ID3 version 2 tag in this stream */
- id3v2TagSize=0;
- }
- /* Seek to first real frame */
- fseek(fpStream,id3v2TagSize+gfp->TotalFrameSize,SEEK_SET);
- /* Read the header (first valid frame) */
- fread(pbtStreamBuffer,4,1,fpStream);
- /* the default VBR header. 48 kbps layer III, no padding, no crc */
- /* but sampling freq, mode andy copyright/copy protection taken */
- /* from first valid frame */
- pbtStreamBuffer[0]=(u_char) 0xff;
- abyte = (pbtStreamBuffer[1] & (char) 0xf1);
- { int bitrate;
- if (1==gfp->version) {
- bitrate = XING_BITRATE1;
- } else {
- if (gfp->out_samplerate < 16000 )
- bitrate = XING_BITRATE25;
- else
- bitrate = XING_BITRATE2;
- }
- bbyte = 16*BitrateIndex(bitrate,gfp->version,gfp->out_samplerate);
- }
- /* Use as much of the info from the real frames in the
- * Xing header: samplerate, channels, crc, etc...
- */
- if (gfp->version==1) {
- /* MPEG1 */
- pbtStreamBuffer[1]=abyte | (char) 0x0a; /* was 0x0b; */
- abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */
- pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG1 frame */
- }else{
- /* MPEG2 */
- pbtStreamBuffer[1]=abyte | (char) 0x02; /* was 0x03; */
- abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */
- pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG2 frame */
- }
- /*Seek to the beginning of the stream */
- fseek(fpStream,id3v2TagSize,SEEK_SET);
- /* Clear all TOC entries */
- memset(btToc,0,sizeof(btToc));
- Xing_seek_table (&gfc->VBR_seek_table, btToc);
- /* print_seeking (btToc); */
- /* Start writing the tag after the zero frame */
- nStreamIndex=gfp->nZeroStreamSize;
- /* Put Vbr tag */
- pbtStreamBuffer[nStreamIndex++]=VBRTag[0];
- pbtStreamBuffer[nStreamIndex++]=VBRTag[1];
- pbtStreamBuffer[nStreamIndex++]=VBRTag[2];
- pbtStreamBuffer[nStreamIndex++]=VBRTag[3];
- /* Put header flags */
- CreateI4(&pbtStreamBuffer[nStreamIndex],FRAMES_FLAG+BYTES_FLAG+TOC_FLAG+VBR_SCALE_FLAG);
- nStreamIndex+=4;
- /* Put Total Number of frames */
- CreateI4(&pbtStreamBuffer[nStreamIndex],gfp->nVbrNumFrames);
- nStreamIndex+=4;
- /* Put Total file size */
- CreateI4(&pbtStreamBuffer[nStreamIndex],(int)lFileSize);
- nStreamIndex+=4;
- /* Put TOC */
- memcpy(&pbtStreamBuffer[nStreamIndex],btToc,sizeof(btToc));
- nStreamIndex+=sizeof(btToc);
- /* Put VBR SCALE */
- CreateI4(&pbtStreamBuffer[nStreamIndex],nVbrScale);
- nStreamIndex+=4;
- /* Put LAME ID */
- sprintf ( str1, "LAME%s", get_lame_short_version () );
- strncpy ( (char*)pbtStreamBuffer + nStreamIndex, str1, 20 );
- nStreamIndex += 20;
- #ifdef DEBUG_VBRTAG
- {
- VBRTAGDATA TestHeader;
- GetVbrTag(&TestHeader,pbtStreamBuffer);
- }
- #endif
- /* Put it all to disk again */
- if (fwrite(pbtStreamBuffer,(unsigned int)gfp->TotalFrameSize,1,fpStream)!=1)
- {
- return -1;
- }
- /* Save to delete the frame buffer */
- free(gfp->pVbrFrames);
- gfp->pVbrFrames=NULL;
- return 0; /* success */
- }
|