

FFmpeg使用实例详解第二节,下载和编译 SDL-1.2.15 库,编写C语言程序,制作自己的视...
source link: https://blog.popkx.com/ffmpeg-example-section-2-download-and-compile-sdl-1-2-15-writing-c-program-to-make-video-player/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上一节简要的介绍了如何编写C语言程序调用 FFmpeg 库,读取视频文件,并将之逐帧转换为 ppm 图片,最后通过 save_frame()
函数将转换后的 ppm 图片保存到磁盘。本节将在上一节的基础上,编写C语言程序,将拆解的视频帧直接输送到屏幕上(取代save_frame()
函数),实现所谓的“视频播放器”。
下载和编译 SDL-1.2.15
本文计划使用多媒体库 SDL 将视频帧输送到屏幕。SDL 的全称是 Simple Direct Layer,是业界常用的跨平台的多媒体库,例如 FFmpeg 的子应用 ffplay 就是基于 SDL 工作的。要使用 SDL 库,首先需要做的工作就是下载和编译它,相关资源可以从其官网获得。
考虑到经典版本 SDL-1.2.15 的学习资源更加丰富,所以本文并未使用最新版 SDL-2.0,读者可以自行下载和编译自己感兴趣的版本(编译步骤都是类似的,只不过使用过程中的某些 API 有一定的改动,详情可参考官网)。下载 SDL 库后,执行下面的命令编译和安装:
$ tar xf SDL-1.2.15.tar.gz
$ cd SDL-1.2.15
$ mkdir build
$ cd build
$ ../configure --prefix=<安装目录>
同样的,configure
时的--prefix
选项可以指定安装目录,若是不指定,接下来 SDL 库将被安装到系统目录。执行完上面几条命令,不出意外的话将得到 makefile,继续执行下面两条命令即可完成 SDL-1.2.15 库的编译和安装:
$ make -j
$ make install
初始化 SDL 库
我们的目标是使用 SDL 库,编写C语言播放视频函数,替代上一节中的 save_frame()
函数,制作自己的视频播放器。要实现这一目标,首先需要明白如何使用 SDL 库,请看下面这段C语言代码:
#include <SDL.h>
#include <SDL_thread.h>
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}
SDL_Init()
函数可以以掩码的形式接收若干选项,以确定 SDL 库接下来将要使用的功能,SDL_GetError()
函数用来获取出错原因信息。如果初始化没有问题,就可以使用 SDL 库的其他功能了。
请注意,本文仅从使用实例出发,并不会详细介绍每一个 API,那样太枯燥乏味了,详细信息可以从官网获得。
创建播放区域
在 SDL 中,用来播放视频图像的区域被称为surface
,创建 surface 的C语言代码可以按照下面这样写:
SDL_Surface *surface;
surface= SDL_SetVideoMode(width, height, 0, 0);
if(!surface) {
fprintf(stderr, "SDL: could not set video mode - exiting\n");
exit(1);
}
上面的SDL_SetVideoMode()
函数创建一个宽为 width,高为 height 的 surface 用于播放视频,可以通过它的后面两个参数指定单位像素位数和其他选项,这里使用默认值了。成功创建 surface 后,便可在屏幕上创建 YUV 播放区域 overlay,之后的视频帧数据都将传递给 overlay 处理,这一过程的C语言代码可以如下实现:
SDL_Overlay *overlay = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, surface);
从SDL_CreateYUVOverlay()
函数的第三个参数可以看出,我们创建的overlay 将接收 YV12 类型的数据,所以应该通过 FFmpeg 库将源视频数据转换为对应类型,这一过程在上一节已经熟悉,相应的C语言代码可以如下写:
struct SWSContext *sws_ctx = NULL;
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_YUV420P,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
从第 6 个参数可以看出,接下来传递给 overlay 的将会是 YUV420P 格式的数据。
SDL 库支持若干种将图像显示到屏幕的方法,但是最常用的还是 YUV 方式。YUV 类似于 RGB,是一种存放图像数据的格式。简略来说,“Y”是图像数据的亮度分量,“U”和“V”则是颜色分量,不过 YUV 格式比 RGB 格式更加复杂一些,因为 Y/U/V 可能不是 1:1:1 等比例分布的,它有 4 种常用的分布,YV12 一般是最快的类型,YUV420P 则类似于 YV12,420 是指几个分量的比例为 4:2:0,即每 1 个颜色分量对应 4 个亮度分量。这里只需理解 YUV 是一种图像存储格式即可,关于 YUV 以后有机会再详谈。
填充图像数据
准备工作做好,就可以将图像数据显示到屏幕,也即播放视频了。这一工作的首要条件是获取完整的一帧数据,回忆下上一节,我们定义了 frame_finished
变量用于标记视频帧是否获取完整,因此要播放视频,检查此标记变量即可。一旦获取到完整的视频帧,就把相应的数据塞入前面创建的 overlay,这一过程的C语言代码可以如下写:
while (av_read_frame(pctx, &pkt) >= 0) {
if (pkt.stream_index != video_stream) {
continue;
}
avcodec_decode_video2(pcodec_ctx, pframe, &frame_finished, &pkt);
if (!frame_finished)
continue;
// 只有 frame_finished 为真才可到达此处
SDL_LockYUVOverlay(overlay);
sws_scale(sws_ctx, pframe->data, pframe->linesize,
0, pcodec_ctx->height, pict.data, pict.linesize);
SDL_UnlockYUVOverlay(overlay);
SDL_DisplayYUVOverlay(overlay, &play_rect);
}
请注意,我们希望获得的是 YUV420P 格式的数据,因此上述C语言代码中用于填充数据的是 sws_scale()
函数,其中的 sws_ctx 由前文创建,我们已经指定将图像数据转换为 YUV420P。转换后的数据被塞入 pict
,它是一个 AVPicture 类型的结构体,通过以下C语言代码与前面创建的 overlay 建立联系:
AVPicture pict;
pict.data[0] = overlay->pixels[0];
pict.data[1] = overlay->pixels[2];
pict.data[2] = overlay->pixels[1];
pict.linesize[0] = overlay->pitches[0];
pict.linesize[1] = overlay->pitches[2];
pict.linesize[2] = overlay->pitches[1];
这里一定要注意,pict 的顺序与 overlay 的顺序并不完全一致。另外值得说明的是在填充 overlay 数据(显式填充的是 pict)的过程中,上述C语言代码调用了 SDL_LockYUVOverlay() 锁函数用于确保数据安全。
现在图像数据也填充好了,终于可以在屏幕上播放了:
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_DisplayYUVOverlay(overlay, &rect);
从上述C语言代码可以看出,SDL_DisplayYUVOverlay() 函数可以通过一个 rect 结构体最终指定播放区域的位置和缩放尺寸,本例将播放器置于屏幕的左上角,缩放比例为 1,也即以视频的原始尺寸播放。
完整C语言代码可以参考:
编译后,指定一个视频文件,即可播放视频:
$ test.out tmp/test.mp4
我们的播放器播放效果如下:
本文主要介绍了如何使用 SDL-1.2.15 库以及C语言,编写自己的视频播放器。但是应该明白,本文的视频播放器只是一个雏形,它还不能播放声音,甚至不能在播放期间与用户交互,这些都是需要改进的部分,我们将在之后的文章中实现这些改进,敬请关注。
Recommend
-
52
Go - @Hanggi - 发现 GO 语言可以在本地 OS 上生成 Linux 可执行的 exe 文件,将 exe 文件上传到服务器就可以直接运行了,好方便啊。其他语言都是怎么部署的?有没有代码部署黑科技分享一下?
-
31
原文地址: 第一个 Go 语言程序:漫画下载器: https://schaepher.github.io/2020/04/11/golang-first-comic-downloader
-
7
视音频的基本概念我们常说的视频文件(例如 avi 文件,MP4 文件等)本质上是一种“容器”,其内部存放一帧帧的视频信息和音频信息。因此,视频文件内部常常包含不止一个“信息流”,而是包含一组“信息流”(若干视频流和若干音频流)。...
-
12
长久以来,其实我使用云服务器开发C语言程序,它还可以建立个人网站 发表于...
-
16
C语言程序如果不关闭打开的文件,会发生什么?为什么要调用fclose() 函数? 发表于...
-
12
使用临时中间变量,会降低C语言程序效率吗? 发表于 2019-10-15 18:10:38...
-
6
详解C语言程序的编译 原创 玄鸟轩墨 2022-03-03 19:40:14 博主文...
-
7
shadowsocks客户端下载和使用教程 很多时候,我们仅仅只是需要上一下google,收个gmail邮件,或者打开某个网站瞄一眼看看有无更新。这种情况下,科学上网可以做到吗,可以,但是很麻烦,连个科学...
-
7
https://garygeng.com/others/ssr-windows/很多的同学还是没有找到有效的SSR客户端下载地址,所以整理了下win下ssr客户端在使用上的问题,本文只提供工具和教程 ...
-
1
Etcher 是由
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK