-
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 *);
- };
-
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解码流程简介
-
/*
-
* 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;
- }
-
mad_decoder_init(&decoder, &buffer,
-
input, 0 /* header */, 0 /* filter */, output,
- error, 0 /* message */);
第六个参数,output_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;
- }
-
#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;
- }
说明:1、实例原本是基于音频OSS框架的,当然,在嵌入式领域,ALSA也是兼容OSS接口的;
-
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;
-
- }
我们设置的输入buff缓冲区的大小是8192字节,但是对于mp3文件来讲,不一定这8192个字节就刚好是若干个完整的帧,有可能会有若干字节是输入下一个帧的,所有要根据struct mad_stream中的两个指针,标示了缓冲区中的完整帧的起始地址:
-
unsigned char const *this_frame; /* start
of current frame */
- unsigned char const *next_frame; /* start of next frame */
- unproc_data_size = stream->bufend - stream->next_frame;
得到剩余的下一个帧的数据,并且需要将其从buff数组的尾部拷贝到头部,再从mp3文件中读取一部分字节拼凑成下一个8192字节,提交给库去解码,如此周而复始。
4、此代码解码出来的pcm可以加上44字节的wav头文件,则可以用播放器正常播放。
五、如何从网络socket获取相应数据,边解码边播放
由于我的项目是要实现一个远程播放器的功能,即手机端的mp3源文件通过wifi传输到开发板上解码播放,所以,对于输入缓冲区的控制就不像操作文件那个,可以通过file结构体精确控制好读取的数据位置了,为此,做了些许修改。
可以开两个线程,一个线程用于接收socket数据,一个用于解码播放。主要是缓冲区的控制,可以如此实现:将接收buff[]大小设置为8192*10字节,然后,解码input函数里面的buff[]的大小设置为8192*11字节,也就是说,多余了8192用来缓冲多余的下一帧字节的数据(因为mp3文件的帧不会超过8192字节),那么,区别于上面的思路,我们可以固定的让socket的buff[]接收8192*10字节的数据,如果解码的buff[]里面初次解码后有剩余的数据,仍然将其复制到解码buff[]的头部,只是这时候还是将socket的buff[]的8192*10字节的数据加到解码buff[]的刚刚拷贝的数据后面,所以,这里调用mad_stream_buffer(stream, buf, bsize)中的bsize就是8192*10+剩余的帧数据大小了。
相关参考:
1、作者:cqulpj 网址: http://cqulpj.blogbus.com/logs/68406670.html