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

《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);

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

时间: 2024-09-17 04:20:24

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

【FFMpeg视频开发与应用基础】二、调用FFmpeg SDK对YUV视频序列进行编码

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 视频由像素格式编码为码流格式是FFMpeg的一项基本功能.通常,视频编码器的输入视频通常为原始的图像像素值,输出格式为符合某种格式规定的二进制码流. 1.FFMpeg进行视频编码所需要的

【FFMpeg视频开发与应用基础】三、调用FFmpeg SDK对H.264格式的视频压缩码流进行解码

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 经过了上篇调用FFMpeg SDK对视频进行编码的过程之后,我们可以比较容易地理解本篇的内容,即上一篇的逆过程--将H.264格式的裸码流解码为像素格式的图像信息. 1.FFMpeg视频

【FFMpeg视频开发与应用基础】五、调用FFMpeg SDK封装音频和视频为视频文件

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 音频和视频的封装过程为解封装的逆过程,即将独立的音频数据和视频数据按照容器文件所规定的格式封装为一个完整的视频文件的过程.对于大多数消费者来说,视频封装的容器是大家最为熟悉的,因为它直接

【FFMpeg视频开发与应用基础】六、调用FFMpeg SDK实现视频文件的转封装

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 有时候我们可能会面对这样的一种需求,即我们不需要对视频内的音频或视频信号进行什么实际的操作,只是希望能把文件的封装格式进行转换,例如从avi转换为mp4格式或者flv格式等.实际上,转封

【FFMpeg视频开发与应用基础】七、 调用FFMpeg SDK实现视频水印

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK Github工程代码地址:FFmpeg_Tutorial 视频的水印通常指附加在原始视频上的可见或者不可见的,与原始视频无直接关联的标识.通常在有线电视画面上电视台的台标以及视频网站上的logo就是典型的视频水印的应用场景.通常实现

【FFMpeg视频开发与应用基础】一、使用FFmpeg命令行工具和批处理脚本进行简单的音视频文件编辑

<FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK>视频教程已经在"CSDN学院"上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础--使用FFMpeg工具与SDK 工程代码地址:FFmpeg_Tutorial 1.基本介绍 对于每一个从事音视频技术开发的工程师,想必没有一个人对FFmpeg这个名称感到陌生.FFmpeg是一套非常知名的音视频处理的开源工具,它包含了开发完成的工具软件.封装好的函

ios-iOS 视频开发硬编码和硬解码

问题描述 iOS 视频开发硬编码和硬解码 最近在做视频的硬编码和硬解码.就像是视频通话,有经验的朋友请指教一下.本人绝对知恩图报,懂味的谢谢 解决方案 参考这几个资源http://download.csdn.net/detail/linux_vae/9283241http://download.csdn.net/detail/hulianchuangcai/9316961http://download.csdn.net/download/dongdongzh/9004185 解决方案二: htt

求助!!怎么用c#调用ffmpeg压缩视频

问题描述 我要做一个在线视频聊天的项目,想把视频压缩后再传输,尽量达到QQ视频聊天的每秒40k的传输率,求高手指导ffmpeg的调用方法 解决方案 解决方案二:高手快来啊解决方案三:高手在哪里解决方案四:直接调用ffmpeg的命令行程序呗.解决方案五:参考http://www.codeproject.com/Articles/24995/FFMPEG-using-ASP-NET实时传输的asp.net的例子没找到,但是可以看官方文档https://trac.ffmpeg.org/wiki/Str

java调用ffmpeg实现视频转换的方法_java

本文实例讲述了java调用ffmpeg实现视频转换的方法.分享给大家供大家参考.具体分析如下: 这里环境我是在windows平台下测试的... 需要在e:\下有ffmpeg.exe;mencoder.exe;drv43260.dll;pncrt.dll共4个文件.   还要在e:\input下放各种文件名为a的以下各种视频文件:还要e:\output:java程序执行后能得到一个a.flv的已转换的文件. ffmpeg.exe能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov