/* * 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. */ #include "machine.h" #include #include "VbrTag.h" #include "version.h" #ifdef _DEBUG /* #define DEBUG_VBRTAG */ #endif int SizeOfEmptyFrame[2][2]= { {32,17}, {17,9}, }; static u_char pbtStreamBuffer[216]; static long g_Position[NUMTOCENTRIES]; static int nZeroStreamSize=0; static int TotalFrameSize=0; static char VBRTag[]={"Xing"}; int* pVbrFrames=NULL; int nVbrNumFrames=0; int nVbrFrameBufferSize=0; /**************************************************************************** * 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(int nStreamPos) { /* Simple exponential growing buffer */ if (pVbrFrames==NULL || nVbrFrameBufferSize==0) { /* Start with 100 frames */ nVbrFrameBufferSize=100; /* Allocate them */ pVbrFrames=(int*)malloc((size_t)(nVbrFrameBufferSize*sizeof(int))); } /* Is buffer big enough to store this new frame */ if (nVbrNumFrames==nVbrFrameBufferSize) { /* Guess not, double th e buffer size */ nVbrFrameBufferSize*=2; /* Allocate new buffer */ pVbrFrames=(int*)realloc(pVbrFrames,(size_t)(nVbrFrameBufferSize*sizeof(int))); } /* Store values */ pVbrFrames[nVbrNumFrames++]=nStreamPos; } /*-------------------------------------------------------------*/ 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_id, h_mode, h_sr_index; static int sr_table[4] = { 44100, 48000, 32000, 99999 }; /* 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; /* 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 = sr_table[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;itoc[i] = buf[i]; } buf+=NUMTOCENTRIES; } pTagData->vbr_scale = -1; if( head_flags & VBR_SCALE_FLAG ) { pTagData->vbr_scale = ExtractI4(buf); buf+=4; } #ifdef DEBUG_VBRTAG printf("\n\n********************* VBR TAG INFO *****************\n"); printf("tag :%s\n",VBRTag); printf("head_flags :%d\n",head_flags); printf("bytes :%d\n",pTagData->bytes); printf("frames :%d\n",pTagData->frames); printf("VBR Scale :%d\n",pTagData->vbr_scale); printf("toc:\n"); if( pTagData->toc != NULL ) { for(i=0;itoc[i])); } } printf("\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 * nVersion: 0= MPEG1 1=MPEG2 * nMode : Channel Mode: 0=STEREO 1=JS 2=DS 3=MONO **************************************************************************** */ int InitVbrTag(Bit_stream_struc* pBs,int nVersion, int nMode, int SampIndex) { int i; /* Clear Frame position array variables */ pVbrFrames=NULL; nVbrNumFrames=0; nVbrFrameBufferSize=0; /* Clear struct */ memset(g_Position,0x00,sizeof(g_Position)); /* Clear stream buffer */ memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer)); /* Set TOC values to 255 */ for (i=0;i2) { fprintf(stderr,"illegal sampling frequency index\n"); exit(-1); } TotalFrameSize= framesize[SampIndex]; tot = (nZeroStreamSize+VBRHEADERSIZE); tot += 20; /* extra 20 bytes for LAME & version string */ if (TotalFrameSize < tot ) { fprintf(stderr,"Xing VBR header problem...use -t\n"); exit(-1); } } /* Put empty bytes into the bitstream */ for (i=0;i255) fRelStreamPos=255; /* Assign toc entry value */ btToc[i]=(u_char) fRelStreamPos; } /* Start writing the tag after the zero frame */ nStreamIndex=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],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_version()); strncpy((char *)&pbtStreamBuffer[nStreamIndex],str1,(size_t) 20); nStreamIndex+=20; #ifdef DEBUG_VBRTAG { VBRTAGDATA TestHeader; GetVbrTag(&TestHeader,pbtStreamBuffer); } #endif /* Put it all to disk again */ if (fwrite(pbtStreamBuffer,TotalFrameSize,1,fpStream)!=1) { return -1; } fclose(fpStream); /* Save to delete the frame buffer */ free(pVbrFrames); pVbrFrames=NULL; return 0; /* success */ } /*-------------------------------------------------------------*/ int SeekPoint(unsigned char TOC[NUMTOCENTRIES], int file_bytes, float percent) { /* interpolate in TOC to get file seek point in bytes */ int a, seekpoint; float fa, fb, fx; if( percent < (float)0.0 ) percent = (float)0.0; if( percent > (float)100.0 ) percent = (float)100.0; a = (int)percent; if( a > 99 ) a = 99; fa = TOC[a]; if( a < 99 ) { fb = TOC[a+1]; } else { fb = (float)256.0; } fx = fa + (fb-fa)*(percent-a); seekpoint = (int)(((float)(1.0/256.0))*fx*file_bytes); return seekpoint; } /*-------------------------------------------------------------*/