讓終端支持播放mp3,移植mp3解碼庫libmad和madplay到嵌入式linux

libmad簡介

官網:https://www.underbit.com/products/mad/

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指定的目錄中。

如果編譯遇到以下錯誤:

只需更新alsa api,libmad裏的api太舊了,新的alsa-lib不支持了

解決方案參考:https://gitlab.savoirfairelinux.com/netdsa/buildroot/commit/97b392b98468859ab2366995b6bee4140b38e388

最後再說一點兒,編譯網上這種開源庫,最好設置下交叉工具鏈的環境變量爲全局的,且用root權限。否則,坑真的好多。

 

附截圖:

如果不用這個現成的播放器madplay,只測試下libmad是否成功,

可以編譯測試下 libmad提供的一個簡單demo,這個demo 不是播放mp3的,而是把mp3解碼成 pcm文件 。

測試如下:

./testmad.out <demo1.mp3 >out1.pcm     

顯示出了信息,且在當前路徑下產生了out1.pcm文件。

mad功能:

madpay功能很強求,可以實現多個節目循環播放,快進拖動播放,暫停,恢復,支持wav等格式輸出等。
目前madplay還不支持網絡播放.不過mp123支持網絡播放。
madplay使用方法: 傳入打印既可以使用短傳入(-n ),也可以使用長傳入(--abc),二者等效,下面只講短傳入
-v  獲取播放時間
-q  不存在任何打印,但現實警告
-Q  不存在任何打印
--downsample  只採用一半數據
-i  忽略CRC校驗錯誤
-o PATH/xx.wav 可以用來轉碼,將mp3轉爲wav
  也可以是其他格式,見說明;.raw  表示是元素pcm,.hex等
-a 開啓衰減音量 ,增加音量,衰減系統爲-175~+18    (當然通過鍵盤+ —也可以調整音量)
-A 同-a
-1 -2 -m -S 分別指左聲道,右聲道,雙聲道,立體聲
-s  用於seek播放 如:0:1:20:11 ,seek到1小時,2分鐘,11秒時開始播放
-t  用於播放時間現在  0:1:20:11 ,播放到1小時,2分鐘,11秒時就停止
-z  用於隨機播放列表
-r, --repeat[=MAX]   循環播放無限次或Max次 
--tty-control            enable keyboard controls  默認是使能熱鍵
--no-tty-control         disable keyboard controls
熱鍵使用
下一首歌曲; f,或ctrl+n 或者>
上一首歌曲  b ,或ctrl+p 或者<
退出:q, Q,獲取ctrl+c
獲取播放狀態信息: i  ?
調節音量: - + _ =
暫停恢復:p
停止:s
Usage: ./madplay [OPTIONS] FILE [...]
Decode and play MPEG audio FILE(s).


Verbosity:
  -v, --verbose                show status while decoding
  -q, --quiet                  be quiet but show warnings
  -Q, --very-quiet             be quiet and do not show warnings
      --display-time=MODE      use default verbose time display MODE
                                 (remaining, current, overall)


Decoding:
      --downsample             reduce sample rate 2:1
  -i, --ignore-crc             ignore CRC errors
      --ancillary-output=PATH  write ancillary data to PATH


Audio output:
  -o, --output=[TYPE:]PATH     write output to PATH with format TYPE (below)
  -b, --bit-depth=DEPTH        request DEPTH bits per sample
  -R, --sample-rate=HERTZ      request HERTZ samples per second
  -d, --no-dither              do not dither output PCM samples
      --fade-in[=DURATION]     fade-in songs over DURATION (default 0:05)
  -a, --attenuate=DECIBELS     attenuate signal by DECIBELS (-)
  -a, --amplify=DECIBELS       amplify signal by DECIBELS (+)
  -A, --adjust-volume=DECIBELS override per-file volume adjustments
  -G, --replay-gain[=PROFILE]  enable Replay Gain volume adjustments using
                                 PROFILE (radio, audiophile)


Channel selection:
  -1, --left                   output first (left) channel only
  -2, --right                  output second (right) channel only
  -m, --mono                   mix left and right channels for monaural output
  -S, --stereo                 force stereo output


Playback:
  -s, --start=TIME             skip to begin at TIME (HH:MM:SS.DDD)
  -t, --time=DURATION          play only for DURATION (HH:MM:SS.DDD)
  -z, --shuffle                randomize file list
  -r, --repeat[=MAX]           play files MAX times, or indefinitely
      --tty-control            enable keyboard controls
      --no-tty-control         disable keyboard controls


Miscellaneous:
  -T, --show-tags-only         show ID3/encoder tags only (do not decode)
  -V, --version                display version number and exit
      --license                show copyright/license message and exit
  -h, --help                   display this help and exit


Supported output formats:
  cdda    CD audio, 16-bit big-endian 44100 Hz stereo PCM (*.cdr, *.cda)
  aiff    Audio IFF, [16-bit] PCM (*.aif, *.aiff)
  wave    Microsoft RIFF/WAVE, [16-bit] PCM (*.wav)
  snd     Sun/NeXT audio, 8-bit ISDN mu-law (*.au, *.snd)
  raw     binary [16-bit] host-endian linear PCM
  hex     ASCII hexadecimal [24-bit] linear PCM
  null    no output (decode only)
————————————————
 

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;
}

 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章