Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1534889
  • 博文数量: 114
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 1357
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 18:13
文章分类
文章存档

2010年(8)

2009年(9)

2008年(27)

2007年(62)

2006年(8)

我的朋友

分类: WINDOWS

2007-12-08 00:44:30

北京理工大学  20981  陈罡
继续上面一篇的内容。既然libmad已经可以顺利的移植到win mobile平台上了,
那么如何使用这个libmad库呢?很遗憾,linux平台下面对于libmad的文档描述
不是非常清晰。呵呵,按照他们的思维就是一切都在代码里面了。但是对于普通
的开发者来说,可能只是涉及到对库的使用,而不是为了使用一个库去学习mp3
的编码、解码原理(如果能够通过这个库弄明白了这些,自然再好不过了)。
我仔细的查看了一下这个libmad的相关开源工程。发现了一个很有趣的项目,它
就是——madlld-1.1p1(我下载的最新版本,不知道现在是否又有更新的版本出来了)
可以google一下,然后直接下载源代码。这个项目的说明如下:
This program is a "how to" about libmad's low-level API. The intended
use of this program is to serve as tutorial or demonstration for
programmers wiling to learn how the currently undocumented library API
can be used. As a side effect the program can be used to decode MPEG
audio streams to raw PCM samples.
这个项目本身就是做为一个向导或者入门的文件,教会大家如何使用libmad的底层
API函数的。因此,把这个项目的代码拿过来仔细分析一下,libmad库的使用方法
就变得非常简单了。它里面的代码写得非常不错,很精炼,可以很容易的移植到
win mobile平台上去。madlld项目的核心部分由3个.c文件组成,最关键的代码在
madlld.c这个文件中的MpegAudioDecoder函数,该函数的定义如下:
static int MpegAudioDecoder(FILE *InputFp, FILE *OutputFp)
现在就把libmad库的API核心部分使用的代码贴出来,简单分析一下备忘:
// 这里用8K的输出缓冲区,这对于mp3转pcm16仅限于文件操作的程序来说是足够的
// 但是对于编解码边播放的应用场合而言就太小了,根本不够
#define INPUT_BUFFER_SIZE (5*8192)
#define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */
static int MpegAudioDecoder(FILE *InputFp, FILE *OutputFp)
{
 struct mad_stream Stream;
 struct mad_frame Frame;
 struct mad_synth Synth;
 mad_timer_t   Timer;
 unsigned char  InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD],
      OutputBuffer[OUTPUT_BUFFER_SIZE],
      *OutputPtr=OutputBuffer,
      *GuardPtr=NULL;
 const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE;
 int     Status=0,
      i;
 unsigned long  FrameCount=0;
 bstdfile_t   *BstdFile;
 /* First the structures used by libmad must be initialized. */
 mad_stream_init(&Stream);
 mad_frame_init(&Frame);
 mad_synth_init(&Synth);
 mad_timer_reset(&Timer);
 /* Decoding options can here be set in the options field of the
  * Stream structure.
  */
 /* {1} When decoding from a file we need to know when the end of
  * the file is reached at the same time as the last bytes are read
  * (see also the comment marked {3} bellow). Neither the standard
  * C fread() function nor the POSIX read() system call provides
  * this feature. We thus need to perform our reads through an
  * interface having this feature, this is implemented here by the
  * bstdfile.c module.
  */
 // 这里说了那么多其实就是想要一个文件读取函数,在读取的同时知道是否到达文件末尾。
 // 普通的fread是不行的,只有当前读取是否成功,对于在当前的读取完成后是否到达文件
 // 末尾没有任何提示。所以这里采用了自己封装的文件操作函数。
 BstdFile=NewBstdFile(InputFp);
 if(BstdFile==NULL)
 {
  fprintf(stderr,"%s: can't create a new bstdfile_t (%s).\n",
    ProgName,strerror(errno));
  return(1);
 }
 /* This is the decoding loop. */
 do
 {
  /* The input bucket must be filled if it becomes empty or if
   * it's the first execution of the loop.
   */
  if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN)
  {
   size_t   ReadSize,
       Remaining;
   unsigned char *ReadStart;
   /* {2} libmad may not consume all bytes of the input
    * buffer. If the last frame in the buffer is not wholly
    * contained by it, then that frame's start is pointed by
    * the next_frame member of the Stream structure. This
    * common situation occurs when mad_frame_decode() fails,
    * sets the stream error code to MAD_ERROR_BUFLEN, and
    * sets the next_frame pointer to a non NULL value. (See
    * also the comment marked {4} bellow.)
    *
    * When this occurs, the remaining unused bytes must be
    * put back at the beginning of the buffer and taken in
    * account before refilling the buffer. This means that
    * the input buffer must be large enough to hold a whole
    * frame at the highest observable bit-rate (currently 448
    * kb/s). XXX=XXX Is 2016 bytes the size of the largest
    * frame? (448000*(1152/32000))/8
    */
   if(Stream.next_frame!=NULL)
   {
    // 这里使用了内存分块使用的方法,写得非常精练
    // 主要用于处理当前数据不够一帧,在读入新数据的时候
    // 要把没有用完的数据放到开头,然后接着读入新的数据
    // 编写过网络程序的朋友应该一目了然了,这就是数据包的
    // “拼接”操作嘛
    Remaining=Stream.bufend-Stream.next_frame;
    memmove(InputBuffer,Stream.next_frame,Remaining);
    ReadStart=InputBuffer+Remaining;
    ReadSize=INPUT_BUFFER_SIZE-Remaining;
   }
   else
    ReadSize=INPUT_BUFFER_SIZE,
     ReadStart=InputBuffer,
     Remaining=0;
   /* Fill-in the buffer. If an error occurs print a message
    * and leave the decoding loop. If the end of stream is
    * reached we also leave the loop but the return status is
    * left untouched.
    */
   // 读取数据,没什么可说的,读取的长度如果小于等于零,就报错
   ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile);
   if(ReadSize<=0)
   {
    if(ferror(InputFp))
    {
     fprintf(stderr,"%s: read error on bit-stream (%s)\n",
       ProgName,strerror(errno));
     Status=1;
    }
    if(feof(InputFp))
     fprintf(stderr,"%s: end of input stream\n",ProgName);
    break;
   }
   /* {3} When decoding the last frame of a file, it must be
    * followed by MAD_BUFFER_GUARD zero bytes if one wants to
    * decode that last frame. When the end of file is
    * detected we append that quantity of bytes at the end of
    * the available data. Note that the buffer can't overflow
    * as the guard size was allocated but not used the the
    * buffer management code. (See also the comment marked
    * {1}.)
    *
    * In a message to the mad-dev mailing list on May 29th,
    * 2001, Rob Leslie explains the guard zone as follows:
    *
    *    "The reason for MAD_BUFFER_GUARD has to do with the
    *    way decoding is performed. In Layer III, Huffman
    *    decoding may inadvertently read a few bytes beyond
    *    the end of the buffer in the case of certain invalid
    *    input. This is not detected until after the fact. To
    *    prevent this from causing problems, and also to
    *    ensure the next frame's main_data_begin pointer is
    *    always accessible, MAD requires MAD_BUFFER_GUARD
    *    (currently 8) bytes to be present in the buffer past
    *    the end of the current frame in order to decode the
    *    frame."
    */
   // 这里是判断文件是否结束,并且加入所谓的MAD_BUFFER_GUARD
   // 用于明确地告诉解码器,已经没有数据了
   if(BstdFileEofP(BstdFile))
   {
    GuardPtr=ReadStart+ReadSize;
    memset(GuardPtr,0,MAD_BUFFER_GUARD);
    ReadSize+=MAD_BUFFER_GUARD;
   }
   /* Pipe the new buffer content to libmad's stream decoder
             * facility.
    */
   mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining);
   Stream.error=0;
  }
  /* Decode the next MPEG frame. The streams is read from the
   * buffer, its constituents are break down and stored the the
   * Frame structure, ready for examination/alteration or PCM
   * synthesis. Decoding options are carried in the Frame
   * structure from the Stream structure.
   *
   * Error handling: mad_frame_decode() returns a non zero value
   * when an error occurs. The error condition can be checked in
   * the error member of the Stream structure. A mad error is
   * recoverable or fatal, the error status is checked with the
   * MAD_RECOVERABLE macro.
   *
   * {4} When a fatal error is encountered all decoding
   * activities shall be stopped, except when a MAD_ERROR_BUFLEN
   * is signaled. This condition means that the
   * mad_frame_decode() function needs more input to complete
   * its work. One shall refill the buffer and repeat the
   * mad_frame_decode() call. Some bytes may be left unused at
   * the end of the buffer if those bytes forms an incomplete
   * frame. Before refilling, the remaining bytes must be moved
   * to the beginning of the buffer and used for input for the
   * next mad_frame_decode() invocation. (See the comments
   * marked {2} earlier for more details.)
   *
   * Recoverable errors are caused by malformed bit-streams, in
   * this case one can call again mad_frame_decode() in order to
   * skip the faulty part and re-sync to the next frame.
   */
  // 这里就是真正的解码部分了,含有对错误的处理方法
  if(mad_frame_decode(&Frame,&Stream))
  {
   if(MAD_RECOVERABLE(Stream.error))
   {
    /* Do not print a message if the error is a loss of
     * synchronization and this loss is due to the end of
     * stream guard bytes. (See the comments marked {3}
     * supra for more informations about guard bytes.)
     */
    if(Stream.error!=MAD_ERROR_LOSTSYNC ||
       Stream.this_frame!=GuardPtr)
    {
     fprintf(stderr,"%s: recoverable frame level error (%s)\n",
       ProgName,MadErrorString(&Stream));
     fflush(stderr);
    }
    continue;
   }
   else
    // 这里的这个错误其实不是错误,只是当前帧的数据不完整,需要再次读入
    // 剩下的部分与新读入的部分“拼接”成为一个完整的帧再解码
    if(Stream.error==MAD_ERROR_BUFLEN)
     continue;
    else
    {
     fprintf(stderr,"%s: unrecoverable frame level error (%s).\n",
       ProgName,MadErrorString(&Stream));
     Status=1;
     break;
    }
  }
  /* The characteristics of the stream's first frame is printed
   * on stderr. The first frame is representative of the entire
   * stream.
   */
  if(FrameCount==0)
   // 这里的PrintFrameInfo用于在解码器接触第一帧的时候,输出一些
   // mp3的采样率、通道个数之类的信息用的
   if(PrintFrameInfo(stderr,&Frame.header))
   {
    Status=1;
    break;
   }
  /* Accounting. The computed frame duration is in the frame
   * header structure. It is expressed as a fixed point number
   * whole data type is mad_timer_t. It is different from the
   * samples fixed point format and unlike it, it can't directly
   * be added or subtracted. The timer module provides several
   * functions to operate on such numbers. Be careful there, as
   * some functions of libmad's timer module receive some of
   * their mad_timer_t arguments by value!
   */
  FrameCount++;
  mad_timer_add(&Timer,Frame.header.duration);
  /* Between the frame decoding and samples synthesis we can
   * perform some operations on the audio data. We do this only
   * if some processing was required. Detailed explanations are
   * given in the ApplyFilter() function.
   */
   // 这里是自定义filter用的,可以用于混音或者特殊音效之类的操作
   // 如果只是单纯的mp3解码的话,这个filter完全可以不用
  if(DoFilter)
   ApplyFilter(&Frame);
  /* Once decoded the frame is synthesized to PCM samples. No errors
   * are reported by mad_synth_frame();
   */
  mad_synth_frame(&Synth,&Frame);
  /* Synthesized samples must be converted from libmad's fixed
   * point number to the consumer format. Here we use unsigned
   * 16 bit big endian integers on two channels. Integer samples
   * are temporarily stored in a buffer that is flushed when
   * full.
   */
  // 这里就是把short类型数据存入output缓冲区
  for(i=0;i  {
   signed short Sample;
   /* Left channel */
   Sample=MadFixedToSshort(Synth.pcm.samples[0][i]);
   // ---------> 这里需要特别注意endian的问题
   *(OutputPtr++)=Sample>>8;  
   *(OutputPtr++)=Sample&0xff;
   /* Right channel. If the decoded stream is monophonic then
    * the right output channel is the same as the left one.
    */
   if(MAD_NCHANNELS(&Frame.header)==2)
    Sample=MadFixedToSshort(Synth.pcm.samples[1][i]);
   // --------->这里需要特别注意endian的问题
   *(OutputPtr++)=Sample>>8;  
   *(OutputPtr++)=Sample&0xff;
   /* Flush the output buffer if it is full. */
   if(OutputPtr==OutputBufferEnd)
   {
    // 这里就是把结果写入pcm文件了,直接的raw数据写入
    if(fwrite(OutputBuffer,1,OUTPUT_BUFFER_SIZE,OutputFp)!=OUTPUT_BUFFER_SIZE)
    {
     fprintf(stderr,"%s: PCM write error (%s).\n",
       ProgName,strerror(errno));
     Status=2;
     break;
    }
    OutputPtr=OutputBuffer;
   }
  }
 }while(1);
 /* The input file was completely read; the memory allocated by our
  * reading module must be reclaimed.
  */
 BstdFileDestroy(BstdFile);
 /* Mad is no longer used, the structures that were initialized must
     * now be cleared.
  */
 mad_synth_finish(&Synth);
 mad_frame_finish(&Frame);
 mad_stream_finish(&Stream);
 /* If the output buffer is not empty and no error occurred during
     * the last write, then flush it.
  */
 if(OutputPtr!=OutputBuffer && Status!=2)
 {
  size_t BufferSize=OutputPtr-OutputBuffer;
  // 这里就是如果还剩下一部分数据,但是mp3文件又结束了
  // 就在这个地方把已经解出来的数据写入到pcm文件中,从这里可以看出
  // 作者做程序态度是很严谨的,hoho,其实这块数据丢掉,一般人听不出来
  if(fwrite(OutputBuffer,1,BufferSize,OutputFp)!=BufferSize)
  {
   fprintf(stderr,"%s: PCM write error (%s).\n",
     ProgName,strerror(errno));
   Status=2;
  }
 }
 /* Accounting report if no error occurred. */
 if(!Status)
 {
  char Buffer[80];
  /* The duration timer is converted to a human readable string
   * with the versatile, but still constrained mad_timer_string()
   * function, in a fashion not unlike strftime(). The main
   * difference is that the timer is broken into several
   * values according some of it's arguments. The units and
   * fracunits arguments specify the intended conversion to be
   * executed.
   *
   * The conversion unit (MAD_UNIT_MINUTES in our example) also
   * specify the order and kind of conversion specifications
   * that can be used in the format string.
   *
   * It is best to examine libmad's timer.c source-code for details
   * of the available units, fraction of units, their meanings,
   * the format arguments, etc.
   */
  // 最后输出一下总结信息,例如共解压了多少帧,用了多长时间之类的
  mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u",
       MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0);
  fprintf(stderr,"%s: %lu frames decoded (%s).\n",
    ProgName,FrameCount,Buffer);
 }
 /* That's the end of the world (in the H. G. Wells way). */
 return(Status);
}
注意需要特别说明的地方是在代码中,我做了标记的地方:
// ---------> 这里需要特别注意endian的问题
*(OutputPtr++)=Sample>>8;  
*(OutputPtr++)=Sample&0xff;
在win mobile开发中,这里需要特别注意,如果直接用这个在unix下面
开发的代码的话,它解码出来的pcm码流就是little endian的,而对于win mobile
的waveout api来说,他们需要big endian的(呵呵,对啦,就是这个问题害得我
用了整整一个上午的时间来调试各个环节,总算找到了!!)
同样的解码文件在little endian和big endian的波形显示效果如下图所示:
显然,只有把output_buffer中的little endian数据改为big endian才能够被win mobile接受。
修改的方法其实很简单(呵呵,找到这个问题还是很复杂的!汗。。。)
// ---------> 这里需要特别注意endian的问题
*(OutputPtr++)=Sample&0xff;
*(OutputPtr++)=Sample>>8; 
把这两句前后位置修改一下即可!
哈哈,简单吧!!libmad学会使用以后,就可以很方便的把mp3文件转换成为pcm码流。
下一篇就是如何在win mobile 5里面播放pcm码流的方法了。
加油,还差一步就可以通过自己的程序在win mobile上面播放mp3文件了!
阅读(5433) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2008-03-11 15:45:20

请教下,我想移植在vce和window上面不可以吗?我在X86标准的vindow ce 5.0 模拟上是可以移植,但是也只限制于WIN32平台下面. 不知道为何?也希望等到你的帮助! dadahacker@126.com 可以把madlld发给我不?