免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1719 | 回复: 0

一个完整的MP3播放的实现 [复制链接]

论坛徽章:
0
发表于 2010-01-29 11:15 |显示全部楼层
虽然想要加的网络文件功能还没有加,但是先记录,以免以后遗忘。
首先是主程序:

/*
*miniplayer--a
simple mp3 player based on libmad and alsa.
*author: zheng.wenwei
*date:
2009.4.17
*version: 0.8(network file supported)
*/
/*
* refix
* 1.sigwait will
action unlike the sigsuspend, when it return, it won't
* trigger the
signal handle, it just discards the signal. howerver, since t
* the
thread directed signal syscalls don't include mounting the signal
*
handle(p*kill, sigmask, sigwait), and the mount action in fack is about

* the process, so it maybe understant here.
*
* 2.before
creating the thread which will send a USR1 to the play thread,
* you
should make sure the USR1 is masked in it. this is the sequence
*
should be obeyed.
*
* 3.assume the MP3 is CBR, 128kps, 2
channels(in coming days, we shouldn't
* use this assumption any
longer.
*
* 4.BUG1:(refixed)
* at the begining, it's silent,
why?
* silly!!! that's because I start playing at one part of the
mem finished
* filling, but not at the time all the mem(index ==
FREAMMEM);
*
* 5.BUG2:(refixed)
* frequencily the music on
lap, why?
* NOTICE THIS:
* the writei in fact count by
frames!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* so I use bytes is
wrong. maybe the alsa find the mem is not so long and
* do
something?
*
*/
#include stdio.h>
#include stdlib.h>
#include unistd.h>
#include
fcntl.h>
#include pthread.h>
#include
error.h>
#include
signal.h>
#include
string.h>
#include
sys/stat.h>
#include
sys/mman.h>
#include
"miniplayer_decode.h"
#include "miniplayer_play.h"
//#define _DEBUG
typedef
enum{LOCAL_FILE, NETWORK_FILE} work_modeT;
//mem_to_fill
is the mem to fill decoded data
char *mem1, *mem2, *mem_to_fill;
//mem
include the MP3 data
char
*mem_to_input;
//if the last_mem is not zero, that means it's the end of
music
int
last_mem_len = 0;
//the tid of play thread(also the main thread)
pthread_t play_tid;
//just for calling sigwait
int signo;
//working mode
work_modeT
work_mode;
sigset_t
usr2_set;
sigset_t
usr1_set;
snd_pcm_t *playback_handle;
//the test and exit function
static void test_null(void *p, const char *errstr)
{
    if(p == NULL)
    {
        fputs(errstr, stderr);
        exit(-1);
    }
}
static void
test_LT0(int no, int flag, const char *errstr)
{
    if(no  0)
    {
        if(flag ==0)
        {
            perror(errstr);
        }
        else
        {
            fputs(errstr, stderr);
        }
        exit(-1);
    }
}
static
void play(char *mem, unsigned int
length);
void
*decode_routin(void *arg);
int main()
{
    int mem_num =
0;
    pthread_t decode_tid;
#ifdef _DEBUG
    //rediret the stdin and stdout
    int stdin_fd;
    int
stdout_fd;
    stdin_fd = open("travel.mp3", O_RDONLY,
0);
    test_LT0(stdin_fd, 0, "open");
    test_LT0(dup2(stdin_fd,
STDIN_FILENO), 0, "dup2");
    stdout_fd =
open("temp", O_WRONLY|O_CREAT,
0);
    test_LT0(stdout_fd, 0, "open");
    test_LT0(dup2(stdout_fd,
STDOUT_FILENO), 0, "dup2");
#endif
    mem1 = (char *)malloc(sizeof(char) * MEMLEN);
    test_null(mem1, "malloc");
    mem2 = (char *)malloc(sizeof(char) * MEMLEN);
    test_null(mem2, "malloc");
    memset(mem1, 0, MEMLEN);
    memset(mem2, 0, MEMLEN);
   
    //the
first mem to fill decoded data is mem1
    mem_to_fill = mem1;
    //do
all prepare for playback handle
    all_prepare();
    //mask signal SIGUSR1 for waiting
    sigemptyset(&usr1_set);
    sigaddset(&usr1_set,
SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &usr1_set, NULL);
    //get play thread id
    play_tid = pthread_self();
    //create decode thread
    test_LT0(pthread_create(&decode_tid, NULL,
decode_routin, NULL), 0, "pthread create");
    //wait for SIGUSR1 to start playing
    sigwait(&usr1_set, &signo);
    fputs("begin playing\n", stderr);
    //loop for playing music
    while(1)
    {
        //tell decodr to start filling mem1 or mem2
        mem_to_fill
= mem_num?mem1:mem2;
        pthread_kill(decode_tid,
SIGUSR2);
        if(mem_num = !mem_num)
        {
            if(last_mem_len)
            {
                play(mem1, last_mem_len);
                break;
            }
            else
            {
                play(mem1, MEMLEN);
            }
        }
        else
        {
            if(last_mem_len)
            {
                play(mem2, last_mem_len);
            }
            else
            {
                play(mem2, MEMLEN);
            }
        }
    }
   
    //finish playing
    fputs("finish
playing\n", stderr);
   
    //wait for decode thread exit
    pthread_join(decode_tid,
NULL);
    close_playback_handle(playback_handle);
    free(mem1);
    mem1
= NULL;
    free(mem2);
    mem2 = NULL;
#ifdef
_DEBUG
    close(stdin_fd);
    close(stdout_fd);
#endif
    return(0);
}
static
void play(char *mem, unsigned int
length)
{
    write_to_audio(mem, length);
}
void
*decode_routin(void *arg)
{
    int retval;
    size_t
mem_size;
    struct stat stat;
    void *fdm;
    int stdin_mode;
    //mask
the USR2 for waiting
    sigemptyset(&usr2_set);
    sigaddset(&usr2_set, SIGUSR2);
    pthread_sigmask(SIG_BLOCK, &usr2_set, NULL);
    //mmap the STDIN_FILE
    test_LT0(fstat(STDIN_FILENO,
&stat), 0, "fstat");
   
    stdin_mode
= stat.st_mode &
S_IFMT;
    //if the stdin is not a regular file
    if(stdin_mode != S_IFREG)
    {
        work_mode
= NETWORK_FILE;
    }
    else
    {
        work_mode = LOCAL_FILE;
        if(stat.st_size == 0)
        {
            fputs("file size error\n", stderr);
            exit(-1);
        }
        fdm
= mmap(0,
stat.st_size, PROT_READ, MAP_SHARED,
STDIN_FILENO, 0);
        if(fdm ==
MAP_FAILED)
        {
            perror("map");
            exit(-1);
        }
        mem_to_input = (char *)fdm;
        mem_size = stat.st_size;
    }
    //decode the STD_FILE
    decode((const unsigned char *)mem_to_input, mem_size);
   
    //wait for the last USR2
    sigwait(&usr2_set, &signo);
    test_LT0(munmap(fdm ,stat.st_size), 0, "munmap");
    pthread_exit(&retval);
}
使用libmad解码的部分:
/*
*
miniplayer_decode.c
* the implementation of miniplayer_decode.h
*/
#include signal.h>
#include pthread.h>
#include stdio.h>
#include
stdlib.h>
#include unistd.h>
#include
"miniplayer_decode.h"
//#define _DEBUG
//memory to fill PCM data
extern char *mem_to_fill;
//the last memory length
extern int
last_mem_len;
//the set include USR2
extern sigset_t usr2_set;
//the play thread id
extern pthread_t
play_tid;
//jsut for calling sigwait
extern int signo;
//the current mem index
static int mem_index;
/*
* 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.
*/
enum mad_flow input(void *data,
         struct
mad_stream *stream)
{
    struct
buffer *buffer = (struct buffer
*)data;
    //fprintf(stderr, "input\n");
    sleep(5);
    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.
*/
enum mad_flow output(void *data,
         struct mad_header const *header,
         struct mad_pcm *pcm)
{
    //the first time to decode?
    static int first =
1;
    //the pos to start fill the mem
    char *mem_pos;
    //the position
    int pos = 0;
    unsigned int nchannels, nsamples;
    mad_fixed_t
const *left_ch,
*right_ch;
    //if the memory full?
    if(mem_index == FRAME_MEM)
    {
        //start the playing
        if(first)
        {
            pthread_kill(play_tid,
SIGUSR1);
            first = 0;
        }
        sigwait(&usr2_set, &signo);
        mem_index = 0;
    }
    mem_pos = mem_to_fill + mem_index *
FRAMELEN;
    /* 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++);
     mem_pos[pos++] = (sample >> 0) & 0xff;
     mem_pos[pos++] = (sample >> 8) & 0xff;
   
if (nchannels == 2) {
     
sample = scale(*right_ch++);
        mem_pos[pos++] = (sample >> 0) & 0xff;
        mem_pos[pos++] = (sample >> 8) & 0xff;
   
}
    }
    //a new MP3 frame decoded
    mem_index
++;
    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.
*/
enum
mad_flow err(void *data,
        
struct mad_stream *stream,
         struct
mad_frame *frame)
{
    struct
buffer *buffer = (struct 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 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).
*/
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,
         err, 0 /* message */);
    /* start decoding */
    result = mad_decoder_run(&decoder,
MAD_DECODER_MODE_SYNC);
    //count the length of last mem
    last_mem_len
= mem_index * FRAMELEN;
    /* release the decoder */
    mad_decoder_finish(&decoder);
    return result;
}
使用alsa播放的部分:

/*
*
miniplayer_play.c
* implement for miniplayer_play.h
*/
#include stdio.h>
#include stdlib.h>
#include
alsa/asoundlib.h>
#include
"miniplayer_play.h"
//the playback handle to process
extern snd_pcm_t *playback_handle;
//do all the preparation for playback handle
//ATTENTATIN:
//all the parameters here to set is the
usual case,
//but
not all the case. in coming versions, we should
//not depend on this assumption.
int all_prepare()
{
    char *pcm_name =
"plughw:0,0";
    int err;
    int rate = 44100;
    snd_pcm_hw_params_t
*hw_params;
    if ((err = snd_pcm_open (&playback_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0))  0)
    {
        fprintf (stderr, "cannot open
audio device %s (%s)\n",

             pcm_name,
            
snd_strerror (err));
        exit(-1);
    }
   

    if ((err =
snd_pcm_hw_params_malloc (&hw_params))  0)
    {
        fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
            
    if ((err = snd_pcm_hw_params_any (playback_handle, hw_params))  0)
    {
        fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
    if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED))  0)
    {
        fprintf (stderr, "cannot set
access type (%s)\n",
            
snd_strerror (err));
        exit(-1);
    }
    if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE))  0)
    {
        fprintf (stderr, "cannot set sample format (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
    if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, (unsigned int *)(&(rate)), 0))  0)
    {
        fprintf (stderr, "cannot set
sample rate (%s)\n",
            
snd_strerror (err));
        exit(-1);
    }
    if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2))  0)
    {
        fprintf (stderr, "cannot set
channel count (%s)\n",
            
snd_strerror (err));
        exit(-1);
    }
    if ((err = snd_pcm_hw_params (playback_handle, hw_params))  0)
    {
        fprintf (stderr, "cannot set parameters (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
    snd_pcm_hw_params_free (hw_params);
    if ((err = snd_pcm_prepare (playback_handle))  0)
    {
        fprintf (stderr, "cannot
prepare audio interface for use (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
    return(0);
}
//write the PCM data to the audio
//NOTICE:
//that the length is counted by bytes,
but the writei count it by
//frames(usually one frame is 4 bytes(2 samples, 2 channel),
here frame is
//a
PCM frame, not a MP3 frame.
int write_to_audio(char *mem, int length)
{
    int err;
    snd_pcm_uframes_t frames = length >> 2 ;
    err =
snd_pcm_writei(playback_handle, mem, frames);
    if(err
== -EPIPE)
    {
        fprintf(stderr, "underrun
occurred\n");
        snd_pcm_prepare(playback_handle);
    }
    else if(err == -ESTRPIPE)
    {
        fprintf(stderr, "suspend
occurred\n");
        while((err = snd_pcm_resume(playback_handle)) == -EAGAIN)
        {
            sleep(1);
        }
        if(err  0)
        {
            err
= snd_pcm_prepare(playback_handle);
        }
    }
    else if(err  0)
    {
        fprintf(stderr, "error from
writei: %s\n",
snd_strerror(err));
    }
    else if(err != (int)frames)
    {
        fprintf(stderr, "short write,
write %d frames of total %d\n",
                err, frames);
    }
}
//close the playback_handle
int close_playback_handle(snd_pcm_t *playback_handle)
{
    snd_pcm_drain(playback_handle);
    snd_pcm_close(playback_handle);
}
现在要做的工作很简单,就是不用alsa,而直接操作/dev/dsp。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/109710/showart_2163871.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP