根据ffmpeg官方网站上的例子程序开始学习ffmpeg和SDL编程。

SDL是一个跨平台的多媒体开发包。适用于游戏,模拟器,播放器等应用软件开发。支持linux 、win32 等操作系统。

主要应用:

视频

  • 设置8bpp或更高的任意色彩深度的视频模式。如果某个模式硬件不支持,可以选择转化为另一模式。
  • 直接写入线性的图像帧缓冲(framebuffer)。
  • 用颜色键值(colorkey)或者alpha混合属性创建surface。
  • Surface的blit能自动的转化为目标格式。blit是优化过的,并能使用硬件加速。x86平台上有针对MMX优化过的blit。
  • 硬件加速的blit和fill(填充)操作,如果硬件支持的话。

事件

  • 提供以下事件:
    • 应用程序的visibility发生改变
    • 键盘输入
    • 鼠标输入
    • 用户要求的退出
  • 每种事件都能通过SDL_EventState()关闭或者打开。
  • 事件经由用户指定的过滤函数再被加入到内部的事件队列。
  • 线程安全的事件队列。

音频

  • 设置8位和16位的音频,单声道或者立体声,如果格式硬件不支持,可以选择转换。
  • 由独立的线程执行音频部分,并提供用户回调(callback)机制。
  • 设计上考虑到了客户定制的软混音器,但实际上在例程中就包含了一个完整的音频/音乐输出库。

CD音频

  • 完整的CD音频控制API

线程

  • 简单的线程创建API
  • 用于同步的简单的二进制信号量(semaphores)

定时器

  • 读取已流逝的毫秒数。
  • 等待指定的毫秒数。
  • 设置一个10毫秒精度的周期性定时器。

字节序无关

  • 侦测当前系统的字节序
  • 快速转换数据的函数
  • 读写指定字节序的数据

这里我们使用SDL作为音视频输出对象,ffmpeg完成音视频的解码。

像使用其他软件包或者开发库一样,首先肯定要初始化相应的库,然后才能够使用,初始化函数如下:

if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
        {
          fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());
          return -1 ;
        }

SDL有很多方法是实现视频的输出,但是YUV overlay是一种简单而又常用的方法,具体使用方法是:

首先创建一个surface用来显示视频数据,然后创建一个overlay,然后就可以通过overlay输出视频到surface

其创建过程如下:

int  init_sdl(int width ,int height)
{

//create screen for displaying
    screen = SDL_SetVideoMode(width, height, 0, 0);
    if(!screen)
    {
          fprintf(stderr, "SDL: could not set video mode - exiting/n");
          return -1 ;
    }
  
    //Now we create a YUV overlay on that screen so we can input video to it:
    bmp = SDL_CreateYUVOverlay(width, height,
                           SDL_YV12_OVERLAY, screen);

return 0 ;
}

创建后就可以显示视频数据了,我对此进行了简单的封装,如下:

//显示函数,提取一个完整的视频帧后,就可以显示此函数
void sdl_display(AVPicture *pict,SDL_Overlay *bmp,enum PixelFormat src_fmt,int width,int height)
{
    SDL_Rect rect ;

struct SwsContext *img_convert_ctx=NULL;
   AVPicture p;

SDL_LockYUVOverlay(bmp);

p.data[0] = bmp->pixels[0];
     p.data[1] = bmp->pixels[2];
     p.data[2] = bmp->pixels[1];

p.linesize[0] = bmp->pitches[0];
      p.linesize[1] = bmp->pitches[2];
      p.linesize[2] = bmp->pitches[1];

//视频格式转化为YUV420P格式
       img_convert_ctx=sws_getCachedContext(img_convert_ctx,width,height,
                     src_fmt,width,height,PIX_FMT_YUV420P,
                    SWS_X ,NULL,NULL,NULL) ;
       if (img_convert_ctx == NULL)
       {
                       
                 printf("can't init convert context!/n") ;
                 return ;
        }
      sws_scale(img_convert_ctx, pict->data, pict->linesize,
                           0,width, p.data, p.linesize);
     
             
       SDL_UnlockYUVOverlay(bmp);

//设置显示区域的位置和大小
       rect.x = 0;
       rect.y = 0;
       rect.w = width;
       rect.h = height;
    //显示视频帧
       SDL_DisplayYUVOverlay(bmp, &rect);
 
}

这样在解码出一帧数据后就可以通过调用此函数完成视频的显示了

视频显示搞定了,那么该轮到音频输出

要想输出音频,首先必须得打开音频设备,SDL对音频设备的打开和初始化已经做好了封装,我们通过调用SDL_OpenAudio 来打开和初始化音频设备,通过结构体 SDL_AudioSpec 设置相应的参数,然后将参数通过 SDL_OpenAudio 设置好设备,封装如下:

SDL_AudioSpec audio_spec ,spec;

int init_sdl_audio(AVCodecContext *aCodecCtx)
{
    audio_spec.freq = aCodecCtx->sample_rate;
    audio_spec.format = AUDIO_S16SYS;
    audio_spec.channels = aCodecCtx->channels;
    audio_spec.silence = 0;
    audio_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    audio_spec.callback = audio_callback;
    audio_spec.userdata = aCodecCtx;

if(SDL_OpenAudio(&audio_spec, &spec) < 0)
    {
          fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());
          return -1;
    }

return 0 ;
}

其他就和视频一样了,先分解出音频流,然后根据音频流找出解码上下文,再根据解码上下文找到解码器,并打开了,接着就可以进行解码了。

但是我们不能想解码视频一样,直接对音频包进行解码,我们不断从文件中的packet,同时SDL又要不断的调用回调函数,解决的办法是创建一个互斥队列,ffmpeg已经为我们封装了一个AVPacketList结构体,我们需要对此进行再次封装如下:

typedef struct PacketQueue {

AVPacketList

*first_pkt, *last_pkt;

int nb_packets;

int size;

SDL_mutex

*mutex;

SDL_cond

*cond;

} PacketQueue;

我们得注意:这里的size是packet的大小,而nb_packets是队列中packet的个数。

对于一个队列首先得有一个初始化函数,完成初始化

void packet_queue_init(PacketQueue *q) {

memset(q, 0, sizeof(PacketQueue));

q->mutex = SDL_CreateMutex

();

q->cond = SDL_CreateCond

();

}

很明显这个初始化函数完成了队列的内存分配、互斥量和条件量的创建。

然后就是入队和出队的函数

//put audio packet in the queue

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

AVPacketList *pkt1;

//if pkt is not allocated ,allocate it

if(av_dup_packet(pkt) < 0) {

return -1;

}

//allocate space for new member of queue
pkt1 = av_malloc(sizeof(AVPacketList));

if (!pkt1)

return -1;

//put pkt in pkt1

pkt1->pkt = *pkt;

pkt1->next = NULL;

//lock queue and wait until finishing put

SDL_LockMutex(q->mutex);

//if last_pkt is NULL,it means that the queue is NULL,so put the packet in the first position

if (!q->last_pkt)

q->first_pkt = pkt1;

else

q->last_pkt->next = pkt1;

q->last_pkt = pkt1;

q->nb_packets++;

q->size += pkt1->pkt.size;

//send signal of finish

SDL_CondSignal(q->cond);

SDL_UnlockMutex(q->mutex);

return 0;

}

//put audio packet in the queue

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {

AVPacketList *pkt1;

int ret;

SDL_LockMutex(q->mutex);

for(;;) {

if(quit) {

ret = -1;

break;

}

pkt1 = q->first_pkt;

if (pkt1) {

q->first_pkt = pkt1->next;

if (!q->first_pkt)

q->last_pkt = NULL;

q->nb_packets--;

q->size -= pkt1->pkt.size;

*pkt = pkt1->pkt;

av_free(pkt1);

ret = 1;

break;

} else if (!block) {

ret = 0;

break;

} else {

SDL_CondWait(q->cond, q->mutex);

}

}

SDL_UnlockMutex(q->mutex);

return ret;

}

这里我们必须得注意SDL为音频处理创建了一个单独的线程,线程中通过调用回调函数完成从包中解码出音频帧

然后再调用解码函数将音频帧解码出来!

void audio_callback(void *userdata, Uint8 *stream, int len) {

AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;

int len1, audio_size;

static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];

static unsigned int audio_buf_size = 0;

static unsigned int audio_buf_index = 0;

while(len > 0) {

if(audio_buf_index >= audio_buf_size) {
audio_size = audio_decode_frame(aCodecCtx, audio_buf,

sizeof(audio_buf));

if(audio_size < 0) {

audio_buf_size = 1024;

memset(audio_buf, 0, audio_buf_size);

} else {

audio_buf_size = audio_size;

}

audio_buf_index = 0;

}

len1 = audio_buf_size - audio_buf_index;

if(len1 > len)

len1 = len;

memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);

len -= len1;

stream += len1;

audio_buf_index += len1;

}

}

解码函数

//decode audio frame

int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf,

int buf_size) {

static AVPacket pkt;

static uint8_t *audio_pkt_data = NULL;

static int audio_pkt_size = 0;

int len1, data_size;

for(;;) {

while(audio_pkt_size > 0) {

data_size = buf_size;

len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size,

audio_pkt_data, audio_pkt_size);

if(len1 < 0) {

audio_pkt_size = 0;

break;

}

audio_pkt_data += len1;

audio_pkt_size -= len1;

if(data_size <= 0) {

continue;

}

return data_size;

}

if(pkt.data)

av_free_packet(&pkt);

if(quit) {

return -1;

}

if(packet_queue_get(&audioq, &pkt, 1) < 0) {

return -1;

}

audio_pkt_data = pkt.data;

audio_pkt_size = pkt.size;

}

}

//主函数

int main()

{

AVFormatContext *pFormatCtx ;

AVCodecContext *pCodecCtx,*aCodecCtx ;

AVCodec *pCodec,*aCodec ;

AVStream *st;

AVFrame *pFrame ;

AVPacket packet ;

struct SwsContext *img_convert_ctx=NULL;

SDL_Event       event;

uint8_t *buffer ;

SDL_Rect rect ;

char *filename="1.asf" ;

int ret,i,videoStream,audioStream,numBytes,frameFinished ;

//init sdl library with video and audio

if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))

{

fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());

return -1 ;

}

//init the format and codec library

av_register_all() ;

ret=av_open_input_file(&pFormatCtx,filename,NULL,0,NULL) ;

if(ret<0)

{

printf("Error1:open input file failed!/n") ;

return -1 ;

}

//retrive stream information

ret=av_find_stream_info(pFormatCtx) ;

if(ret<0)

{

printf("Error2:find stream information failed!/n") ;

return -1 ;

}

//dump information about file onto standard error

dump_format(pFormatCtx,0,filename,0) ;

videoStream=-1 ;

audioStream=-1 ;

for(i=0; i < pFormatCtx->nb_streams; i++)

{

if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO

&&videoStream < 0)

{

videoStream=i;

}

if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO &&

audioStream < 0)

{

audioStream=i;

}

}

//check whether find video stream and audio stream

if(videoStream==-1)

{

printf("Error3:can't find video stream!/n") ;

return -1 ;

}

if(audioStream==-1)

{

printf("Error4:can't find audio stream!/n") ;

return -1 ;

}

//get video codec context

st=pFormatCtx->streams[videoStream] ;

pCodecCtx=st->codec;

//get audio codec contex

aCodecCtx=pFormatCtx->streams[audioStream] ->codec;

//find video codec

pCodec=avcodec_find_decoder(pCodecCtx->codec_id) ;

if(pCodec==NULL)

{

printf("Error5:can't find video decoder!/n") ;

return -1 ;

}

//open video decoder

ret=avcodec_open(pCodecCtx,pCodec) ;

if(ret<0)

{

printf("open video decoder failed!/n") ;

return -1 ;

}

// Allocate video frame

pFrame=avcodec_alloc_frame();

//init  audio codec context

if(init_sdl_audio(aCodecCtx)<0)

{

printf("Error6:init sdl audio failed!/n") ;

return -1 ;

}

//find audio codec

aCodec=avcodec_find_decoder(aCodecCtx->codec_id) ;

if(aCodec==NULL)

{

printf("Error7:can't find audio decoder!/n") ;

return -1 ;

}

//open audio decoder

ret=avcodec_open(aCodecCtx, aCodec);

if(ret<0)

{

printf("Error8:open audio decoder failed!/n") ;

return -1 ;

}

//init audio packet queue

packet_queue_init(&audioq);

SDL_PauseAudio(0);

//init overalay

if(init_sdl(pCodecCtx->width,pCodecCtx->height)<0)

{

printf("Error9:init sdl library failed!/n") ;

return -1 ;

}

//不知道为什么

url_set_interrupt_cb(decode_interrupt_cb);

//decode video and audio frame

while(av_read_frame(pFormatCtx,&packet)>=0)

{

if(packet.stream_index==videoStream)

{

//decode a frame

avcodec_decode_video(pCodecCtx,pFrame,&frameFinished,packet.data,packet.size) ;

//finish or not ?

if(frameFinished)

{

sdl_display((AVPicture *)pFrame,bmp,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height) ;

}

}else if(packet.stream_index==audioStream)

{

packet_queue_put(&audioq, &packet);

}else

{

av_free_packet(&packet) ;

}

SDL_PollEvent(&event);

switch(event.type)

{

case SDL_QUIT:

quit = 1;

SDL_Quit();

exit(0);

break;

default:

break;

}

}

// Free the YUV frame

av_free(pFrame);

// Close the codec

avcodec_close(pCodecCtx);

// Close the video file

av_close_input_file(pFormatCtx);

return 0;

}

但是这个程序没有解决音视频同步等问题,视频数据显示很快!

ffmpeg和SDL学习笔记相关推荐

  1. 雷神FFMpeg源码学习笔记

    雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...

  2. 【SDL】SDL学习笔记二 定时器

    定时器相关API(SDL_timer.h):   1. Uint32 SDL_GetTicks(): 获取从SDL库初始化(定时器模块初始化)开始到当前的运行时间(ms);   2. Uint64 S ...

  3. 【SDL】SDL学习笔记一 SDL的子系统的初始化和退出

    使用SDL库之前需要装载各个子系统,就像要放电影,必须给播放器提供影片一样,程序退出的时候,应该先退出子系统,释放资源. 1. 初始化SDL函数 该函数必须是在程序开始的地方调用 int SDL_In ...

  4. ffmpeg源码学习笔记三

    9. 关于如何parse mkv 前面为了不把战线拉太长,把如何parse mkv container 内容直接跳过了 接下来还是从read_header 开始讲解 static int matros ...

  5. ffmpeg源码学习笔记五

    14.read_frame_internal static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {...//初始化pa ...

  6. FFmpeg基础到工程-多路H265监控录放开发学习笔记

    多路H265监控录放开发学习笔记 课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等.包括:音视频.流媒体.直播.Android.视频监控28181.等. 具体 ...

  7. NDK学习笔记:FFmpeg解压MP34提取音频PCM(swrContext、swr_alloc_set_opts)

    NDK学习笔记:FFmpeg解压MP34提取音频PCM 承接 FFmpeg解压MP4提取视频YUV ,这次我们需要提取的是音频原始数据PCM.代码流程大同小异,主要区别就是AVFrame->PC ...

  8. NDK学习笔记:FFmpeg音视频同步3(你追我赶,升级ffmpeg/libyuv支持neon)

    NDK学习笔记:FFmpeg音视频同步3 本篇内容说多不多,但如果要说得明明白白的,可能就有点难度了.所以我决定把我的调试过程日志都呈现出来,方便大家理解.继上一篇文末,我们学习到了什么是DTS/PT ...

  9. ffmpeg学习笔记

           对于每一个刚開始学习的人,刚開始接触ffmpeg时,想必会有三个问题最为关心,即ffmpeg是什么?能干什么?怎么開始学习?本人前段时间開始接触ffmpeg,在刚開始学习过程中.这三个问 ...

最新文章

  1. GET POST 区别详解
  2. “2016智能终端峰会新闻发布会暨移动信息化可信选型认证结果发布会” —— 在京成功召开...
  3. cout的输出格式初探
  4. 程序员食品营养(1)-面包基础
  5. RStudio(You‘re using a non-UTF8 locale, therefore only ASCII characters will work)
  6. 机器人抓取方式,值得研究。
  7. 【UIKit】UITableView 3
  8. ASP.NET 自定义项目模板
  9. [译] 想帮助用户做决定?你的APP可以这样设计!
  10. ​再现Bug?iPhone13 拍照翻车自带炫屏 网友:没想到我也“中奖”了
  11. Python运行效率低的原因
  12. “本是青灯不归客,却因浊酒留风尘,星光不问赶路人,岁月不负有心人”,你是怎么理解的?
  13. 用the_excerpt处理中文文章字数限制的方法
  14. 最接近win7的Linux系统,Win7的优势所在 - Ubuntu PK Win7旗舰版到底还差多少火候_Linux新闻_Linux公社-Linux系统门户网站...
  15. win7系统安装信息服务器不可用怎么办,win7系统rpc服务器不可用怎么办
  16. Linux的任督二脉:进程调度和内存管理
  17. 都严肃点!史上最早的“喜当爹”其实是一项国家计划
  18. mysql 恢复到新库_mysql恢复到一个新的mysql主和从数据库教程
  19. 清华王宏计算机编程,培养出300多个信息学奥赛冠军后,听他谈如何造就精英!...
  20. IE 播放wav文件

热门文章

  1. 数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释)
  2. rsyslog syslog详解
  3. 【手算】行列式树形展开
  4. Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集
  5. java redis 生成唯一id_Redis在集群环境中生成唯一ID
  6. CCIE-LAB-第一篇-教学导入环境
  7. 【HDU - 5706】GirlCat(bfs)
  8. 【CodeForces-1041C】Coffee Break(贪心,STL,set二分维护,题意难,有坑,SJ题,构造)(知识点总结)
  9. 图解算法学习笔记(五):散列表
  10. 小程序中input标签没有反应_鸢尾花预测:如何创建机器学习Web应用程序?