說明一點,我之前是做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;
}