日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ffmpeg和SDL学习笔记

發布時間:2023/12/18 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ffmpeg和SDL学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

根據ffmpeg官方網站上的例子程序開始學習ffmpegSDL編程。

SDL是一個跨平臺的多媒體開發包。適用于游戲,模擬器,播放器等應用軟件開發。支持linuxwin32 等操作系統。

主要應用:

視頻

  • 設置8bpp或更高的任意色彩深度的視頻模式。如果某個模式硬件不支持,可以選擇轉化為另一模式。
  • 直接寫入線性的圖像幀緩沖(framebuffer)。
  • 用顏色鍵值(colorkey)或者alpha混合屬性創建surface
  • Surfaceblit能自動的轉化為目標格式。blit是優化過的,并能使用硬件加速。x86平臺上有針對MMX優化過的blit。
  • 硬件加速的blitfill(填充)操作,如果硬件支持的話。

事件

  • 提供以下事件:
    • 應用程序的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;

?

我們得注意:這里的sizepacket的大小,而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学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。