本帖最后由 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 将其作为编码器。
下载
- git clone https://code.videolan.org/videolan/x264.git
复制代码
源码目录:
交叉编译
在 x264 文件夹同级目录下创建 build_x264.sh 文件 build_x264.sh 文件内容如下,注意:RTT_EXEC_PATH 和 ROOTDIR 修改为自己本地路径: # Get initial variablesexport RTT_EXEC_PATH=/home/liukang/repo/ART-Pi-smart/tools/gnu_gcc/arm-linux-musleabi_for_x86_64-pc-linux-gnu/binexport PATH=$PATHRTT_EXEC_PATHRTT_EXEC_PATH/../arm-linux-musleabi/binexport CROSS_COMPILE="arm-linux-musleabi"if [ "$1" == "debug" ]; then export CFLAGS="-march=armv7-a -marm -msoft-float -D__RTTHREAD__ -O0 -g -gdwarf-2 -Wall -n --static"else export CFLAGS="-march=armv7-a -marm -msoft-float -D__RTTHREAD__ -O2 -Wall -n --static"fiexport AR=${CROSS_COMPILE}-arexport AS=${CROSS_COMPILE}-asexport LD=${CROSS_COMPILE}-ldexport RANLIB=${CROSS_COMPILE}-ranlibexport CC=${CROSS_COMPILE}-gccexport CXX=${CROSS_COMPILE}-g++export NM=${CROSS_COMPILE}-nmROOTDIR="/home/liukang/repo/ART-Pi-smart/userapps"APP_NAME="x264"APP_DIR=${APP_NAME}LIB_DIR=${ROOTDIR}/sdk/libINC_DIR=${ROOTDIR}/sdk/includeRT_DIR=${ROOTDIR}/sdk/rt-threadRT_INC=" -I. -Iinclude -I${ROOTDIR} -I${RT_DIR}/include -I${RT_DIR}/components/dfs -I${RT_DIR}/components/drivers -I${RT_DIR}/components/finsh -I${RT_DIR}/components/net -I${INC_DIR}/sdl -DHAVE_CCONFIG_H"RT_INC+=" -I${ROOTDIR}/../kernel/bsp/imx6ull-artpi-smart/drivers/"export CPPFLAGS=${RT_INC}export LDFLAGS="-L${LIB_DIR} "export LIBS="-T ${ROOTDIR}/linker_scripts/arm/cortex-a/link.lds -march=armv7-a -marm -msoft-float -L${RT_DIR}/lib -Wl,--whole-archive -lrtthread -Wl,--no-whole-archive -n -static -Wl,--start-group -lc -lgcc -lrtthread -Wl,--end-group"# default buildfunction builddef() { cd ${APP_DIR} ./configure \ --prefix=/home/liukang/repo/x264lib \ --host=${CROSS_COMPILE} \ --disable-asm \ --enable-static make clean if [ "$1" == "verbose" ]; then make V=1 else make fi make install}builddef $1复制[size=0.825em]错误[size=0.825em]复制成功运行 build_x264.sh 文件,生成静态库: 上面步骤成功后,在 x264lib 文件夹下,会生成 x264 的静态库文件和头文件: 静态库文件: 头文件:
[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)]ffmpegFFMPEG_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)
完整代码- #include <stdio.h>
- #include <SDL.h>
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- extern Uint32 rtt_screen_width;
- extern Uint32 rtt_screen_heigth;
- int main (int argc, char *argv[])
- {
- int ret = -1;
- AVFormatContext *pFormatCtx = NULL;
- int videoStream;
- AVCodecParameters *pCodecParameters = NULL;
- AVCodecContext *pCodecCtx = NULL;
- AVCodec *pCodec = NULL;
- AVFrame *pFrame = NULL;
- AVPacket packet;
- SDL_Rect rect;
- SDL_Window *win = NULL;
- SDL_Renderer *renderer = NULL;
- SDL_Texture *texture = NULL;
- if(( argc != 2 ))
- {
- printf("error input arguments!\n");
- return(1);
- }
- // 默认窗口大小
- int w_width = rtt_screen_width;
- int w_height = rtt_screen_heigth;
- // use dummy video driver
- SDL_setenv("SDL_VIDEODRIVER","rtt",1);
- //Initialize SDL
- if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
- {
- printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
- return -1;
- }
- // 打开输入文件
- if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
- {
- printf("Couldn't open video file!: %s\n", argv[1]);
- goto __exit;
- }
- // 找到视频流
- videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
- if (videoStream == -1)
- {
- printf("Din't find a video stream!\n");
- goto __exit;// Didn't find a video stream
- }
- // 流参数
- pCodecParameters = pFormatCtx->streams[videoStream]->codecpar;
- // 获取解码器
- pCodec = avcodec_find_decoder(pCodecParameters->codec_id);
- if (pCodec == NULL)
- {
- printf("Unsupported codec!\n");
- goto __exit; // Codec not found
- }
- // 初始化一个编解码上下文
- pCodecCtx = avcodec_alloc_context3(pCodec);
- if (avcodec_parameters_to_context(pCodecCtx, pCodecParameters) != 0)
- {
- printf("Couldn't copy codec context\n");
- goto __exit;// Error copying codec context
- }
- // 打开解码器
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
- {
- printf("Failed to open decoder!\n");
- goto __exit; // Could not open codec
- }
- // Allocate video frame
- pFrame = av_frame_alloc();
- w_width = pCodecCtx->width;
- w_height = pCodecCtx->height;
- // 创建窗口
- win = SDL_CreateWindow("Media Player",
- SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED,
- w_width, w_height,
- SDL_WINDOW_SHOWN );
- if (!win)
- {
- printf("Failed to create window by SDL\n");
- goto __exit;
- }
- // 创建渲染器
- renderer = SDL_CreateRenderer(win, -1, 0);
- if (!renderer)
- {
- printf("Failed to create Renderer by SDL\n");
- goto __exit;
- }
- // 创建纹理
- texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV,
- SDL_TEXTUREACCESS_STREAMING,
- w_width,
- w_height);
- // 读取数据
- while (av_read_frame(pFormatCtx, &packet) >= 0)
- {
- if (packet.stream_index == videoStream)
- {
- // 解码
- avcodec_send_packet(pCodecCtx, &packet);
- while (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
- {
- SDL_UpdateYUVTexture(texture, NULL,
- pFrame->data[0], pFrame->linesize[0],
- pFrame->data[1], pFrame->linesize[1],
- pFrame->data[2], pFrame->linesize[2]);
- // set size of Window
- rect.x = 0;
- rect.y = 0;
- rect.w = pCodecCtx->width;
- rect.h = pCodecCtx->height;
- SDL_RenderClear(renderer);
- SDL_RenderCopy(renderer, texture, NULL, &rect);
- SDL_RenderPresent(renderer);
- }
- }
- av_packet_unref(&packet);
- }
- __exit:
- if (pFrame)
- {
- av_frame_free(&pFrame);
- }
- if (pCodecCtx)
- {
- avcodec_close(pCodecCtx);
- }
- if (pCodecParameters)
- {
- avcodec_parameters_free(&pCodecParameters);
- }
- if (pFormatCtx)
- {
- avformat_close_input(&pFormatCtx);
- }
- if (win)
- {
- SDL_DestroyWindow(win);
- }
- if (renderer)
- {
- SDL_DestroyRenderer(renderer);
- }
- if (texture)
- {
- SDL_DestroyTexture(texture);
- }
- SDL_Quit();
- return ret;
- }
复制代码
实机演示git直接获取 - git clone https://github.com/liukangcc/ART-Pi-Smart.git
复制代码
|