欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux下使用libmad库实现mp3文件的解码、播放

程序员文章站 2022-06-21 15:08:44
...

linux下使用libmad库实现mp3文件的解码、播放

据说这个更新到2004年2月的libmad是一种高品质的MPEG音频解码器,支持24-bit输出,优点多多。

对其的详细介绍请参考主页:http://www.underbit.com/products/mad/

准备工作

x86_64平台的编译可直接运行configure,arm下

libmad ./configure –host=arm-xxx(arm-xxx为交叉编译工具的前缀) 

解码流程

libmad将mp3文件解码后生成pcm数据。libmad库自带一个非常简洁的实例:minimad.c,分析其中的decode函数可看到其解码流程非常简单:

1、配置输入回调函数、输出回调函数、用户数据、筛选回调函数、错误回调函数、消息回调函数,参考初始化函数源代码。

回调函数中,输入(读取原始音频数据)、输出(对解码后的音频数据进行处理)是必需的。

  1. void mad_decoder_init(struct mad_decoder *decoder, void *data,  
  2.                       enum mad_flow(*input_func)(void *,  
  3.                                                  struct mad_stream *),  
  4.                       enum mad_flow(*header_func)(void *,  
  5.                                                   struct mad_header const *),  
  6.                       enum mad_flow(*filter_func)(void *,  
  7.                                                   struct mad_stream const *,  
  8.                                                   struct mad_frame *),  
  9.                       enum mad_flow(*output_func)(void *,  
  10.                                                   struct mad_header const *,  
  11.                                                   struct mad_pcm *),  
  12.                       enum mad_flow(*error_func)(void *,  
  13.                                                  struct mad_stream *,  
  14.                                                  struct mad_frame *),  
  15.                       enum mad_flow(*message_func)(void *,  
  16.                                                    void *, unsigned int *))  
  17. {  
  18.     decoder->mode         = -1;  
  19.   
  20.     decoder->options      = 0;  
  21.   
  22.     decoder->async.pid    = 0;  
  23.     decoder->async.in     = -1;  
  24.     decoder->async.out    = -1;  
  25.   
  26.     decoder->sync         = 0;  
  27.   
  28.     decoder->cb_data      = data;  
  29.   
  30.     decoder->input_func   = input_func;  
  31.     decoder->header_func  = header_func;  
  32.     decoder->filter_func  = filter_func;  
  33.     decoder->output_func  = output_func;  
  34.     decoder->error_func   = error_func;  
  35.     decoder->message_func = message_func;  
  36. }  
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函数打开,那么可以定义一个对此描述的数据结构: 

  1. typedef struct _mp3_file  
  2. {  
  3.     int *fd;//open(“xx.mp3”,O_RDONLY)  
  4.     uint32_t flen;//mp3文件的长度  
  5.     uint32_t fpos;//当前文件指针位置  
  6.   
  7.     uint8_t  buf[BUFSIZE];  
  8.     uint32_t buf_size;  
  9.   
  10. } mp3_file;  
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,用来设置流缓冲区指针。 

错误处理在minimad中被忽略了,可参考madplay中处理方法(player.c:1776),此处贴出来大概的流程

  1. /* 
  2.  * NAME:    decode->error() 
  3.  * DESCRIPTION: handle a decoding error 
  4.  */  
  5. static  
  6. enum mad_flow decode_error(void *data, struct mad_stream *stream,  
  7.                            struct mad_frame *frame)  
  8. {  
  9.     struct player *player = data;  
  10.     signed long tagsize;  
  11.   
  12.     switch (stream->error)  
  13.     {  
  14.         case MAD_ERROR_BADDATAPTR:  
  15.                         /*do something*/  
  16.             return MAD_FLOW_CONTINUE;  
  17.   
  18.         case MAD_ERROR_LOSTSYNC:  
  19.               
  20.             /* todo*/  
  21.   
  22.         default:  
  23.             /*todo*/  
  24.   
  25.     }  
  26.   
  27.     if (stream->error == MAD_ERROR_BADCRC)  
  28.     {  
  29.         return MAD_FLOW_IGNORE;  
  30.     }  
  31.   
  32.     return MAD_FLOW_CONTINUE;  
  33. }  
/*
 * NAME:    decode->error()
 * DESCRIPTION: handle a decoding error
 */
static
enum mad_flow decode_error(void *data, struct mad_stream *stream,
                           struct mad_frame *frame)
{
    struct player *player = data;
    signed long tagsize;

    switch (stream->error)
    {
        case MAD_ERROR_BADDATAPTR:
                        /*do something*/
            return MAD_FLOW_CONTINUE;

        case MAD_ERROR_LOSTSYNC:

            /* todo*/

        default:
            /*todo*/

    }

    if (stream->error == MAD_ERROR_BADCRC)
    {
        return MAD_FLOW_IGNORE;
    }

    return MAD_FLOW_CONTINUE;
}

错误处理的返回值定义如下:

  1. enum mad_flow {  
  2.   MAD_FLOW_CONTINUE = 0x0000,   /* continue normally */  
  3.   MAD_FLOW_STOP     = 0x0010,   /* stop decoding normally */  
  4.   MAD_FLOW_BREAK    = 0x0011,   /* stop decoding and signal an error */  
  5.   MAD_FLOW_IGNORE   = 0x0020    /* ignore the current frame */  
  6. };  
enum mad_flow {
  MAD_FLOW_CONTINUE = 0x0000,   /* continue normally */
  MAD_FLOW_STOP     = 0x0010,   /* stop decoding normally */
  MAD_FLOW_BREAK    = 0x0011,   /* stop decoding and signal an error */
  MAD_FLOW_IGNORE   = 0x0020    /* ignore the current frame */
};


可视情况选择继续或者终止解码。
 

2、调用mad_decoder_run开始解码,支持两种同步、异步两种运行方式

  1. enum mad_decoder_mode {  
  2.   MAD_DECODER_MODE_SYNC  = 0,  
  3.   MAD_DECODER_MODE_ASYNC  
  4. };  
enum mad_decoder_mode {
  MAD_DECODER_MODE_SYNC  = 0,
  MAD_DECODER_MODE_ASYNC
};

3、解码结束后调用mad_decoder_finish释放解码器


 播放

在输出回调函数中,将解码数据直接写入“/dev/dsp”即可实现mp3文件的播放,也可以生成pcm文件,供支持pcm解码的音频芯片使用。

 

 遇到的问题

1、解码错误 (0x0101 lost synchronization)

对于这个错误的解释可参考http://www.mars.org/pipermail/mad-dev/2004-January/000975.html

需要的两个库的下载地址以及编译方法如下:

zlib

这个库的configure脚本没有提供编译器选项。直接运行configure程序后,打开产生的Makefile,将CC=gcc改为你要使用的编译器的名字。

libid3tag

依赖于zlib,需要指定交叉编译工具名称,以及zlib库的头文件路径(-I)、库路径(-L)

运行./configure  –host=arm-xxx CPPFLAGS=-I(zlib头文件路径) LDFLAGS=-L(zlib库路径)

 

2、找不到“/dev/dsp”

对于无法正常播放声音的系统,可采用手动建立dsp设备的方式:

sudo mknod /dev/dsp c 14 3 (其中的设备号可通过Linux源码目录下/Documentation/devices.txt文件中查找/dev/dsp得到)

sudo chmod 666 /dev/dsp (设置普通用户可用)

对于弃用/dev/dsp方式的系统来说,不能采用上述方式,可使用padsp程序,如:

padsp madplay xxx.mp3

据说这个更新到2004年2月的libmad是一种高品质的MPEG音频解码器,支持24-bit输出,优点多多。

对其的详细介绍请参考主页:http://www.underbit.com/products/mad/

准备工作

x86_64平台的编译可直接运行configure,arm下

libmad ./configure –host=arm-xxx(arm-xxx为交叉编译工具的前缀) 

解码流程

libmad将mp3文件解码后生成pcm数据。libmad库自带一个非常简洁的实例:minimad.c,分析其中的decode函数可看到其解码流程非常简单:

1、配置输入回调函数、输出回调函数、用户数据、筛选回调函数、错误回调函数、消息回调函数,参考初始化函数源代码。

回调函数中,输入(读取原始音频数据)、输出(对解码后的音频数据进行处理)是必需的。

  1. void mad_decoder_init(struct mad_decoder *decoder, void *data,  
  2.                       enum mad_flow(*input_func)(void *,  
  3.                                                  struct mad_stream *),  
  4.                       enum mad_flow(*header_func)(void *,  
  5.                                                   struct mad_header const *),  
  6.                       enum mad_flow(*filter_func)(void *,  
  7.                                                   struct mad_stream const *,  
  8.                                                   struct mad_frame *),  
  9.                       enum mad_flow(*output_func)(void *,  
  10.                                                   struct mad_header const *,  
  11.                                                   struct mad_pcm *),  
  12.                       enum mad_flow(*error_func)(void *,  
  13.                                                  struct mad_stream *,  
  14.                                                  struct mad_frame *),  
  15.                       enum mad_flow(*message_func)(void *,  
  16.                                                    void *, unsigned int *))  
  17. {  
  18.     decoder->mode         = -1;  
  19.   
  20.     decoder->options      = 0;  
  21.   
  22.     decoder->async.pid    = 0;  
  23.     decoder->async.in     = -1;  
  24.     decoder->async.out    = -1;  
  25.   
  26.     decoder->sync         = 0;  
  27.   
  28.     decoder->cb_data      = data;  
  29.   
  30.     decoder->input_func   = input_func;  
  31.     decoder->header_func  = header_func;  
  32.     decoder->filter_func  = filter_func;  
  33.     decoder->output_func  = output_func;  
  34.     decoder->error_func   = error_func;  
  35.     decoder->message_func = message_func;  
  36. }  
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函数打开,那么可以定义一个对此描述的数据结构: 

  1. typedef struct _mp3_file  
  2. {  
  3.     int *fd;//open(“xx.mp3”,O_RDONLY)  
  4.     uint32_t flen;//mp3文件的长度  
  5.     uint32_t fpos;//当前文件指针位置  
  6.   
  7.     uint8_t  buf[BUFSIZE];  
  8.     uint32_t buf_size;  
  9.   
  10. } mp3_file;  
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,用来设置流缓冲区指针。 

错误处理在minimad中被忽略了,可参考madplay中处理方法(player.c:1776),此处贴出来大概的流程

  1. /* 
  2.  * NAME:    decode->error() 
  3.  * DESCRIPTION: handle a decoding error 
  4.  */  
  5. static  
  6. enum mad_flow decode_error(void *data, struct mad_stream *stream,  
  7.                            struct mad_frame *frame)  
  8. {  
  9.     struct player *player = data;  
  10.     signed long tagsize;  
  11.   
  12.     switch (stream->error)  
  13.     {  
  14.         case MAD_ERROR_BADDATAPTR:  
  15.                         /*do something*/  
  16.             return MAD_FLOW_CONTINUE;  
  17.   
  18.         case MAD_ERROR_LOSTSYNC:  
  19.               
  20.             /* todo*/  
  21.   
  22.         default:  
  23.             /*todo*/  
  24.   
  25.     }  
  26.   
  27.     if (stream->error == MAD_ERROR_BADCRC)  
  28.     {  
  29.         return MAD_FLOW_IGNORE;  
  30.     }  
  31.   
  32.     return MAD_FLOW_CONTINUE;  
  33. }  
/*
 * NAME:    decode->error()
 * DESCRIPTION: handle a decoding error
 */
static
enum mad_flow decode_error(void *data, struct mad_stream *stream,
                           struct mad_frame *frame)
{
    struct player *player = data;
    signed long tagsize;

    switch (stream->error)
    {
        case MAD_ERROR_BADDATAPTR:
                        /*do something*/
            return MAD_FLOW_CONTINUE;

        case MAD_ERROR_LOSTSYNC:

            /* todo*/

        default:
            /*todo*/

    }

    if (stream->error == MAD_ERROR_BADCRC)
    {
        return MAD_FLOW_IGNORE;
    }

    return MAD_FLOW_CONTINUE;
}

错误处理的返回值定义如下:

  1. enum mad_flow {  
  2.   MAD_FLOW_CONTINUE = 0x0000,   /* continue normally */  
  3.   MAD_FLOW_STOP     = 0x0010,   /* stop decoding normally */  
  4.   MAD_FLOW_BREAK    = 0x0011,   /* stop decoding and signal an error */  
  5.   MAD_FLOW_IGNORE   = 0x0020    /* ignore the current frame */  
  6. };  
enum mad_flow {
  MAD_FLOW_CONTINUE = 0x0000,   /* continue normally */
  MAD_FLOW_STOP     = 0x0010,   /* stop decoding normally */
  MAD_FLOW_BREAK    = 0x0011,   /* stop decoding and signal an error */
  MAD_FLOW_IGNORE   = 0x0020    /* ignore the current frame */
};


可视情况选择继续或者终止解码。
 

2、调用mad_decoder_run开始解码,支持两种同步、异步两种运行方式

  1. enum mad_decoder_mode {  
  2.   MAD_DECODER_MODE_SYNC  = 0,  
  3.   MAD_DECODER_MODE_ASYNC  
  4. };  
enum mad_decoder_mode {
  MAD_DECODER_MODE_SYNC  = 0,
  MAD_DECODER_MODE_ASYNC
};

3、解码结束后调用mad_decoder_finish释放解码器


 播放

在输出回调函数中,将解码数据直接写入“/dev/dsp”即可实现mp3文件的播放,也可以生成pcm文件,供支持pcm解码的音频芯片使用。

 

 遇到的问题

1、解码错误 (0x0101 lost synchronization)

对于这个错误的解释可参考http://www.mars.org/pipermail/mad-dev/2004-January/000975.html

需要的两个库的下载地址以及编译方法如下:

zlib

这个库的configure脚本没有提供编译器选项。直接运行configure程序后,打开产生的Makefile,将CC=gcc改为你要使用的编译器的名字。

libid3tag

依赖于zlib,需要指定交叉编译工具名称,以及zlib库的头文件路径(-I)、库路径(-L)

运行./configure  –host=arm-xxx CPPFLAGS=-I(zlib头文件路径) LDFLAGS=-L(zlib库路径)

 

2、找不到“/dev/dsp”

对于无法正常播放声音的系统,可采用手动建立dsp设备的方式:

sudo mknod /dev/dsp c 14 3 (其中的设备号可通过Linux源码目录下/Documentation/devices.txt文件中查找/dev/dsp得到)

sudo chmod 666 /dev/dsp (设置普通用户可用)

对于弃用/dev/dsp方式的系统来说,不能采用上述方式,可使用padsp程序,如:

padsp madplay xxx.mp3

相关标签: linux mp3

上一篇: java 合并mp3

下一篇: DBUtils类