让终端支持播放mp3,移植mp3解码库libmad和madplay到嵌入式linux

2019-07-12 18:49发布

libmad简介 MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24
-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。
移植涉及到的库:
zlib-1.2.3.tar.gz
libid3tag-0.15.1b.tar.gz
libmad-0.15.1b.tar.gz
---------------------------------------------------------------------------------------------------------------
madplay介绍:
 madplay基于libmad的基础上做了一个播放器,该播放器除了目前不支持网络播放以为,其余功能都支持。如快进播放,seek播放,暂停,恢复等
最后移植了一个基于libmad的应用madplay,可以直接用它来播放mp3.
madplay-0.15.2b.tar.gz

介绍完了,当然移植不是一番风顺的,折腾了一天。中间遇到不少问题,还好,最后都一一解决了。 网上提供的代码。交叉编译,由于环境不一样,会出现各种问题。 这里记录一下过程。 首先是交叉编译zlib-1.2.3.tar.gz 用交叉编译工具编译zlib,并且把库生成到交叉编译环境的库目录下
./configure --prefix=/home/ban/madplay/source   
修改Makefile.
CC=arm-linux-gnueabihf-gcc
AR=arm-linux-gnueabihf-ar rc
RANLIB=arm-linux-gnueabihf-ranlib
make 
make install
安装完成后,在/home/ban/madplay/source/ 中将生产lib跟include2个文件夹。
这步一般不会有啥问题,但是,默认成功的是静态库啊, 虽然配置上是说默认配置生成动态库,但是确实没有。 这个没办法,仔细查makefile,把相关的编译语句找出来,我手动调用gcc - shared -fPIC - $(OBJS)生成了.so 这里还需要注意的是,指定好自己的--prifix,因为后续的编译,好多是要依赖这个的。 接下来编译libid3tag-0.15.1b.tar.gz 这时候要注意了,如果上一步编译不过,或者没有指定--prifix, 这里就麻烦了。 由于我需要的是动态库,发现configer后,竟没带-FPIC参数,还要去改makefile才行。
./configure --host=arm-linux-gnueabihf  --disable-debugging --prefix=/home/ban/madplay/source CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make 
make install

编译libmad
./configure --host=arm-linux-gnueabihf  --disable-debugging --prefix=/home/ban/madplay/source CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make
make install 

出现错误:
cc1: error: unrecognized command line option “-fforce-mem”
原因是高版本的gcc,已经将-fforce-mem去除了,解决方法:
 sed -i '/-fforce-mem/d' configure
再执行:
./configure --host=arm-linux-gnueabihf --prefix=/usr/local/libmad_arm --enable-shared --enable-static --enable-fpm=arm --


with-gnu-ld=arm-linux-gnueabihf-ld --build=arm
出现错误:
/tmp/ccf2FxyW.s:1299: Error: selected processor does not support Thumb mode `rsc r0,r0,#0'
/tmp/ccf2FxyW.s:1435: Error: selected processor does not support Thumb mode `rsc r8,r8,#0'
/tmp/ccf2FxyW.s:1857: Error: selected processor does not support Thumb mode `rsc r0,r0,#0'
/tmp/ccf2FxyW.s:1996: Error: selected processor does not support Thumb mode `rsc r0,r0,#0
百度一下发现这是libmad的一个bug.
解决方法是:
vim  fixed.h

#  define MAD_F_MLN(hi, lo) 
    asm ("rsbs  %0, %2, #0 " 
         "rsc   %1, %3, #0" 
         : "=r" (lo), "=r" (hi) 
         : "0" (lo), "1" (hi) 
         : "cc")
改为
#ifdef __thumb__
/* In Thumb-2, the RSB-immediate instruction is only allowed with a zero
operand. If needed this code can also support Thumb-1 
(simply append "s" to the end of the second two instructions). */
# define MAD_F_MLN(hi, lo)
asm ("rsbs %0, %0, #0 "
"       sbc %1, %1, %1 "
        "sub %1, %1, %2"
        : "+&r" (lo), "=&r" (hi)
        : "r" (hi)
        : "cc")
#else /* ! __thumb__ */
# define MAD_F_MLN(hi, lo)
        asm ("rsbs %0, %2, #0 "
        "rsc %1, %3, #0"
         : "=r" (lo), "=r" (hi)
          : "=&r" (lo), "=r" (hi)
          : "0" (lo), "1" (hi)
          : "cc")
#endif /* __thumb__ */
再make,编译通过了!
编译madplay
./configure --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc --disable-debugging --with-alsa CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make 
make install
完成以后把生成的可执行文件madplay下载到开发板中
执行./madplay filename.mp3 这个需要注意的是,如果不指定--with-alsa,即便编译成功了,放到板子上也是跑不起的,提示找不到dev/dsp,这个让我折腾了好久,竟发现,配置上没启用alsa啊, 但板子上带的是alsa架构的linux音频驱动。
总体上操作是就这么几步,但是,你会发现,如果照这个步骤来,仍是有错。 具体细节。,根据编译提示的错误,基本都能定为到。比如,找不到上几步编译出的库,就去改makefile吧,添加进去路径 或者仍拷贝到 --prifix指定的目录中。

最后再说一点儿,编译网上这种开源库,最好设置下交叉工具链的环境变量为全局的,且用root权限。否则,坑真的好多。
附截图:


如果不用这个现成的播放器madplay,只测试下libmad是否成功, 可以编译测试下 libmad提供的一个简单demo,这个demo 不是播放mp3的,而是把mp3解码成 pcm文件 。 测试如下: ./testmad.out out1.pcm      显示出了信息,且在当前路径下产生了out1.pcm文件。 9522 frames decoded (0:04:08.7), +1.7 dB peak amplitude, 4202 clipped samples
/* * libmad - MPEG audio decoder library * Copyright (C) 2000-2004 Underbit Technologies, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: minimad.c,v 1.4 2004/01/23 09:41:32 rob Exp $ */ # include # include # include # include # include "mad.h" /* * 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(unsigned char const *, unsigned long); int main(int argc, char *argv[]) { struct stat stat; void *fdm; if (argc != 1) return 1; if (fstat(STDIN_FILENO, &stat) == -1 || stat.st_size == 0) return 2; fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, STDIN_FILENO, 0); if (fdm == MAP_FAILED) return 3; decode(fdm, stat.st_size); if (munmap(fdm, stat.st_size) == -1) return 4; return 0; } /* * 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 { unsigned char const *start; unsigned long length; }; /* * This is the input callback. The purpose of this callback is to (re)fill * the stream buffer which is to be decoded. In this example, an entire file * has been mapped into memory, so we just call mad_stream_buffer() with the * address and length of the mapping. When this callback is called a second * time, we are finished decoding. */ static enum mad_flow input(void *data, struct mad_stream *stream) { struct buffer *buffer = data; if (!buffer->length) return MAD_FLOW_STOP; mad_stream_buffer(stream, buffer->start, buffer->length); buffer->length = 0; return MAD_FLOW_CONTINUE; } /* * 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]; while (nsamples--) { signed int sample; /* output sample(s) in 16-bit signed little-endian PCM */ sample = scale(*left_ch++); putchar((sample >> 0) & 0xff); putchar((sample >> 8) & 0xff); if (nchannels == 2) { sample = scale(*right_ch++); putchar((sample >> 0) & 0xff); putchar((sample >> 8) & 0xff); } } 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) { struct buffer *buffer = data; fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u ", stream->error, mad_stream_errorstr(stream), stream->this_frame - buffer->start); /* 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(unsigned char const *start, unsigned long length) { struct buffer buffer; struct mad_decoder decoder; int result; /* initialize our private message structure */ buffer.start = start; buffer.length = length; /* configure input, output, and error functions */ mad_decoder_init(&decoder, &buffer, 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; }