当前位置:   article > 正文

音视频开发:SDL播放器实战_sdl2.0

sdl2.0

1 SDL简介

SDL(Simple DirectMedia Layer)是一个跨平台开发库(Windows、macOS、Linux、iOS 和 Android等),旨在通过 OpenGL 和 Direct3D 提供对音频、键盘、鼠标、游戏杆和图形硬件的低级访问,开发者只需要编写一套代码既可以支持跨平台的运行。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。本文主要用到的是SDL中音视频控制和基础事件部分。下图可以看出SDL主要是对于不同平台的硬件控制库进行支持,使得上层应用对于底层控制无感,降低了开发的门槛。

粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

2 SDL音视频播放基础API

2.1 SDL基础组成

SDL将功能分成下列数个子系统,不同的子系统包含特定功能本文关注的几个模块包括音频、视频和事件等子系统:

  1. - SDL_INIT_TIMER:定时器
  2. - SDL_INIT_AUDIO:音频
  3. - SDL_INIT_VIDEO:视频
  4. - SDL_INIT_JOYSTICK:摇杆
  5. - SDL_INIT_HAPTIC:触摸屏
  6. - SDL_INIT_GAMECONTROLLER:游戏控制器
  7. - SDL_INIT_EVENTS:事件
  8. - SDL_INIT_EVERYTHING:包含上述所有选项

2.2 SDL视频模块

2.2.1 视频API

视频API三大控件窗口(window)、渲染器(renderer)、纹理(texture)。窗口作为所有界面的载体负责基础的窗口事件的捕获和响应;渲染器则是将图像数据渲染成图像;纹理主要是用来管理图像将图像数据加入到纹理,方便后续加载到渲染器。

  1. - SDL_Init():初始化SDL系统
  2. - SDL_CreateWindow():创建窗口SDL_Window
  3. - SDL_CreateRenderer():创建渲染器SDL_Renderer
  4. - SDL_CreateTexture():创建纹理SDL_Texture
  5. - SDL_UpdateTexture():设置纹理的数据
  6. - SDL_RenderCopy():将纹理的数据拷贝给渲染器
  7. - SDL_RenderPresent():显示
  8. - SDL_Delay():工具函数,用于延时
  9. - SDL_Quit():退出SDL系统

2.2.2 视频结构体

视频基础:窗口、渲染器、纹理和矩形等对象。

  1. - SDL_Window // 窗口
  2. - SDL_Renderer // 渲染
  3. - SDL_Texture // 纹理
  4. - SDL_Rect // 矩形

2.2.2 音频API

音频API基础流程比较简单,首先是打开音频设备SDL_OpenAudio,并开启音频播放SDL_PauseAudio,利用音频回调函数SDL_AudioCallback将数据写入音频播放缓冲区播放SDL_MixAudio,最后关闭音频播放SDL_CloseAudio。

  1. - SDL_Init():初始化SDL系统
  2. - SDL_OpenAudio():打开音频设备
  3. - SDL_AudioCallback():音频播放回调函数
  4. - SDL_PauseAudio():开启或者暂停播放
  5. - SDL_MixAudio():将音频数据写入播放缓冲区
  6. - SDL_CloseAudio():关闭音频设备
  7. - SDL_Quit():退出SDL系统

3 SDL开发环境构建

3.1 SDL源码下载与编译

本文介绍的是Linux下的环境搭建,主要通过源码进行编译获取SDL动态库。下载路径:https://www.libsdl.org/download-2.0.php

  1. mkdir SDL
  2. cd SDL
  3. wget https://www.libsdl.org/release/SDL2-2.0.22.tar.gz
  4. tar -xvf SDL2-2.0.22.tar.gz
  5. cd SDL2-2.0.22
  6. mkdir build
  7. cmake ..
  8. make -j4

整个编译过程很顺利,最终会得到编译文件如下图,这里会用到其中libSDL2-2.0.so动态库文件和根目录下的include文件夹。

3.2 工程构建

得到SDL2动态库后,接下来就是进行工程构建,创建一个SDL_project工程目录,包含该标准bin、build、include、lib、src目录文件。并且将依赖库拷贝到工程目录下。基本框架就构建好了。

  1. mkdir SDL_project
  2. cd SDL_project
  3. mkdir bin build include lib src
  4. cp ../SDL2-2.0.22/build/libSDL2-2.0.so* ./lib/
  5. cp ../SDL2-2.0.22/include/ ./src/ -rf
  6. touch CMakeLists.txt
  7. touch ./src/CMakeLists.txt

4 SDL简易视频播放器

这里截取部分源码进行描述;

第一初始化基础结构;

创建窗口:SDL_CreateWindow
创建渲染器:SDL_CreateRenderer
创建纹理:SDL_CreateTexture
创建刷新线程:SDL_CreateThread

第二是利用SDL_WaitEvent,响应不同SDL事件,包括视频显示流程:

读取yuv文件

将视频缓冲数据更新到纹理:SDL_UpdateTexture;

清理渲染器:SDL_RenderClear

纹理拷贝到渲染器:SDL_RenderCopy

显示视频画面:SDL_RenderPresent

  1. //SDL_CreateWindow
  2. window = SDL_CreateWindow("Simple YUV Player",
  3. SDL_WINDOWPOS_UNDEFINED,
  4. SDL_WINDOWPOS_UNDEFINED,
  5. video_width, video_height,
  6. SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
  7. if(!window)
  8. {
  9. fprintf(stderr, "SDL: could not create window, err:%s\n",SDL_GetError());
  10. goto _FAIL;
  11. }
  12. // SDL_CreateRenderer
  13. renderer = SDL_CreateRenderer(window, -1, 0);
  14. // SDL_CreateTexture
  15. texture = SDL_CreateTexture(renderer,
  16. pixformat,
  17. SDL_TEXTUREACCESS_STREAMING,
  18. video_width,
  19. video_height);
  20. video_buf = (uint8_t*)malloc(yuv_frame_len);
  21. if(!video_buf)
  22. {
  23. fprintf(stderr, "Failed to alloce yuv frame space!\n");
  24. goto _FAIL;
  25. }
  26. // open yuv_path
  27. video_fd = fopen(yuv_path, "rb");
  28. if( !video_fd )
  29. {
  30. fprintf(stderr, "Failed to open yuv file:%s\n", yuv_path);
  31. goto _FAIL;
  32. }
  33. // SDL_CreateThread
  34. timer_thread = SDL_CreateThread(refresh_video_timer,
  35. NULL,
  36. NULL);
  37. while (1)
  38. {
  39. // SDL_WaitEvent
  40. SDL_WaitEvent(&event);
  41. if(event.type == SDL_REFRESH_EVENT) // update video frame
  42. {
  43. video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);
  44. if(video_buff_len <= 0)
  45. {
  46. fprintf(stderr, "Failed to read data from yuv file!\n");
  47. goto _FAIL;
  48. }
  49. // SDL_UpdateTexture
  50. SDL_UpdateTexture(texture, NULL, video_buf, video_width);
  51. // keep window scale
  52. rect.x = 0;
  53. rect.y = 0;
  54. float w_ratio = win_width * 1.0 /video_width;
  55. float h_ratio = win_height * 1.0 /video_height;
  56. rect.w = video_width * w_ratio;
  57. rect.h = video_height * h_ratio;
  58. // SDL_RenderClear
  59. SDL_RenderClear(renderer);
  60. // SDL_RenderCopy
  61. SDL_RenderCopy(renderer, texture, NULL, &rect);
  62. // SDL_RenderPresent
  63. SDL_RenderPresent(renderer);
  64. }
  65. else if(event.type == SDL_WINDOWEVENT)
  66. {
  67. //If Resize
  68. SDL_GetWindowSize(window, &win_width, &win_height);
  69. //printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n",win_width,
  70. // win_height );
  71. }
  72. else if(event.type == SDL_QUIT) //退出事件
  73. {
  74. g_thread_exit = 1;
  75. }
  76. else if(event.type == SDL_QUIT_EVENT)
  77. {
  78. break;
  79. }
  80. }

yuv播放器测试,播放一个yuv文件,可以正常播放画面,调节画面大小。

5 SDL简易音频播放器

SDL 音频播放相对视频播放器更为简单:

音频设备初始化并且读取音频数据:

打开音频设备:SDL_OpenAudio
开启音频设备播放
读取pcm文件,写入音频缓冲区

  1. if(SDL_Init(SDL_INIT_AUDIO))
  2. {
  3. fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
  4. return ret;
  5. }
  6. //open pcmfile
  7. audio_fd = fopen(path, "rb");
  8. if(!audio_fd)
  9. {
  10. fprintf(stderr, "Failed to open pcm file!\n");
  11. goto _FAIL;
  12. }
  13. g_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);
  14. // SDL_AudioSpec
  15. spec.freq = 44100;
  16. spec.format = AUDIO_S16SYS;
  17. spec.channels = 2;
  18. spec.silence = 0;
  19. spec.samples = 1024;
  20. spec.callback = fill_audio_pcm;
  21. spec.userdata = NULL;
  22. if(SDL_OpenAudio(&spec, NULL))
  23. {
  24. fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());
  25. goto _FAIL;
  26. }
  27. //play audio
  28. SDL_PauseAudio(0);
  29. int data_count = 0;
  30. while(1)
  31. {
  32. // read pcm
  33. read_buffer_len = fread(g_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
  34. if(read_buffer_len == 0)
  35. {
  36. break;
  37. }
  38. data_count += read_buffer_len;
  39. printf("now playing %10d bytes data.\n",data_count);
  40. g_audio_end = g_audio_buf + read_buffer_len;
  41. g_audio_pos = g_audio_buf;
  42. //the main thread wait for a moment
  43. while(g_audio_pos < g_audio_end)
  44. {
  45. SDL_Delay(10);
  46. }
  47. }
  48. printf("play PCM finish\n");
  49. // SDL_CloseAudio
  50. SDL_CloseAudio();

回调函数将音频数据写入播放缓冲区:

  1. void mix_audio_pcm(void *udata, Uint8 *stream, int len)
  2. {
  3. SDL_memset(stream, 0, len);
  4. if(g_audio_pos >= g_audio_end)
  5. {
  6. return;
  7. }
  8. int remain_buffer_len = g_audio_end - g_audio_pos;
  9. len = (len < remain_buffer_len) ? len : remain_buffer_len;
  10. SDL_MixAudio(stream, g_audio_pos, len, SDL_MIX_MAXVOLUME/8);
  11. printf("len = %d\n", len);
  12. g_audio_pos += len;
  13. }

粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1018902
推荐阅读
相关标签
  

闽ICP备14008679号