使用ffmpeg为库编写的小型多媒体播放器源代码
今天突发奇想,就在以前音频播放器(详细情况请看这里——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);
}
来至:http://blog.csdn.net/baymoon/archive/2007/03/15/1530629.aspx