#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <libavutil/samplefmt.h>
struct AVContext{
AVFormatContext* fc;
AVCodecContext* acc;
AVCodecContext* vcc;
int audio_index;
int video_index;
struct SwsContext* sws;
struct SwrContext* swr;
SDL_Surface* wnd;
SDL_Overlay* overlay;
SDL_AudioSpec audio_spec;
int width;
int height;
int fdRead;
int fdWrite;
AVPacket* packet;
AVFrame* audio_frame;
AVFrame* video_frame;
};
struct AVContext avc;
int doWrite(int fd, uint8_t* buf, int len)
{
int index = 0;
while (index != len)
{
int ret = write(fd, buf + index, len - index);
if (ret > 0)
index += ret;
else if (ret <= 0)
return -1;
}
return 0;
}
int doRead(int fd, uint8_t* buf, int len)
{
int index = 0;
while (index != len)
{
int ret = read(fd, buf + index, len - index);
if (ret > 0)
index += ret;
else if (ret <= 0)
return -1;
}
}
void open_avfile(const char* filename)
{
if (NULL == filename)
{
return;
}
avc.fc = NULL;
if (avformat_open_input(&avc.fc, filename, NULL, NULL) != 0)
{
printf("open error\n");
exit(1);
}
}
void find_stream_info()
{
avc.audio_index = -1;
avc.video_index = -1;
if (avformat_find_stream_info(avc.fc, NULL) != 0)
{
printf("find stream info error\n");
exit(1);
}
int i;
for (i = 0; i < avc.fc->nb_streams; ++i)
{
if (avc.fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
avc.audio_index = i;
}
else if (avc.fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
avc.video_index = i;
}
}
if (avc.audio_index == -1 || avc.video_index == -1)
{
printf("cannot find audio or video stream info err\n");
exit(1);
}
}
void open_codec_context()
{
avc.acc = NULL;
avc.vcc = NULL;
// video
avc.vcc = avc.fc->streams[avc.video_index]->codec;
avc.fc->video_codec = avcodec_find_decoder(avc.vcc->codec_id);
if (avcodec_open2(avc.vcc, avc.fc->video_codec, NULL) != 0)
{
printf("open video codec err\n");
}
// audio
avc.acc = avc.fc->streams[avc.audio_index]->codec;
avc.fc->audio_codec = avcodec_find_decoder(avc.acc->codec_id);
if (avcodec_open2(avc.acc, avc.fc->audio_codec, NULL) != 0)
{
printf("open audio codec err\n");
}
}
void init_convert_context()
{
avc.sws = NULL;
avc.swr = NULL;
avc.sws = sws_getContext(avc.vcc->width, avc.vcc->height, avc.vcc->pix_fmt,
avc.vcc->width, avc.vcc->height, avc.vcc->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
avc.swr = swr_alloc_set_opts(NULL,
avc.acc->channel_layout,
AV_SAMPLE_FMT_S16,
avc.acc->sample_rate,
avc.acc->channel_layout,
avc.acc->sample_fmt,
avc.acc->sample_rate,
0, NULL);
swr_init(avc.swr);
}
void audio_callback(void* userdata, Uint8* stream, int len)
{
doRead(avc.fdRead, stream, len);
}
void init_sdl()
{
avc.width = avc.vcc->width;
avc.height = avc.vcc->height;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0)
{
printf("sdl init error\n");
exit(1);
}
avc.wnd = SDL_SetVideoMode(avc.width, avc.height, 0, 0);
if (avc.wnd == NULL)
{
printf("create window error\n");
exit(1);
}
avc.overlay = SDL_CreateYUVOverlay(avc.width, avc.height, SDL_YV12_OVERLAY
, avc.wnd);
if (avc.overlay == NULL)
{
printf("create overlay error\n");
exit(1);
}
// audio play
avc.audio_spec.callback = audio_callback;
avc.audio_spec.channels = avc.acc->channels;
avc.audio_spec.format = AUDIO_S16SYS;
avc.audio_spec.freq = avc.acc->sample_rate;
avc.audio_spec.samples = 1024;
avc.audio_spec.silence = 0;
avc.audio_spec.userdata = NULL;
if (SDL_OpenAudio(&avc.audio_spec, NULL) != 0)
{
printf("sdl open audio error\n");
exit(1);
}
SDL_PauseAudio(0);
int fd[2];
pipe(fd);
// 为了简便,用管道来控制每次读取的数据
avc.fdRead = fd[0];
avc.fdWrite = fd[1];
}
void process_audio_data()
{
int got;
avcodec_decode_audio4(avc.acc, avc.audio_frame, &got, avc.packet);
if (got == 0)
return;
int bytesPerSample = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
int frameSize = avc.acc->frame_size * avc.acc->channels * bytesPerSample;
uint8_t* data = (uint8_t*)malloc(frameSize);
swr_convert(avc.swr, &data, avc.acc->frame_size * 2,
avc.audio_frame->extended_data, avc.acc->frame_size);
doWrite(avc.fdWrite, data, frameSize);
free(data);
}
void process_video_data()
{
int got;
avcodec_decode_video2(avc.vcc, avc.video_frame, &got, avc.packet);
if (got == 0)
return;
SDL_LockYUVOverlay(avc.overlay);
uint8_t* data[AV_NUM_DATA_POINTERS];
int linesize[AV_NUM_DATA_POINTERS];
data[0] = avc.overlay->pixels[0];
data[1] = avc.overlay->pixels[1];
data[2] = avc.overlay->pixels[2];
linesize[0] = avc.overlay->pitches[0];
linesize[1] = avc.overlay->pitches[1];
linesize[2] = avc.overlay->pitches[2];
sws_scale(avc.sws, avc.video_frame->data, avc.video_frame->linesize,
0, avc.vcc->height,data, linesize);
SDL_UnlockYUVOverlay(avc.overlay);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = avc.wnd->w;
rect.h = avc.wnd->h;
SDL_DisplayYUVOverlay(avc.overlay, &rect);
}
void process_data()
{
avc.packet = av_packet_alloc();
avc.audio_frame = av_frame_alloc();
avc.video_frame = av_frame_alloc();
while (1)
{
if (av_read_frame(avc.fc, avc.packet) < 0)
{
break;
}
if (avc.packet->stream_index == avc.audio_index)
{
process_audio_data();
}
else if (avc.packet->stream_index == avc.video_index)
{
process_video_data();
}
}
}
int main()
{
av_register_all();
open_avfile("/home/tuser/test/test.mp4");
find_stream_info();
open_codec_context();
init_convert_context();
init_sdl();
process_data();
return 0;
}
Makefile:
play.out: play.c
gcc play.c -o play.out -pthread -lavdevice -lavfilter -lswscale -lpostproc -lavformat -lavcodec -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lX11 -lasound -lSDL -lx264 -lpthread -ldl -lfaac -lz -lswresample -lavutil -lm
gcc play.c -o play.out -pthread -lavdevice -lavfilter -lswscale -lpostproc -lavformat -lavcodec -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lX11 -lasound -lSDL -lx264 -lpthread -ldl -lfaac -lz -lswresample -lavutil -lm