谷动谷力

标题: RT-Thread Smart基于 FFmpeg + SDL2 在 ART-Pi Smart 平台上视频播放. [打印本页]

作者: fannifu    时间: 2022-4-28 14:40
标题: RT-Thread Smart基于 FFmpeg + SDL2 在 ART-Pi Smart 平台上视频播放.
本帖最后由 fannifu 于 2022-4-28 14:43 编辑

RT-Thread Smart基于 FFmpeg + SDL2  在 ART-Pi Smart 平台上实现视频播放


基于文档《使用 VS Code 开发 GUI 应用》,使用 FFmpeg+SDL2 在 ART-Pi Smart 平台上实现视频播放功能;由于 ART-Pi Smart 没有音频模块,所以没有实现音频的解码播放。

简介
X264 是由 VideoLAN 开发的一个免费开源软件库和命令行实用程序,用于将视频流编码为 H.264 / MPEG-4 AVC 格式,根据GNU通用公共许可证的条款发布的。
FFmpeg 是一个编解码库,功能丰富,其自带 H.264 解码功能,但是要实现 H.264 编码需要集成 X264 将其作为编码器。

下载
  1. git clone https://code.videolan.org/videolan/x264.git
复制代码




源码目录:

交叉编译

[color=var(--theme-color, #42b983)]FFmpeg[color=var(--theme-color, #42b983)]简介
FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用 LGP L或 GPL 许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库 libavcodec,为了保证高可移植性和编解码质量,libavcodec 里很多 code 都是从头开发的。
FFmpeg 在 Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括 Windows、Mac OS X 等。这个项目最早由 Fabrice Bellard 发起,2004 年至 2015 年间由 Michael Niedermayer 主要负责维护。下面介绍如何将 FFmpeg 移植到 ART-Pi Smart 平台上,实现视频的解码功能。
[color=var(--theme-color, #42b983)]下载
打开 FFmpeg 官网,下载源码:
[color=var(--theme-color, #42b983)]交叉编译[color=var(--theme-color, #42b983)]视频播放 Demo[color=var(--theme-color, #42b983)]project 根路径
PROJECT_DIR := $(shell pwd)
[color=var(--theme-color, #42b983)]userapps 根路径
UROOT_DIR = $(PROJECT_DIR)/../..
[color=var(--theme-color, #42b983)]rt-thread 路径
RT_DIR = $(UROOT_DIR)/sdk/rt-thread INC_DIR =$(UROOT_DIR)/sdk/rt-thread/include LIB_DIR = ${UROOT_DIR}/sdk/rt-thread/lib
[color=var(--theme-color, #42b983)]sdl 路径
SDL_DIR = ${UROOT_DIR}/sdk/include/sdl
[color=var(--theme-color, #42b983)]ffmpeg
FFMPEG_DIR = ${UROOT_DIR}/sdk/include/ffmpeg
#x264 X264_DIR = ${UROOT_DIR}/sdk/include/x264
[color=var(--theme-color, #42b983)]配置编译参数
CFLAGS = -march=armv7-a -marm -msoft-float -D__RTTHREAD__ -Wall -O0 -g -gdwarf-2 -n --static
[color=var(--theme-color, #42b983)]加入头文件搜索路径
CFLAGS += -I. -I$(UROOT_DIR) -I$(PROJECT_DIR) -I$(RT_DIR)/components/dfs -I$(RT_DIR)/components/drivers -I$(RT_DIR)/components/finsh -I$(RT_DIR)/components/net
-I$(RT_DIR)/components/net/netdev -I$(RT_DIR)/components/net/arpa -I${INC_DIR} -I${INC_DIR}/libc -I${INC_DIR}/sys -I${SDL_DIR} -I${FFMPEG_DIR}
-I${FFMPEG_DIR}/libavcodec -I${FFMPEG_DIR}/libavdevice -I${FFMPEG_DIR}/libavfilter -I${FFMPEG_DIR}/libavformat
-I${FFMPEG_DIR}/libavutil -I${FFMPEG_DIR}/libpostproc -I${FFMPEG_DIR}/libswresample -I${FFMPEG_DIR}/libswscale -I${X264_DIR}
[color=var(--theme-color, #42b983)]加入链接文件
LDFLAGS = -march=armv7-a -marm -msoft-float -T ${UROOT_DIR}/linker_scripts/arm/cortex-a/link.lds
[color=var(--theme-color, #42b983)]加入库文件
LDFLAGS += -L$(LIB_DIR) -Wl,--whole-archive -Os -lrtthread -lSDL2 -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale -lx264 -Wl,--no-whole-archive -n --static -Wl,--start-group -lc -lgcc -lrtthread -lSDL2 -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lpostproc -lswresample -lswscale -lx264 -Wl,--end-group
default: $(CC) $(CFLAGS) -c main.c -o main.o $(CC) $(LDFLAGS) main.o -o hello.elf
clean: @rm *.o *.elf
.PHONY: default clean
4. 编译![Snipaste_2022-01-17_10-10-13.png](figures/10.png)5. 通过 SD Card 启动 elf 文件,将生成的 hello.elf 文件和视频文件放到 SD 卡中,插入到 ART-Pi Smart: ![Snipaste_2022-01-17_10-15-04.png](figures/11.png)
完整代码
  1. #include <stdio.h>
  2. #include <SDL.h>
  3. #include <libavcodec/avcodec.h>
  4. #include <libavformat/avformat.h>
  5. #include <libswscale/swscale.h>

  6. extern Uint32 rtt_screen_width;
  7. extern Uint32 rtt_screen_heigth;

  8. int main (int argc, char *argv[])
  9. {
  10. int ret = -1;
  11. AVFormatContext *pFormatCtx = NULL;
  12. int videoStream;
  13. AVCodecParameters *pCodecParameters = NULL;
  14. AVCodecContext *pCodecCtx = NULL;
  15. AVCodec *pCodec = NULL;
  16. AVFrame *pFrame = NULL;
  17. AVPacket packet;

  18. SDL_Rect rect;
  19. SDL_Window *win = NULL;
  20. SDL_Renderer *renderer = NULL;
  21. SDL_Texture *texture = NULL;

  22. if(( argc != 2 ))
  23. {
  24.      printf("error input arguments!\n");
  25.      return(1);
  26. }

  27. // 默认窗口大小
  28. int w_width  = rtt_screen_width;
  29. int w_height = rtt_screen_heigth;

  30. // use dummy video driver
  31. SDL_setenv("SDL_VIDEODRIVER","rtt",1);
  32. //Initialize SDL
  33. if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
  34. {
  35.      printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
  36.      return -1;
  37. }

  38. // 打开输入文件
  39. if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
  40. {
  41.      printf("Couldn't open video file!: %s\n", argv[1]);
  42.      goto __exit;
  43. }

  44. // 找到视频流
  45. videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
  46. if (videoStream == -1)
  47. {
  48.      printf("Din't find a video stream!\n");
  49.      goto __exit;// Didn't find a video stream
  50. }

  51. // 流参数
  52. pCodecParameters = pFormatCtx->streams[videoStream]->codecpar;

  53. // 获取解码器
  54. pCodec = avcodec_find_decoder(pCodecParameters->codec_id);
  55. if (pCodec == NULL)
  56. {
  57.      printf("Unsupported codec!\n");
  58.      goto __exit; // Codec not found
  59. }

  60. // 初始化一个编解码上下文
  61. pCodecCtx = avcodec_alloc_context3(pCodec);
  62. if (avcodec_parameters_to_context(pCodecCtx, pCodecParameters) != 0)
  63. {
  64.      printf("Couldn't copy codec context\n");
  65.      goto __exit;// Error copying codec context
  66. }

  67. // 打开解码器
  68. if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
  69. {
  70.      printf("Failed to open decoder!\n");
  71.      goto __exit; // Could not open codec
  72. }

  73. // Allocate video frame
  74. pFrame = av_frame_alloc();

  75. w_width = pCodecCtx->width;
  76. w_height = pCodecCtx->height;

  77. // 创建窗口
  78. win = SDL_CreateWindow("Media Player",
  79.                         SDL_WINDOWPOS_UNDEFINED,
  80.                         SDL_WINDOWPOS_UNDEFINED,
  81.                         w_width, w_height,
  82.                         SDL_WINDOW_SHOWN );
  83. if (!win)
  84. {
  85.      printf("Failed to create window by SDL\n");
  86.      goto __exit;
  87. }

  88. // 创建渲染器
  89. renderer = SDL_CreateRenderer(win, -1, 0);
  90. if (!renderer)
  91. {
  92.      printf("Failed to create Renderer by SDL\n");
  93.      goto __exit;
  94. }

  95. // 创建纹理
  96. texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV,
  97.                              SDL_TEXTUREACCESS_STREAMING,
  98.                              w_width,
  99.                              w_height);


  100. // 读取数据
  101. while (av_read_frame(pFormatCtx, &packet) >= 0)
  102. {
  103.      if (packet.stream_index == videoStream)
  104.      {
  105.          // 解码
  106.          avcodec_send_packet(pCodecCtx, &packet);
  107.          while (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
  108.          {
  109.              SDL_UpdateYUVTexture(texture, NULL,
  110.                                   pFrame->data[0], pFrame->linesize[0],
  111.                                   pFrame->data[1], pFrame->linesize[1],
  112.                                   pFrame->data[2], pFrame->linesize[2]);

  113.              // set size of Window
  114.              rect.x = 0;
  115.              rect.y = 0;
  116.              rect.w = pCodecCtx->width;
  117.              rect.h = pCodecCtx->height;

  118.              SDL_RenderClear(renderer);
  119.              SDL_RenderCopy(renderer, texture, NULL, &rect);
  120.              SDL_RenderPresent(renderer);
  121.          }
  122.      }

  123.      av_packet_unref(&packet);
  124. }

  125. __exit:

  126. if (pFrame)
  127. {
  128.      av_frame_free(&pFrame);
  129. }

  130. if (pCodecCtx)
  131. {
  132.      avcodec_close(pCodecCtx);
  133. }

  134. if (pCodecParameters)
  135. {
  136.      avcodec_parameters_free(&pCodecParameters);
  137. }

  138. if (pFormatCtx)
  139. {
  140.      avformat_close_input(&pFormatCtx);
  141. }

  142. if (win)
  143. {
  144.      SDL_DestroyWindow(win);
  145. }

  146. if (renderer)
  147. {
  148.      SDL_DestroyRenderer(renderer);
  149. }

  150. if (texture)
  151. {
  152.      SDL_DestroyTexture(texture);
  153. }

  154. SDL_Quit();

  155. return ret;
  156. }
复制代码

实机演示
https://github.com/liukangcc/ART-Pi-Smart/blob/main/figures/8.gif
该仓库放置了编译好的 FFmpeg 和 X264 库文件:https://github.com/liukangcc/ART-Pi-Smart
git直接获取
  1. git clone https://github.com/liukangcc/ART-Pi-Smart.git
复制代码








欢迎光临 谷动谷力 (http://bbs.sunsili.com/) Powered by Discuz! X3.2