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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

SDL音视频渲染

發布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SDL音视频渲染 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SDL音視頻渲染


目錄

  • SDL簡介
  • SDL窗口顯示
  • SDL Event事件
  • SDL Thread
  • SDL PCM播放
  • SDL YUV播放

  • 1. SDL基本介紹

    1. SDL簡介

  • SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平臺多媒體開發庫,使用C語言寫成。
  • SDL提供了數種控制圖像、聲音、輸出入的函數,讓開發者只要用相同或是相似的代碼就可以開發出跨多個平臺(Linux、 Windows、 Mac OS X等)的應用軟件。
  • 目前SDL多用于開發游戲、模擬器、 媒體播放器等多媒體應用領域。
  • 學習SDL主要用來輔助學習Ffmpeg。
  • 官網:https://www.libsdl.org/
  • 文檔:http://wiki.libsdl.org/Introduction
  • 2. SDL子系統

  • SDL將功能分成下列數個子系統(subsystem):
  • SDL_INIT_TIMER:定時器
  • SDL_INIT_AUDIO:音頻
  • SDL_INIT_VIDEO:視頻
  • SDL_INIT_JOYSTICK:搖桿
  • SDL_INIT_HAPTIC:觸摸屏
  • SDL_INIT_GAMECONTROLLER:游戲控制器
  • SDL_INIT_EVENTS:事件
  • SDL_INIT_EVERYTHING:包含上述所有選項

  • 2. SDL窗口顯示

    1. SDL視頻顯示函數簡介

  • SDL_Init():初始化SDL系統
  • SDL_CreateWindow():創建窗口SDL_Window
  • SDL_CreateRenderer():創建渲染器SDL_Renderer
  • SDL_CreateTexture():創建紋理SDL_Texture
  • SDL_UpdateTexture():設置紋理的數據
  • SDL_RenderCopy():將紋理的數據拷貝給渲染器
  • SDL_RenderPresent():顯示
  • SDL_Delay():工具函數,用于延時
  • SDL_Quit():退出SDL系統
  • 2. SDL數據結構簡介

  • SDL_Window 代表了一個“窗口”
  • SDL_Renderer 代表了一個“渲染器”
  • SDL_Texture 代表了一個“紋理”
  • SDL_Rect 一個簡單的矩形結構
  • 一個窗口可以有多個render,render可用來渲染紋理,也可以用來顯示紋理。
  • 存儲RGB和存儲紋理的區別:比如一個從左到右由紅色漸變到藍色的矩形,用存儲RGB的話就需要把矩形中每個點的具體顏色值存儲下來;而紋理只是一些描述信息,比如記錄了矩形的大小、起始顏色、終止顏色等信息,顯卡可以通過這些信息推算出矩形塊的詳細信息。所以相對于存儲RGB而已,存儲紋理占用的內存要少的多。
  • 3. 代碼示例

  • pro文件設置動態庫
  • TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qtSOURCES += \main.cINCLUDEPATH += "/usr/local/Cellar/sdl2/2.0.12_1/include"# 默認是動態庫的鏈接 LIBS += -L/usr/local/Cellar/sdl2/2.0.12_1/lib -lSDL2
  • 代碼
  • #include <stdio.h> #include <SDL2/SDL.h>int main() {printf("Hello World!\n");int run = 1;SDL_Window *window = NULL;SDL_Renderer *renderer = NULL;SDL_Texture *texture = NULL;SDL_Rect rect; //長方形,原點在左上角rect.w = 50;rect.h = 50;SDL_Init(SDL_INIT_VIDEO);window = SDL_CreateWindow("2 WINDOW", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); //創建窗口if (!window) {return -1;}renderer = SDL_CreateRenderer(window, -1, 0); //基于窗口創建渲染器if (!renderer) {return -1;}texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, 640, 480); // 創建紋理if (!texture) {return -1;}int show_count = 0;while (run) {rect.x = rand() % 600;rect.y = rand() % 400;SDL_SetRenderTarget(renderer, texture); //設置渲染目標為紋理SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); //紋理背景為黑色SDL_RenderClear(renderer); //清屏SDL_RenderDrawRect(renderer, &rect); //繪制一個長方形SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255); //長方形為白色SDL_RenderFillRect(renderer, &rect);SDL_SetRenderTarget(renderer, NULL); //恢復默認,渲染目標為窗口SDL_RenderCopy(renderer, texture, NULL, NULL); //拷貝紋理到CPUSDL_RenderPresent(renderer); //輸出到目標窗口上SDL_Delay(300);if (show_count++ > 30) {run = 0; //不跑了}}SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();return 0; }

    3. SDL Event事件

    1. 函數

  • SDL_WaitEvent():等待一個事件
  • SDL_PushEvent():發送一個事件
  • SDL_PumpEvents():將硬件設備產生的事件放入事件隊列,用于讀取事件,在調用該函數之前,必須調用SDL_PumpEvents搜集鍵盤等事件
  • SDL_PeepEvents():從事件隊列提取一個事件
  • 2. 數據結構

  • SDL_Event:代表一個事件
  • 3. 代碼示例

  • 代碼
  • #include <stdio.h> #include <SDL2/SDL.h>#define FF_QUIT_EVENT (SDL_USEREVENT + 2) // 用戶自定義事件int main() {SDL_Window *window = NULL; // Declare a pointerSDL_Renderer *renderer = NULL;SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2// Create an application window with the following settings:window = SDL_CreateWindow("An SDL2 window", // window titleSDL_WINDOWPOS_UNDEFINED, // initial x positionSDL_WINDOWPOS_UNDEFINED, // initial y position640, // width, in pixels480, // height, in pixelsSDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below);// Check that the window was successfully createdif (window == NULL) {// In the case that the window could not be made...printf("Could not create window: %s\n", SDL_GetError());return 1;}/* We must call SDL_CreateRenderer in order for draw calls to affect this window. */renderer = SDL_CreateRenderer(window, -1, 0);/* Select the color for drawing. It is set to red here. */SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);/* Clear the entire screen to our selected color. */SDL_RenderClear(renderer);/* Up until now everything was drawn behind the scenes.This will show the new, red contents of the window. */SDL_RenderPresent(renderer);SDL_Event event;int b_exit = 0;for (;;) {SDL_WaitEvent(&event);switch (event.type) {case SDL_KEYDOWN: /* 鍵盤事件 */switch (event.key.keysym.sym) {case SDLK_a:printf("key down a\n");break;case SDLK_s:printf("key down s\n");break;case SDLK_d:printf("key down d\n");break;case SDLK_q:printf("key down q and push quit event\n");SDL_Event event_q;event_q.type = FF_QUIT_EVENT;SDL_PushEvent(&event_q);break;default:printf("key down 0x%x\n", event.key.keysym.sym);break;}break;case SDL_MOUSEBUTTONDOWN: /* 鼠標按下事件 */if (event.button.button == SDL_BUTTON_LEFT) {printf("mouse down left\n");} else if (event.button.button == SDL_BUTTON_RIGHT) {printf("mouse down right\n");} else {printf("mouse down %d\n", event.button.button);}break;case SDL_MOUSEMOTION: /* 鼠標移動事件 */printf("mouse movie (%d,%d)\n", event.button.x, event.button.y);break;case FF_QUIT_EVENT:printf("receive quit event\n");b_exit = 1;break;}if (b_exit)break;}//destory rendererif (renderer)SDL_DestroyRenderer(renderer);// Close and destroy the windowif (window)SDL_DestroyWindow(window);// Clean upSDL_Quit();return 0; }
  • 效果

  • 4. SDL Thread

    1. 函數

  • SDL線程創建: SDL_CreateThread
  • SDL線程等待: SDL_WaitThead
  • SDL互斥鎖: SDL_CreateMutex/SDL_DestroyMutex
  • SDL鎖定互斥: SDL_LockMutex/SDL_UnlockMutex
  • SDL條件變量(信號量): SDL_CreateCond/SDL_DestoryCond
  • SDL條件變量(信號量)等待/通知: SDL_CondWait/SDL_CondSingal
  • 2. 代碼示例

  • 代碼
  • #include <stdio.h> #include <SDL2/SDL.h> #include <unistd.h>SDL_mutex *s_lock = NULL; SDL_cond *s_cond = NULL;int thread_work(void *arg) {SDL_LockMutex(s_lock);printf(" <============thread_work sleep\n"); // sleep(10); // 用來測試獲取鎖sleep(10);printf(" <============thread_work wait\n");// 釋放s_lock資源,并等待signal。之所以釋放s_lock是讓別的線程能夠獲取到s_lockSDL_CondWait(s_cond, s_lock); //另一個線程(1)發送signal和(2)釋放lock后,這個函數退出printf(" <===========thread_work receive signal, continue to do ~_~!!!\n");printf(" <===========thread_work end\n");SDL_UnlockMutex(s_lock);return 0; }#undef main int main() {s_lock = SDL_CreateMutex();s_cond = SDL_CreateCond();SDL_Thread * t = SDL_CreateThread(thread_work,"thread_work",NULL);if(!t){printf(" %s",SDL_GetError);return -1;}for(int i = 0;i< 2;i++){sleep(2);printf("main execute =====>\n");}printf("main SDL_LockMutex(s_lock) before ====================>\n");SDL_LockMutex(s_lock); // 獲取鎖,但是子線程還拿著鎖printf("main ready send signal====================>\n");printf("main SDL_CondSignal(s_cond) before ====================>\n");SDL_CondSignal(s_cond); // 發送信號,喚醒等待的線程printf("main SDL_CondSignal(s_cond) after ====================>\n"); // sleep(10);SDL_UnlockMutex(s_lock);// 釋放鎖,讓其他線程可以拿到鎖printf("main SDL_UnlockMutex(s_lock) after ====================>\n");SDL_WaitThread(t, NULL);SDL_DestroyMutex(s_lock);SDL_DestroyCond(s_cond);return 0; }

    注:mac系統下sleep需要引用頭文件:#include <unistd.h>,Windows下不需要。

  • 效果

  • 5. SDL PCM播放

    1. 函數

  • 打開音頻設備
  • int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired,SDL_AudioSpec * obtained); // desired:期望的參數。 // obtained:實際音頻設備的參數,一般情況下設置為NULL即可。
  • SDL_AudioSpec
  • typedef struct SDL_AudioSpec {int freq; // 音頻采樣率SDL_AudioFormat format; // 音頻數據格式Uint8 channels; // 聲道數: 1 單聲道, 2 立體聲Uint8 silence; // 設置靜音的值, 因為聲音采樣是有符號的, 所以0當然就是這個值Uint16 samples; // 音頻緩沖區中的采樣個數,要求必須是2的n次Uint16 padding; // 考慮到兼容性的一個參數Uint32 size; // 音頻緩沖區的大小,以字節為單位SDL_AudioCallback callback; // 填充音頻緩沖區的回調函數void *userdata; // 用戶自定義的數據 } SDL_AudioSpec;
  • SDL_AudioCallback
  • // userdata: SDL_AudioSpec結構中的用戶自定義數據,一般情況下可以不用。 // stream:該指針指向需要填充的音頻緩沖區。 // len:音頻緩沖區的大小(以字節為單位) 1024*2*2。 void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);

    2. 代碼示例

  • 代碼
  • /*** SDL2播放PCM*** 本程序使用SDL2播放PCM音頻采樣數據。SDL實際上是對底層繪圖* API(Direct3D,OpenGL)的封裝,使用起來明顯簡單于直接調用底層* API。* 測試的PCM數據采用采樣率44.1k, 采用精度S16SYS, 通道數2** 函數調用步驟如下:** [初始化]* SDL_Init(): 初始化SDL。* SDL_OpenAudio(): 根據參數(存儲于SDL_AudioSpec)打開音頻設備。* SDL_PauseAudio(): 播放音頻數據。** [循環播放數據]* SDL_Delay(): 延時等待播放完成。**/#include <stdio.h> #include <SDL2/SDL.h>// 每次讀取2幀數據, 以1024個采樣點一幀 2通道 16bit采樣點為例 #define PCM_BUFFER_SIZE (1024*2*2*2)// 音頻PCM數據緩存 static Uint8 *s_audio_buf = NULL; // 目前讀取的位置 static Uint8 *s_audio_pos = NULL; // 緩存結束位置 static Uint8 *s_audio_end = NULL;//音頻設備回調函數 void fill_audio_pcm(void *udata, Uint8 *stream, int len) {SDL_memset(stream, 0, len);if(s_audio_pos >= s_audio_end) // 數據讀取完畢{return;}// 數據夠了就讀預設長度,數據不夠就只讀部分(不夠的時候剩多少就讀取多少)int remain_buffer_len = s_audio_end - s_audio_pos;len = (len < remain_buffer_len) ? len : remain_buffer_len;// 拷貝數據到stream并調整音量SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);printf("len = %d\n", len);s_audio_pos += len; // 移動緩存指針 }// 提取PCM文件 // ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm // 測試PCM文件 // ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm #undef main int main(int argc, char *argv[]) {int ret = -1;FILE *audio_fd = NULL;SDL_AudioSpec spec;const char *path = "44100_16bit_2ch.pcm";// 每次緩存的長度size_t read_buffer_len = 0;//SDL initializeif(SDL_Init(SDL_INIT_AUDIO)) // 支持AUDIO{fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());return ret;}//打開PCM文件audio_fd = fopen(path, "rb");if(!audio_fd){fprintf(stderr, "Failed to open pcm file!\n");goto _FAIL;}s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);// 音頻參數設置SDL_AudioSpecspec.freq = 44100; // 采樣頻率spec.format = AUDIO_S16SYS; // 采樣點格式spec.channels = 2; // 2通道spec.silence = 0;spec.samples = 1024; // 23.2ms -> 46.4ms 每次讀取的采樣數量,多久產生一次回調和 samplesspec.callback = fill_audio_pcm; // 回調函數spec.userdata = NULL;//打開音頻設備if(SDL_OpenAudio(&spec, NULL)){fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());goto _FAIL;}//play audioSDL_PauseAudio(0);int data_count = 0;while(1){// 從文件讀取PCM數據read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);if(read_buffer_len == 0){break;}data_count += read_buffer_len; // 統計讀取的數據總字節數printf("now playing %10d bytes data.\n",data_count);s_audio_end = s_audio_buf + read_buffer_len; // 更新buffer的結束位置s_audio_pos = s_audio_buf; // 更新buffer的起始位置//the main thread wait for a momentwhile(s_audio_pos < s_audio_end){SDL_Delay(10); // 等待PCM數據消耗}}printf("play PCM finish\n");// 關閉音頻設備SDL_CloseAudio();_FAIL://release some resourcesif(s_audio_buf)free(s_audio_buf);if(audio_fd)fclose(audio_fd);//quit SDLSDL_Quit();return 0; }
  • 效果,聽到音頻聲音

  • 6. SDL YUV播放

    1. YUV顯示:SDL視頻顯示流程

    2. 代碼

  • 代碼
  • #include <stdio.h> #include <SDL2/SDL.h> #include <string.h>//自定義消息類型 #define REFRESH_EVENT (SDL_USEREVENT + 1) // 請求畫面刷新事件 #define QUIT_EVENT (SDL_USEREVENT + 2) // 退出事件//定義分辨率 // YUV像素分辨率 #define YUV_WIDTH 320 #define YUV_HEIGHT 240 //定義YUV格式 #define YUV_FORMAT SDL_PIXELFORMAT_IYUVint s_thread_exit = 0; // 退出標志 = 1則退出int refresh_video_timer(void *data) {while (!s_thread_exit) {SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);SDL_Delay(40);}s_thread_exit = 0;//push quit eventSDL_Event event;event.type = QUIT_EVENT;SDL_PushEvent(&event); }int main() {if (SDL_Init(SDL_INIT_VIDEO)) {fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());return -1;}//SDLSDL_Event event; // 事件SDL_Rect rect; // 矩形SDL_Window *window = NULL; // 窗口SDL_Renderer *renderer = NULL; // 渲染SDL_Texture *texture = NULL; // 紋理SDL_Thread *timer_thread = NULL; // 請求刷新線程u_int32_t pixformat = YUV_FORMAT; // YUV420P,即是SDL_PIXELFORMAT_IYUV// 分辨率// 1. YUV的分辨率int video_width = YUV_WIDTH;int video_hight = YUV_HEIGHT;// 2.顯示窗口的分辨率int win_width = YUV_WIDTH;int win_height = YUV_WIDTH;// YUV文件句柄FILE *video_fd = NULL;const char *yuv_path = "yuv420p_320x240.yuv";size_t video_buff_len = 0;uint8_t *video_buf = NULL; //讀取數據后放buff里面// 我們測試的文件是YUV420P格式uint32_t y_frame_len = video_width * video_hight;uint32_t u_frame_len = video_width * video_hight / 4;uint32_t v_frame_len = video_width * video_hight / 4;uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;window = SDL_CreateWindow("simplest YUV Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, video_width,video_hight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {fprintf(stderr, "SDL: could not create window, err:%s\n", SDL_GetError());goto _FAIL;}// 基于窗口創建渲染器renderer = SDL_CreateRenderer(window, -1, 0);// 基于渲染器創建紋理texture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, video_width, video_hight);//分配空間video_buf = (uint8_t *) malloc(yuv_frame_len);if (!video_buf) {fprintf(stderr, "Failed to alloce yuv frame space!\n");goto _FAIL;}video_fd = fopen(yuv_path, "rb");if (!video_fd) {fprintf(stderr, "Failed to open yuv file\n");goto _FAIL;}//創建請求刷新線程timer_thread = SDL_CreateThread(refresh_video_timer, NULL, NULL);while (1) {// 收取SDL系統里面的事件SDL_WaitEvent(&event);if (event.type == REFRESH_EVENT) {video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);if (video_buff_len <= 0) {fprintf(stderr, "Failed to read data from yuv file!\n");goto _FAIL;}// 設置紋理的數據 video_width = 320, planeSDL_UpdateTexture(texture, NULL, video_buf, video_width);//顯示區域,可以用個修改w和h進行縮放rect.x = 0;rect.y = 0;float w_ratio = win_width * 1.0 / video_width;float h_ratio = win_height * 1.0 / video_hight;// 320x240 怎么保持原視頻的寬高比例rect.w = video_width * w_ratio;rect.h = video_hight * h_ratio;SDL_RenderClear(renderer);// 將紋理的數據拷貝給渲染器SDL_RenderCopy(renderer, texture, NULL, &rect);// 顯示SDL_RenderPresent(renderer);} else if (event.type == SDL_WINDOWEVENT) {//If ResizeSDL_GetWindowSize(window, &win_width, &win_height);printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n", win_width,win_height);} else if (event.type == SDL_QUIT) {s_thread_exit = 1;} else if (event.type == QUIT_EVENT) {break;}}_FAIL:s_thread_exit = 1; // 保證線程能夠退出// 釋放資源if (timer_thread)SDL_WaitThread(timer_thread, NULL); // 等待線程退出if (video_buf)free(video_buf);if (video_fd)fclose(video_fd);if (texture)SDL_DestroyTexture(texture);if (renderer)SDL_DestroyRenderer(renderer);if (window)SDL_DestroyWindow(window);SDL_Quit();return 0; }
  • 效果
  • 總結

    以上是生活随笔為你收集整理的SDL音视频渲染的全部內容,希望文章能夠幫你解決所遇到的問題。

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