说明一点,我之前是做Java的,在多媒体这块也是初学者,突然接触C语言感觉有一定难度,所以,6月份花了一个月时间自学了C语言和C++,粗略的看了ffplay.c的源代码,感觉逻辑性不如Java那么强,看起来挺吃力,所以,我也希望各位同仁,在学习ffmpeg之前,没有C和C++基础的,需要恶补一下。不管怎么样,我相信功夫不负有心人,让我们共同学习共同进步吧!
在这里我把我的学习经历分享给大家,对的地方大家参考参考,不对的地方也希望大家多多指正,谢谢!
写代码之前,先请各位朋友去了解一下FFMPEG和SDL的工作原理,我简单介绍一下,FFMPEG用于视音频编解码,SDL用于视音频播放。
下面简单写了FFMPEG+SDL实现的一个视音频播放器,为了供初学者学习,还没做视音频同步。播放起来有些卡顿,但是,基本播放音视频的功能是有了。
/**
* 丁鹏
*
* 2016-10-18
*/
#include "stdafx.h"
#include
#include
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
//SDL
#include "sdl/SDL.h"
#include "sdl/SDL_thread.h"
};
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
/* The audio function callback takes the following parameters:
* stream: A pointer to the audio buffer to be filled
* len: The length (in bytes) of the audio buffer
* 回调函数
*/
void fill_audio(void *udata,Uint8 *stream,int len){
if(audio_len==0) /* Only play if we have data left */
return;
len=(len>audio_len?audio_len:len); /* Mix as much data as possible */
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
int _tmain(int argc, _TCHAR* argv[])
{
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx, *vCodecCtx;
AVCodec *pCodec, *vCodec;
char url[] = "掀起你的盖头来.FLV";
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
//Open
if(avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0){
printf("Couldn't find stream information.\n");
return -1;
}
// Dump valid information onto standard error
av_dump_format(pFormatCtx, 0, url, false);
// Find the first audio stream
int audioStream = -1, videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
videoStream = i;
}
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
audioStream = i;
}
}
if (audioStream == -1 || videoStream == -1){
printf("Didn't find a audio|video stream.\n");
return -1;
}
vCodecCtx = pFormatCtx->streams[videoStream]->codec;
// Get a pointer to the codec context for the audio stream
pCodecCtx = pFormatCtx->streams[audioStream]->codec;
// Find the decoder for the audio stream
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
if(pCodec == NULL) {
printf("Codec not found.\n");
return -1;
}
// Open 视频
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
// Open 音频
if (avcodec_open2(vCodecCtx, vCodec, NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
AVFrame *pFrame = av_frame_alloc(), *pFrameYUV = av_frame_alloc();
int frame_size = avpicture_get_size(PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height);
uint8_t *v_buffer = (uint8_t *)av_malloc(frame_size*sizeof(uint8_t));
avpicture_fill((AVPicture*)pFrameYUV, v_buffer, PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height);
SwsContext *swsCtx = sws_getCachedContext(NULL, vCodecCtx->width, vCodecCtx->height, vCodecCtx->pix_fmt,
vCodecCtx->width, vCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
if (sws_init_context(swsCtx, NULL, NULL) < 0) {
printf("转换器初始化失败\n");
exit(0);
}
//Init
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf( "Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
//解析视频
SDL_Surface *surface = SDL_SetVideoMode(vCodecCtx->width, vCodecCtx->height, 0, SDL_SWSURFACE);
SDL_Overlay *bmp = SDL_CreateYUVOverlay(vCodecCtx->width, vCodecCtx->height, SDL_IYUV_OVERLAY, surface);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = vCodecCtx->width;
rect.h = vCodecCtx->height;
//解析音频
uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
int audio_length = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
SDL_AudioSpec wanted_spec;
wanted_spec.freq = pCodecCtx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);// pCodecCtx->channels;
wanted_spec.silence = 0;
wanted_spec.samples = audio_length;
wanted_spec.callback = fill_audio;
wanted_spec.userdata = NULL;
if (SDL_OpenAudio(&wanted_spec, NULL) < 0){
printf("can't open audio.\n");
return -1;
}
int got_frame, got_audio;
//Swr
struct SwrContext *swrCtx = swr_alloc();
swrCtx = swr_alloc_set_opts(swrCtx, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, pCodecCtx->sample_rate,
pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
swr_init(swrCtx);
while(av_read_frame(pFormatCtx, packet) >= 0){
if (packet->stream_index == videoStream){
//Decode
avcodec_decode_video2(vCodecCtx, pFrame, &got_frame, packet);
if (got_frame) {
printf("o");
sws_scale(swsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, vCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
SDL_LockYUVOverlay(bmp);
bmp->pixels[0] = pFrameYUV->data[0];
bmp->pixels[1] = pFrameYUV->data[1];
bmp->pixels[2] = pFrameYUV->data[2];
bmp->pitches[0] = pFrameYUV->linesize[0];
bmp->pitches[1] = pFrameYUV->linesize[1];
bmp->pitches[2] = pFrameYUV->linesize[2];
SDL_UnlockYUVOverlay(bmp);
SDL_DisplayYUVOverlay(bmp, &rect);
//Delay 40ms
SDL_Delay(40);
}
}
else if (packet->stream_index == audioStream){
//Decode
avcodec_decode_audio4(pCodecCtx, pFrame, &got_audio, packet);
if (got_audio) {
printf("x");
swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);
//FIX:FLAC,MP3,AAC Different number of samples
if (wanted_spec.samples != pFrame->nb_samples){
SDL_CloseAudio();
audio_length = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pFrame->nb_samples, AV_SAMPLE_FMT_S16, 1);
wanted_spec.samples = pFrame->nb_samples;
SDL_OpenAudio(&wanted_spec, NULL);
}
}
audio_chunk = (Uint8 *)out_buffer;
//Audio buffer length
audio_len = audio_length;
audio_pos = audio_chunk;
//Play
SDL_PauseAudio(0);
while (audio_len>0)//Wait until finish
SDL_Delay(1);
}
av_free_packet(packet);
}
swr_free(&swrCtx);
sws_freeContext(swsCtx);
//SDL_free(&event);
SDL_FreeSurface(surface);
SDL_FreeYUVOverlay(bmp);
SDL_CloseAudio();//Close SDL
SDL_Quit();
av_free(v_buffer);
av_free(out_buffer);
// Close the codec
avcodec_close(pCodecCtx);
avcodec_close(vCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 0;
}