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

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

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个文件夹。


这步一般不会有啥问题,但是,默认成功的是静态库啊, 虽然配置上是说默认配置生成动态库,但是确实没有。


实际是有的,指定--shared即可。


或者仔细查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\n\t"  \
         "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\n\t" \
"       sbc %1, %1, %1\n\t" \
        "sub %1, %1, %2" \
        : "+&r" (lo), "=&r" (hi) \
        : "r" (hi) \
        : "cc")
#else /* ! __thumb__ */
# define MAD_F_MLN(hi, lo) \
        asm ("rsbs %0, %2, #0\n\t" \
        "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 <demo1.mp3 >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 <stdio.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/mman.h>
# 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\n",
    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;
}
相关文章
|
10天前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
51 15
|
24天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
94 13
|
5月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
196 3
|
5月前
|
传感器 人工智能 网络协议
:嵌入式 Linux 及其用途
【8月更文挑战第24天】
229 0
|
6月前
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
**嵌入式Linux学习误区摘要** 1. **过度聚焦桌面Linux** - 许多学习者误将大量时间用于精通桌面Linux系统(如RedHat、Fedora、Ubuntu),认为这是嵌入式Linux开发的基石。 - 实际上,桌面Linux仅作为开发工具和环境,目标不应是成为Linux服务器专家,而应专注于嵌入式开发工具和流程。 2. **盲目阅读Linux内核源码** - 初学者在不了解Linux基本知识时试图直接研读内核源码,这往往导致困惑和挫败感。 - 在具备一定嵌入式Linux开发经验后再有针对性地阅读源码,才能有效提升技能。
|
2月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
170 8
|
2月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
707 6
|
2月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
119 3
|
2月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
97 2
|
1月前
|
Linux Shell
Linux 10 个“who”命令示例
Linux 10 个“who”命令示例
62 14
Linux 10 个“who”命令示例