【FFMpeg视频开发与应用基础】八、 调用FFMpeg SDK实现视频缩放

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDKGithub工程代码地址:FFmpeg_Tutorial视频缩放是视频开发中一项最基本的功能。

《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK

Github工程代码地址:FFmpeg_Tutorial


视频缩放是视频开发中一项最基本的功能。通过对视频的像素数据进行采样或插值,可以将低分辨率的视频转换到更高的分辨率,或者将高分辨率的视频转换为更低的分辨率。通过FFMpeg提供了libswscale库,可以轻松实现视频的分辨率转换功能。除此之外,libswscale库还可以实现颜色空间转换等功能。

通常情况下视频缩放的主要思想是对视频进行解码到像素域后,针对像素域的像素值进行采样或差值操作。这种方式需要在解码端消耗一定时间,但是通用性最好,不需要对码流格式作出任何特殊处理。在FFMpeg中libswscale库也是针对AVFrame结构进行缩放处理。

1. 解析命令行参数

输入输出的数据使用以下结构进行封装:

typedef struct _IOFiles
{
    char *inputName;            //输入文件名
    char *outputName;           //输出文件名
    char *inputFrameSize;       //输入图像的尺寸
    char *outputFrameSize;      //输出图像的尺寸

    FILE *iFile;                //输入文件指针
    FILE *oFile;                //输出文件指针

} IOFiles;

输入参数解析过程为:

static bool hello(int argc, char **argv, IOFiles &files)
{
    printf("FFMpeg Scaling Demo.\nCommand format: %s input_file input_frame_size output_file output_frame_size\n", argv[0]);
    if (argc != 5)
    {
        printf("Error: command line error, please re-check.\n");
        return false;
    }

    files.inputName = argv[1];
    files.inputFrameSize = argv[2];
    files.outputName = argv[3];
    files.outputFrameSize = argv[4];

    fopen_s(&files.iFile, files.inputName, "rb+");
    if (!files.iFile)
    {
        printf("Error: cannot open input file.\n");
        return false;
    }

    fopen_s(&files.oFile, files.outputName, "wb+");
    if (!files.oFile)
    {
        printf("Error: cannot open output file.\n");
        return false;
    }

    return true;
}

在参数读入完成后,需要从表示视频分辨率的字符串中解析出图像的宽和高两个值。我们在命令行中传入的视频分辨率字符串的格式为“width x height”,例如”720x480”。解析过程需要调用av_parse_video_size函数。声明如下:

int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str);

例如,我们传入下面的参数:

int frameWidth, frameHeight;
av_parse_video_size(&frameWidth, &frameHeight, "720x480");

函数将分别把720和480传入frameWidth和frameHeight中。

在获取命令行参数后,调用该函数解析图像分辨率:

int srcWidth, srcHeight, dstWidth, dstHeight;
if (av_parse_video_size(&srcWidth, &srcHeight, files.inputFrameSize))
{
    printf("Error: parsing input size failed.\n");
    goto end;
}
if (av_parse_video_size(&dstWidth, &dstHeight, files.outputFrameSize))
{
    printf("Error: parsing output size failed.\n");
    goto end;
}

这样,我们就获得了源和目标图像的宽和高度。

2. 创建SwsContext结构

进行视频的缩放操作离不开libswscale的一个关键的结构,即SwsContext,该结构提供了缩放操作的必要参数。创建该结构需调用函数sws_getContext。该函数的声明如下:

struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH, enum AVPixelFormat dstFormat,
                                  int flags, SwsFilter *srcFilter,
                                 SwsFilter *dstFilter, const double *param);

该函数的前两行参数分别表示输入和输出图像的宽、高、像素格式,参数flags表示采样和差值使用的算法,常用的有SWS_BILINEAR表示双线性差值等。剩余的不常用参数通常设为NULL。创建该结构的代码如:

//创建SwsContext结构
enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P;
enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P;
struct SwsContext *sws_ctx = sws_getContext(srcWidth, srcHeight, src_pix_fmt, dstWidth, dstHeight, dst_pix_fmt, SWS_BILINEAR, NULL,NULL,NULL );
if (!sws_ctx)
{
    printf("Error: parsing output size failed.\n");
    goto end;
}

3. 分配像素缓存

视频缩放实际上是在像素域实现,但是实际上我们没有必要真的建立一个个AVFrame对象,我们只需要其像素缓存空间即可,我们定义两个指针数组和两个保存stride的数组,并为其分配内存区域:

//分配input和output
uint8_t *src_data[4], *dst_data[4];
int src_linesize[4], dst_linesize[4];
if ((ret = av_image_alloc(src_data, src_linesize, srcWidth, srcHeight, src_pix_fmt, 32)) < 0)
{
    printf("Error: allocating src image failed.\n");
    goto end;
}   
if ((ret = av_image_alloc(dst_data, dst_linesize, dstWidth, dstHeight, dst_pix_fmt, 1)) < 0)
{
    printf("Error: allocating dst image failed.\n");
    goto end;
}

4. 循环处理输入frame

循环处理的代码为:

//从输出frame中写出到输出文件
int dst_bufsize = ret;
for (int idx = 0; idx < MAX_FRAME_NUM; idx++)
{
    read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 0, files);
    read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 1, files);
    read_yuv_from_ifile(src_data, src_linesize, srcWidth, srcHeight, 2, files);

    sws_scale(sws_ctx, (const uint8_t * const*)src_data, src_linesize, 0, srcHeight, dst_data, dst_linesize);

    fwrite(dst_data[0], 1, dst_bufsize, files.oFile);
}

其核心函数为sws_scale,其声明为:

int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
          const int srcStride[], int srcSliceY, int srcSliceH,
          uint8_t *const dst[], const int dstStride[]);

该函数的各个参数比较容易理解,除了第一个是之前创建的SwsContext之外,其他基本上都是源和目标图像的缓存区和大小等。在写完一帧后,调用fwrite将输出的目标图像写入输出yuv文件中。

5. 收尾工作

收尾工作除了释放缓存区和关闭输入输出文件之外,就是需要释放SwsContext结构,需调用函数:sws_freeContext。实现过程为:

fclose(files.iFile);
fclose(files.oFile);
av_freep(&src_data[0]);
av_freep(&dst_data[0]);
sws_freeContext(sws_ctx);

视频缩放前后的效果图如下:

FFMpegScaling

目录
相关文章
|
24天前
|
监控 开发工具 Android开发
ARMS 用户体验监控正式发布原生鸿蒙应用 SDK
阿里云 ARMS 用户体验监控(RUM)推出了针对原生鸿蒙应用的 SDK。SDK 使用 ArkTS 语言开发,支持页面采集、资源加载采集、异常采集及自定义采集等功能,能够全面监控鸿蒙应用的表现。集成简单,只需几步即可将 SDK 接入项目中,为鸿蒙应用的开发者提供了强有力的支持。
|
5月前
|
数据采集 开发工具 Python
海康威视工业相机SDK+Python+PyQt开发数据采集系统(支持软件触发、编码器触发)
该系统基于海康威视工业相机SDK,使用Python与PyQt开发,支持Gige与USB相机设备的搜索及双相机同时显示。系统提供软件触发与编码器触发模式,并可在数据采集过程中实时保存图像。此外,用户可以调节曝光时间和增益,并进行信息输入,这些信息将被保存至配置文件以便下次自动加载。参数调节与实时预览等功能进一步增强了系统的实用性。
336 1
|
5月前
|
存储 监控 开发工具
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
Django 后端架构开发:手机与邮箱验证码接入、腾讯云短信SDK和网易邮箱
84 0
|
5月前
|
开发工具 数据安全/隐私保护 Python
【Azure 环境】通过Python SDK收集所有订阅简略信息,例如订阅id 名称, 资源组及组内资源信息等,如何给Python应用赋予相应的权限才能获取到信息呢?
【Azure 环境】通过Python SDK收集所有订阅简略信息,例如订阅id 名称, 资源组及组内资源信息等,如何给Python应用赋予相应的权限才能获取到信息呢?
|
7月前
|
文字识别 小程序 API
视觉智能开放平台产品使用合集之使用SDK进行视频活体检查时,如何将视频URL传递给后端服务
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
|
6月前
|
NoSQL 开发工具 数据库
开发与运维测试问题之应用启动报 Can not load this fake sdk class 的异常如何解决
开发与运维测试问题之应用启动报 Can not load this fake sdk class 的异常如何解决
|
3月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
254 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
3月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
96 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
3月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
196 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
3月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
87 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势

热门文章

最新文章