您的位置:首页 > 运维架构 > Linux

linux mp3播放及分析

2016-03-02 16:59 731 查看
 MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和
API 等。

     网上有很多关于libmad的使用实例,在他们的基础上,我稍加总结、整理和衍生,文末给出相关参考链接,表示感谢!

 

     一、libmad库源码

 

     可以去相关网站下载,给出链接:

     http://download.chinaunix.net/download.php?id=11891&ResourceID=5910

可以根据不同的平台自行编译或者移植,略述。

 

     二、相关数据结构及函数接口简介

 

     1、struct mad_decode

struct mad_decoder {

  enum mad_decoder_mode mode;

  int options;

  struct {

    long pid;

    int in;

    int out;

  } async;

  struct {

    struct mad_stream stream;

    struct mad_frame frame;

    struct mad_synth synth;

  } *sync;

  void *cb_data;

  enum mad_flow (*input_func)(void *, struct
mad_stream *);

  enum mad_flow (*header_func)(void *, struct
mad_header const *);

  enum mad_flow (*filter_func)(void *,

             struct mad_stream const *, struct mad_frame *);

  enum mad_flow (*output_func)(void *,

             struct mad_header const *, struct mad_pcm *);

  enum mad_flow (*error_func)(void *, struct
mad_stream *, struct mad_frame *);

  enum mad_flow (*message_func)(void *, void *, unsigned int *);

};

     2、struct mad_stream

struct mad_stream {

  unsigned char const *buffer;        /* input
bitstream buffer */


  unsigned char const *bufend;        /* end of
buffer */


  unsigned long skiplen;              /* bytes to skip before next frame */

  int sync;                           /* stream sync found */

  unsigned long freerate;             /* free bitrate (fixed) */

  unsigned char const *this_frame;    /* start
of current frame */


  unsigned char const *next_frame;    /* start
of next frame */


  struct mad_bitptr ptr;              /* current processing bit pointer */

  struct mad_bitptr anc_ptr;          /* ancillary bits pointer */

  unsigned int anc_bitlen;            /* number of ancillary
bits */

  unsigned char (*main_data)[MAD_BUFFER_MDLEN];

                                      /* Layer III main_data() */

  unsigned int md_len;                /* bytes in main_data */

  int options;                        /* decoding options (see
below) */

  enum mad_error error;               /* error code (see
above) */

};

     三、MP3解码流程简介
 
     MP3解码有同步方式和异步方式两种,libmad是以桢为单位对MP3进行解码的,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。
     1、首先创建一个解码器 struct mad_decoder decoder,紧接着调用函数  mad_decoder_init(...)函数,给出这个函数的原型及定义

/*

 * NAME:    decoder->init()

 * DESCRIPTION:    initialize a decoder object with callback routines

 */

void mad_decoder_init(struct mad_decoder *decoder, void *data,

         enum mad_flow (*input_func)(void *,

                         struct mad_stream *),

         enum mad_flow (*header_func)(void *,

                         struct mad_header const *),

         enum mad_flow (*filter_func)(void *,

                         struct mad_stream const *,

                         struct mad_frame *),

         enum mad_flow (*output_func)(void *,

                         struct mad_header const *,

                         struct mad_pcm *),

         enum mad_flow (*error_func)(void *,

                         struct mad_stream *,

                         struct mad_frame *),

         enum mad_flow (*message_func)(void *,

                         void *, unsigned int *))

{

  decoder->mode = -1;

  decoder->options = 0;

  decoder->async.pid = 0;

  decoder->async.in = -1;

  decoder->async.out = -1;

  decoder->sync = 0;

  decoder->cb_data = data;

  decoder->input_func = input_func;

  decoder->header_func = header_func;

  decoder->filter_func = filter_func;

  decoder->output_func = output_func;

  decoder->error_func = error_func;

  decoder->message_func = message_func;

}

    
其中的data参数是用户需要传给回调函数的自定义数据结构。比如,解码一个文件,并且采用系统函数open函数打开,那么可以定义一个对此描述的数据结构: 

[cpp]
view plain
copy

typedef struct _mp3_file  
{  
    int *fd;//open("xx.mp3",O_RDONLY)  
    uint32_t flen;//mp3文件的长度  
    uint32_t fpos;//当前文件指针位置  
  
    uint8_t  buf[BUFSIZE];  
    uint32_t buf_size;  
  
} mp3_file;  

数据成员buf、buf_size传递给mad_stream_buffer,用来设置流缓冲区指针。

     用户编程可以用如下方式调用,可以看到从第三个参数开始,其实都是一些列的函数指针,这里初始化的目的其实是给创建的decoder注册下面即将要自己实现的这些函数。Libmad库会在解码过程中回调这些函数:

mad_decoder_init(&decoder, &buffer,

         input, 0 /* header */, 0 /* filter */, output,

         error, 0 /* message */);

     第一个参数,就是定义的解码器decoder;
     第二个参数,是一个void型的函数指针,这里也就是给你的用户空间定义私有的数据结构体用的,下面会给出具体的例子来说明其用法;
     第三个参数,input_func函数,这个是用来读取你的mp3资源的函数;
     第四个参数,header_func函数,这个顾名思义是处理mp3头部信息的函数,可以根据需要取舍;
     第五个参数,filter_func函数,也没有深入理解过,可以不必实现;

     第六个参数,output_func函数,这个是用来将解码之后的数据写入输出缓冲区或者音频设备节点的;
     第七个参数,error_func函数,是用来打印返回的解码出错信息的;
     第八个参数,message_func可以不必实现。

     2、调用mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC)函数启动解码,查看Libmad库源码可知,这个函数里面会注册一个函数指针

/*

 * NAME:    decoder->run()

 * DESCRIPTION:    run the decoder thread either synchronously or asynchronously

 */

int mad_decoder_run(struct mad_decoder *decoder, enum mad_decoder_mode
mode)

{

  int result;

  int (*run)(struct
mad_decoder *) = 0;

  switch (decoder->mode = mode) {

  case MAD_DECODER_MODE_SYNC:


    run = run_sync; 

    break;


  case MAD_DECODER_MODE_ASYNC:

# if defined(USE_ASYNC)

    run = run_async;

# endif

    break;

  }

  if (run == 0)

    return -1;

  decoder->sync = malloc(sizeof(*decoder->sync));

  if (decoder->sync == 0)

    return -1;

  result = run(decoder);


  free(decoder->sync);

  decoder->sync = 0;

  return result;

}

     而在这个run_sync(struct mad_decoder *decoder)函数中则有一个大的while循环来依次调用
decoder->input_func(decoder->cb_data, stream)获取mp3源文件,然后交由相关库函数解码。
而后会有decoder->output_func(decoder->cb_data,  &frame->header, &synth->pcm)函数来输出解码后的数据。
在输出回调函数中,将解码数据直接写入“/dev/dsp”即可实现mp3文件的播放,也可以生成pcm文件,供支持pcm解码的音频芯片使用。

     3、最后调用mad_decoder_finish(&decoder)结束解码,释放decoder资源。

     4、在input_func函数中,会调用一个很重要的函数

mad_stream_buffer(stream, buffer->start, buffer->length) ,第一个参数指向一个mad_stream变量,mad_stream结构定义在stream.h头文件里,用于记录文件的地址和当前处理的位置。第二、三个参数分别是mp3文件在内存中映像的起始地址和文件长度。mad_stream_buffer()函数将mp3文件与mad_stream结构进行关联。

 

     四、MP3解码编程实例

代码一:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <sys/soundcard.h>

#include "mad.h"

#define BUFSIZE 8192

/*

 * This is a private message structure. A generic pointer to this
structure

 * is passed to each of the callback functions. Put
here any data you need

 * to access from within the callbacks.

 */

struct buffer {

    FILE *fp; /*file pointer*/

    unsigned int flen; /*file length*/

    unsigned int fpos; /*current position*/

    unsigned char fbuf[BUFSIZE]; /*buffer*/

    unsigned int fbsize; /*indeed size of buffer*/

};

typedef struct buffer mp3_file;

int soundfd; /*soundcard file*/

unsigned int prerate = 0; /*the
pre simple rate*/

int writedsp(int c)

{

    return write(soundfd, (char *)&c, 1);

}

void set_dsp()

{

#if 0

    int format = AFMT_S16_LE;

    int channels = 2;

    int rate = 44100;

    soundfd = open("/dev/dsp", O_WRONLY);

    ioctl(soundfd, SNDCTL_DSP_SPEED,&rate);

    ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);

    ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);

#else

    if((soundfd = open("test.bin" , O_WRONLY | O_CREAT)) < 0)

    {

        fprintf(stderr , "can't open sound device!\n");

        exit(-1);

    }

#endif

}

/*

 * This is perhaps the simplest example use of the MAD high-level API.

 * Standard input is mapped into memory via mmap(), then the
high-level API

 * is invoked with three callbacks: input, output, and error. The
output

 * callback converts MAD's high-resolution PCM samples to 16
bits, then

 * writes them to standard output in little-endian, stereo-interleaved

 * format.

 */

static int decode(mp3_file *mp3fp);

int main(int argc, char *argv[])

{

    long flen, fsta, fend;

    int dlen;

    mp3_file *mp3fp;

    if (argc != 2)

        return 1;

    mp3fp = (mp3_file *)malloc(sizeof(mp3_file));

    if((mp3fp->fp = fopen(argv[1], "r")) == NULL)

    {

        printf("can't open source file.\n");

        return 2;

    }

    fsta = ftell(mp3fp->fp);

    fseek(mp3fp->fp, 0, SEEK_END);

    fend = ftell(mp3fp->fp);

    flen = fend - fsta;

    if(flen > 0)

        fseek(mp3fp->fp, 0, SEEK_SET);

    fread(mp3fp->fbuf, 1, BUFSIZE, mp3fp->fp);

    mp3fp->fbsize = BUFSIZE;

    mp3fp->fpos = BUFSIZE;

    mp3fp->flen = flen;

    set_dsp();

    decode(mp3fp);

    close(soundfd);

    fclose(mp3fp->fp);

    return 0;

}

static enum mad_flow input(void *data, struct mad_stream *stream)

{

    mp3_file *mp3fp;

    int ret_code;

    int unproc_data_size; /*the unprocessed data's
size*/

    int copy_size;

    mp3fp = (mp3_file *)data;

    if(mp3fp->fpos < mp3fp->flen) {

        unproc_data_size = stream->bufend - stream->next_frame;


        //printf("%d,
%d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);


        memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);


        copy_size = BUFSIZE - unproc_data_size;


        if(mp3fp->fpos + copy_size > mp3fp->flen) {


            copy_size = mp3fp->flen - mp3fp->fpos;


        }


        fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);


        mp3fp->fbsize = unproc_data_size + copy_size;


        mp3fp->fpos += copy_size;


        /*Hand off the buffer to the mp3 input stream*/


        mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);


        ret_code = MAD_FLOW_CONTINUE;

    } else {

        ret_code = MAD_FLOW_STOP;

    }

    return ret_code;

}

/*

 * The following utility routine performs simple rounding, clipping, and

 * scaling of MAD's high-resolution samples down to 16 bits. It
does not

 * perform any dithering or noise shaping, which would be recommended to

 * obtain any exceptional audio quality. It is therefore not recommended to

 * use this routine if high-quality output is desired.

 */

static inline signed int scale(mad_fixed_t sample)

{

    /* round */

    sample += (1L << (MAD_F_FRACBITS - 16));

    /* clip */

    if (sample >= MAD_F_ONE)

        sample = MAD_F_ONE - 1;

    else if (sample < -MAD_F_ONE)

        sample = -MAD_F_ONE;

    /* quantize */

    return sample >> (MAD_F_FRACBITS + 1 - 16);

}

/*

 * This is the output callback function. It is called
after each frame of

 * MPEG audio data has been completely decoded. The purpose of this callback

 * is to output (or play) the
decoded PCM audio.

 */

//输出函数做相应的修改,目的是解决播放音乐时声音卡的问题。

static enum mad_flow output(void *data, struct mad_header const *header,

        struct mad_pcm *pcm)

{

    unsigned int nchannels, nsamples;

    mad_fixed_t const *left_ch, *right_ch;

    // pcm->samplerate contains the sampling frequency

    nchannels = pcm->channels;

    nsamples = pcm->length;

    left_ch = pcm->samples[0];

    right_ch = pcm->samples[1];

    short buf[nsamples *2];

    int i = 0;

    //printf(">>%d\n", nsamples);

    while (nsamples--) {

        signed int sample;

        // output sample(s) in 16-bit
signed little-endian PCM

        sample = scale(*left_ch++);

        buf[i++] = sample & 0xFFFF;

        if (nchannels == 2) {

            sample = scale(*right_ch++);

            buf[i++] = sample & 0xFFFF;

        }

    }

    //fprintf(stderr, ".");

    write(soundfd, &buf[0], i * 2);

    return MAD_FLOW_CONTINUE;

}

/*

 * This is the error callback function. It is called
whenever a decoding

 * error occurs. The error is indicated
by stream->error; the list of

 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)

 * header file.

 */

static enum mad_flow error(void *data,

        struct mad_stream *stream,

        struct mad_frame *frame)

{

    mp3_file *mp3fp = data;

    fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",

            stream->error, mad_stream_errorstr(stream),

            stream->this_frame - mp3fp->fbuf);

    /* return MAD_FLOW_BREAK here to stop decoding (and propagate
an error) */

    return MAD_FLOW_CONTINUE;

}

/*

 * This is the function called by main() above to perform
all the decoding.

 * It instantiates a decoder object and configures it with the input,

 * output, and error callback functions above. A
single call to

 * mad_decoder_run() continues until a callback function returns

 * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop
decoding and

 * signal an error).

 */

static int decode(mp3_file *mp3fp)

{

    struct mad_decoder decoder;

    int result;

    /* configure input, output, and error functions */

    mad_decoder_init(&decoder, mp3fp,

            input, 0 /* header */, 0 /* filter */, output,

            error, 0 /* message */);

    /* start decoding */

    result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

    /* release the decoder */

    mad_decoder_finish(&decoder);

    return result;

}

代码二:

(第二种解码)
#include "stdio.h"

#include "stdlib.h"

#include "string.h"

#include "unistd.h"

#include "fcntl.h"

#include "mad.h"

#include "linux/soundcard.h"

typedef unsigned int DWORD;

typedef unsigned char Byte;

typedef char *PChar;

typedef PChar *PPChar;

typedef void *Pointer;

//MAD解码必要的3个结构声明

struct mad_stream stream;

struct mad_frame frame;

struct mad_synth synth;

int sampleofs,samplecnt;

#define MAD_BLOCK_SIZE 8192

#define MAD_INBUF_SIZE MAD_BLOCK_SIZE + MAD_BUFFER_GUARD

Byte Inbuf[MAD_INBUF_SIZE];

//初始化Libmad

void Mad_Decode_Init()

{

 mad_stream_init(&stream);

 mad_frame_init(&frame);

 mad_synth_init(&synth);

 samplecnt = 0;

 sampleofs = 0;

}

//释放Libmad

void Mad_Decode_Free()

{

 mad_synth_finish(&synth);

 mad_frame_finish(&frame);

 mad_stream_finish(&stream);

}

//MP3解码和输出PCM

int Mad_Decode_Read(int hFile, Pointer buf, int len)

{

 int i, num = len, ofs = 0;

 Byte *in_buf;

 int remaining, rlen;

 DWORD nchannels, nsamples;

 

 while(num > 0)

 {

  if(samplecnt == 0)

  {

   if((stream.buffer== NULL)||(stream.error = MAD_ERROR_BUFLEN))

   {

    if(stream.next_frame!= NULL)

    {

     remaining= stream.bufend - stream.next_frame;

     memcpy(Inbuf,stream.next_frame, remaining);

     in_buf= Inbuf + remaining;

     rlen= MAD_BLOCK_SIZE - remaining;

    }

    else

    { 

     rlen= MAD_BLOCK_SIZE;

     in_buf= Inbuf;

     remaining= 0;

    }

 

    rlen= read(hFile, in_buf, rlen);//读MPEG流

    if(rlen<= 0)

    {

     returnofs;

    }

    //向MAD写入MP3流

    mad_stream_buffer(&stream,Inbuf, rlen + remaining);

    stream.error= MAD_ERROR_NONE;

   }

   

   //解码

   if(mad_frame_decode(&frame,&stream) != 0)

   {

    if(MAD_RECOVERABLE(stream.error)||(stream.error== MAD_ERROR_BUFLEN)) 

     continue;

    returnofs;

   }  

   

   mad_synth_frame(&synth,&frame);

   

   nchannels =synth.pcm.channels;

   nsamples = synth.pcm.length;

   if(nchannels== 2)

   {

    for(i= 0; i < nsamples; i++)

    {

     if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;

     if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;

     synth.pcm.samples[0][i]= synth.pcm.samples[0][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);

     

     if(synth.pcm.samples[1][i]>= MAD_F_ONE)synth.pcm.samples[1][i]
= MAD_F_ONE - 1;

     if(synth.pcm.samples[1][i]< -MAD_F_ONE)synth.pcm.samples[1][i]
= -MAD_F_ONE;

     synth.pcm.samples[1][i]= synth.pcm.samples[1][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);     

    }

   }

   else

   {

    for(i= 0; i < nsamples; i++)

    {

     if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;

     if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;

     
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: