使用ffmpeg为库编写的小型多媒体播放器源代码

来源:互联网 发布:linux expect -c 编辑:程序博客网 时间:2024/04/28 17:27

 今天突发奇想,就在以前音频播放器(详细情况请看这里——http://blog.csdn.net/baymoon/archive/2006/11/16/1388693.aspx)的基础上用ffmpeg写了个简单的多媒体播放器,这里把源代码贴出来,供大家参评;这里的多媒体播放,并没有用到什么很强大的音视频同步技术,而只是简单的使用了视频随着音频同步,想必你看了代码之后会有所悟的。。。不多说了,看代码。。。

/***************************************************************************
 *            main.cc
 *
 *  Thu Nov  9 20:47:33 2006
 *  Copyright  2006 
 *  Email lsosa.cs2c
 ***************************************************************************
*/



#include 
<avcodec.h>
#include 
<avformat.h>
#include 
<avutil.h>
#include 
<assert.h>
#include 
<stdio.h>
#include 
<stdlib.h>
#include 
<X11/Xlib.h>
#include 
<sys/soundcard.h>
#include 
<sys/stat.h>
#include 
<fcntl.h>
#include 
<sys/ioctl.h>
#include 
<unistd.h>
#include 
<errno.h>
#include 
<string.h>
#include 
<sched.h>
#include 
<SDL/SDL.h>

#define ALL_DEBUG

#ifdef ALL_DEBUG
    
#define AV_DEBUG
    
#define AUDIO_DEBUG
#endif

//------------------------------------------------------------------------------
// manipulations for file
int open_file (char *file_name, int mode)
{
    
// open file file_name and return the file descriptor;
    int fd;

    
if ((fd = open (file_name, mode)) < 0)
    
{
        fprintf (stderr, 
" Can't open %s! ", file_name);
        exit (
-1);
    }

    
return fd;
}


int set_audio (int fd, AVCodecContext * pCodecCtx)
{
    
// set the properties of audio device with pCodecCtx;

    
int i, err;
    
/* 设置适当的参数,使得声音设备工作正常 */
    
/* 详细情况请参考Linux关于声卡编程的文档 */
    
    i 
= 0;
    ioctl (fd, SNDCTL_DSP_RESET, 
&i);
    i 
= 0;
    ioctl (fd, SNDCTL_DSP_SYNC, 
&i);
    i 
= 1;
    ioctl (fd, SNDCTL_DSP_NONBLOCK, 
&i);
    
    
// set sample rate;
    #ifdef AUDIO_DEBUG
    printf (
"pCodecCtx->sample_rate:%d ", pCodecCtx->sample_rate);
    
#endif
    i 
= pCodecCtx->sample_rate;
    
if (ioctl (fd, SNDCTL_DSP_SPEED, &i) == -1)
    
{
        fprintf (stderr, 
"Set speed to %d failed:%s ", i,
             strerror (errno));
        
return (-1);
    }

    
if (i != pCodecCtx->sample_rate)
    
{
        fprintf (stderr, 
"do not support speed %d,supported is %d ",
             pCodecCtx
->sample_rate, i);
        
return (-1);
    }

    
    
// set channels;
    i = pCodecCtx->channels;
    #ifdef AUDIO_DEBUG
    printf (
"pCodecCtx->channels:%d ", pCodecCtx->channels);
    
#endif
    
if ((ioctl (fd, SNDCTL_DSP_CHANNELS, &i)) == -1)
    
{
        fprintf (stderr, 
"Set Audio Channels %d failed:%s ", i,
             strerror (errno));
        
return (-1);
    }

    
if (i != pCodecCtx->channels)
    
{
        fprintf (stderr, 
"do not support channel %d,supported %d ",
            pCodecCtx
->channels, i);
        
return (-1);
    }

    
// set bit format;
    i = AFMT_S16_LE;
    
if (ioctl (fd, SNDCTL_DSP_SETFMT, &i) == -1)
    
{
        fprintf (stderr, 
"Set fmt to bit %d failed:%s ", i,
             strerror (errno));
        
return (-1);
    }

    
if (i != AFMT_S16_LE)
    
{
        fprintf (stderr, 
"do not support bit %d, supported %d ",
             AFMT_S16_LE, i);
        
return (-1);
    }

    
    
// set application buffer size;
    
// i = (0x00032 << 16) + 0x000c;        // 32 4kb buffer;
    
// ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &i);
    i = 1;
    ioctl (fd, SNDCTL_DSP_PROFILE, 
&i);
    
    
return 0;
}


void close_file (int fd)
{
    
// close the file pointed by file descriptor fd;
    close (fd);
}


//------------------------------------------------------------------------------
// handle audio;

void display_AVCodecContext(AVCodecContext *pCodecCtx){
    
//
    #define STDOUT stderr
    fprintf(STDOUT, 
"pCodecCtx->bit_rate:%d ", pCodecCtx->bit_rate);
    fprintf(STDOUT, 
"pCodecCtx->sample_rate:%d ", pCodecCtx->sample_rate);
    fprintf(STDOUT, 
"pCodecCtx->channels:%d ", pCodecCtx->channels);
    fprintf(STDOUT, 
"pCodecCtx->frame_size:%d ", pCodecCtx->frame_size);
    fprintf(STDOUT, 
"pCodecCtx->frame_number:%d ", pCodecCtx->frame_number);
    fprintf(STDOUT, 
"pCodecCtx->delay:%d ", pCodecCtx->delay);
    fprintf(STDOUT, 
"pCodecCtx->frame_bits:%d ", pCodecCtx->frame_bits);
}


// error if return -1;
// success if return 0;
// 这里要用到指向指针的指针,否则传不到值;
int av_init (char *file_name, AVFormatContext ** pFormatCtx,
    AVCodecContext 
** pAudioCodecCtx, int *p_audioStream, 
    AVCodecContext 
** pVideoCodecCtx, int *p_videoStream)
{
    
// init the codec and format of input file file_name;
    int audioStream, i;
    
int videoStream;
    AVCodec 
*pAudioCodec;
    AVCodec 
*pVideoCodec;
    
// catch error
    assert(file_name != NULL);
    assert(
*pFormatCtx != NULL);
    assert(
*pAudioCodecCtx != NULL);
    
    
// Register all formats and codecs
    av_register_all ();
    
    
// open file
    if (av_open_input_file (pFormatCtx, file_name, NULL, 0, NULL) != 0){
        
// Couldn't open file
        fprintf (stderr, " Can't open %s! ", file_name);
        
return -1;    
    }


    
// Retrieve stream information
    if (av_find_stream_info (*pFormatCtx) < 0){
        
// Couldn't find stream information
        return -1;    
    }

    
    #ifdef AV_DEBUG
    
// Dump information about file onto standard error
    dump_format (*pFormatCtx, 0, file_name, false);
    
#endif
    
    
// Find the first audio and video stream respectively
    audioStream = -1;
    videoStream 
= -1;
    
for (i = 0; i < (*pFormatCtx)->nb_streams; i++){
        
if ((*pFormatCtx)->streams[i]->codec->codec_type ==
            CODEC_TYPE_AUDIO)
        
{
            audioStream 
= i;
        }
else if ((*pFormatCtx)->streams[i]->codec->codec_type ==
            CODEC_TYPE_VIDEO)
{
            videoStream 
= i;
        }

    }

    
    #ifdef AV_DEBUG
    
// dump_stream_info(pFormatCtx);
    #endif
    
    
// exclude error
    if (audioStream == -1){
        
// Didn't find a audio or video stream
        
// return -1;    
        printf("No Audio ");
    }

    
if (videoStream == -1){
        
// Didn't find a audio or video stream
        
// return -1;    
        printf("No Video ");
    }


    
// Get a pointer to the codec context for the audio stream
    *pAudioCodecCtx = (*pFormatCtx)->streams[audioStream]->codec;
    
*pVideoCodecCtx = (*pFormatCtx)->streams[videoStream]->codec;

    
// Find the decoder for the audio stream
    pAudioCodec = avcodec_find_decoder ((*pAudioCodecCtx)->codec_id);
    pVideoCodec 
= avcodec_find_decoder ((*pVideoCodecCtx)->codec_id);
    
// 
    if (pAudioCodec == NULL){
        
return -1;    // Codec not found
    }

    
if (pVideoCodec == NULL){
        
return -1;    // Codec not found
    }


    
// Open audio codec
    if (avcodec_open ((*pAudioCodecCtx), pAudioCodec) < 0){
        
return -1;    // Could not open codec
    }

    
// Open video codec
    if (avcodec_open ((*pVideoCodecCtx), pVideoCodec) < 0){
        
return -1;    // Could not open codec
    }

    
    #ifdef AUDIO_DEBUG
    
// printf ("pCodecCtx->sample_rate:%d, audioStream:%d ", (*pCodecCtx)->sample_rate, audioStream);
    
// display_AVCodecContext(*pCodecCtx);
    #endif
    
    
*p_audioStream = audioStream;
    
*p_videoStream = videoStream;
    
    
return 0;
}


void av_play (AVFormatContext * pFormatCtx,
    AVCodecContext 
* pAudioCodecCtx, int audioStream, 
    AVCodecContext 
* pVideoCodecCtx, int videoStream)
    
// AVCodecContext * pCodecCtx, int audioStream)
{
    
// which was read from one frame;
    AVPacket packet;
    uint32_t len;
    uint8_t decompressed_audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE 
* 3/ 2];
    
int decompressed_audio_buf_size;
    uint8_t 
* p_decompressed_audio_buf;
    
int fd = -1;    // audio file or test file?
    char filename[64= "/dev/dsp";
    
int mode = O_WRONLY;
    
// Video;
    AVFrame *pFrame;
    AVFrame 
*pFrameYUV;
    
int frameFinished;
    
    
///////// SDL initialization
    SDL_Surface *screen =
    SDL_SetVideoMode (pVideoCodecCtx
->width, pVideoCodecCtx->height, 0, SDL_HWSURFACE);
    SDL_Overlay 
*overlay =
    SDL_CreateYUVOverlay (pVideoCodecCtx
->width, pVideoCodecCtx->height,
              SDL_YV12_OVERLAY,
              screen);
    
static SDL_Rect rect;
    rect.x 
= 0;
    rect.y 
= 0;
    rect.w 
= pVideoCodecCtx->width;
    rect.h 
= pVideoCodecCtx->height;
    
//////////
    
    
// open audio file or written file
    
// printf("fd:%d", fd);
    fd = open_file(filename, mode);
    
// printf("fd:%d", fd);
    
// 
    set_audio(fd, pAudioCodecCtx);
    
    
//
    printf("(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2=%d ", (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3/ 2);
    printf(
"AVCODEC_MAX_AUDIO_FRAME_SIZE=%d ", AVCODEC_MAX_AUDIO_FRAME_SIZE);
    
    
// for a test
    
// char test_file[256] = "my_pcm.pcm";
    
// fd = open_file(test_file, mode);
    
    #ifdef AV_DEBUG
    
static int size = 0;
    
#endif
    
//
    
    
// set the sched priority
    
// 这是为了提高音频优先级;不晓得起作用没;
    int policy = SCHED_FIFO;
    sched_setscheduler(
0, policy, NULL);
    
    
// Allocate video frame
    pFrame = avcodec_alloc_frame ();
    
// Allocate an AVFrame structure
    pFrameYUV = avcodec_alloc_frame ();
    
if (pFrameYUV == NULL)
        
return;
    
    
// Set SDL events
    SDL_EventState (SDL_ACTIVEEVENT, SDL_IGNORE);
    SDL_EventState (SDL_MOUSEMOTION, SDL_IGNORE);
    
// SDL_ShowCursor (SDL_ENABLE);
    
    
int write_buf_size = 4196;
    
int written_size;
    
while ((av_read_frame (pFormatCtx, &packet) >= 0)
        
&& (SDL_PollEvent (NULL) == 0))
    
{
        
// Is this a packet from the audio stream?
        
// 判断是否音频帧;
        if (packet.stream_index == audioStream)
        
{
            
// Decode audio frame
            
// 解码音频数据为pcm数据;
            len = avcodec_decode_audio (pAudioCodecCtx,  
                            (int16_t 
*)decompressed_audio_buf, 
                            
&decompressed_audio_buf_size,        // it is the decompressed frame in BYTES 解码后的数据大小,字节为单位;
                            packet.data, 
                            packet.size );
            
// printf("len:%d, packet.size:%d ", len, packet.size);
            
// printf("packet.pts:%d packet.dts:%d ", packet.pts, packet.dts);
            if ( len < 0 ){
                
// if error len = -1
                printf("+----- error in decoding audio frame ");
                
// exit(0);
            }

            
// audio_buf_info info;
            p_decompressed_audio_buf = decompressed_audio_buf;
            
while ( decompressed_audio_buf_size > 0 ){
                
// 解码后数据不为零,则播放之,为零,则;
                written_size = write(fd, p_decompressed_audio_buf, decompressed_audio_buf_size);
                
if ( written_size == -1 ){
                    
// printf("error:decompressed_audio_buf_size:%d, decompressed_audio_buf_size:%d, %s ", 
                                decompressed_audio_buf_size, decompressed_audio_buf_size,strerror(errno));
                    
// usleep(100);
                    continue;
                }

                
// printf("decompressed_audio_buf_size:%d, written_size:%d ", 
                            decompressed_audio_buf_size, written_size);
                decompressed_audio_buf_size 
-= written_size;
                p_decompressed_audio_buf 
+= written_size;
            }
// end while
        }

        
else if (packet.stream_index == videoStream)
        
{
            
// Decode video frame
            avcodec_decode_video (pVideoCodecCtx, pFrame, &frameFinished,
                    packet.data, packet.size);
            
// Did we get a video frame?
            if (frameFinished) {
            
// Convert the image from its native format to YUV, and display
            
            SDL_LockYUVOverlay (overlay);
            pFrameYUV
->data[0= overlay->pixels[0];
            pFrameYUV
->data[1= overlay->pixels[2];
            pFrameYUV
->data[2= overlay->pixels[1];
            
            pFrameYUV
->linesize[0= overlay->pitches[0];
            pFrameYUV
->linesize[1= overlay->pitches[2];
            pFrameYUV
->linesize[2= overlay->pitches[1];
            
            img_convert ((AVPicture 
*) pFrameYUV, PIX_FMT_YUV420P, 
                        (AVPicture 
*) pFrame, pVideoCodecCtx->pix_fmt, 
                        pVideoCodecCtx
->width, pVideoCodecCtx->height);
            SDL_UnlockYUVOverlay (overlay);
            SDL_DisplayYUVOverlay (overlay, 
&rect);
            
///
            // SDL_Delay (33);
            }

        }
// end if
        
// Free the packet that was allocated by av_read_frame
        av_free_packet (&packet);
    }
// end while of reading one frame;
    
    
// Free the RGB image
    av_free (pFrameYUV);
    
// Free the YUV frame
    av_free (pFrame);
    
// for test lsosa
    
// printf("size = %d ", size / 1024 / 1024 );
    SDL_FreeYUVOverlay (overlay);
    
    close_file(fd);
}


void av_close (AVFormatContext * pFormatCtx, AVCodecContext * pAudioCodecCtx, 
    AVCodecContext 
* pVideoCodecCtx)
{
    
// close the file and codec
    
// Close the codec
    avcodec_close (pAudioCodecCtx);
    
// Close the codec
    avcodec_close (pVideoCodecCtx);

    
// Close the video file
    av_close_input_file (pFormatCtx);
}


//------------------------------------------------------------------------------

int main (int argc, char **argv){
    
//
    AVFormatContext *pFormatCtx;
    
int audioStream = -1;
    
int videoStream = -1;
    AVCodecContext 
*pAudioCodecCtx;
    AVCodecContext 
*pVideoCodecCtx;
    
    
// exclude the error about args;
    if ( argc != 2 ){
        printf(
"please give a file name ");
        exit(
0);
    }

    
    
// 注意:这里要用到指向指针的指针,是因为这个初始化函数需要对指针的地址进行改动,
    
// 所以,只有这么做,才能达到目的;
    if ( av_init(argv[1], &pFormatCtx, &pAudioCodecCtx, &audioStream, &pVideoCodecCtx, &videoStream) < 0 ){
        
//
        fprintf(stderr, "error when av_init ");
    }

    
    
// play the audio file
    av_play(pFormatCtx, pAudioCodecCtx, audioStream, pVideoCodecCtx, videoStream);
    
    
// close all the opend files
    av_close(pFormatCtx, pAudioCodecCtx, pVideoCodecCtx);
    
}

原创粉丝点击