FFmpeg初步了解

本文介绍了FFmpeg,它是可记录、转换数字音视频并转化为流的开源程序,具有视频采集、格式转换、截图、加水印等功能。还分享了学习笔记,包括视频文件解码流程、封装与编码格式,以及音视频编解码、转码的方案流程、程序和YUV相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、了解FFmpeg

1.1 什么是FFmpeg

        FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

      FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。这个项目最早由Fabrice Bellard发起,2004年至2015年间由Michael Niedermayer主要负责维护。许多FFmpeg的开发人员都来自MPlayer项目,而且当前FFmpeg也是放在MPlayer项目组的服务器上。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。FFmpeg编码库可以使用GPU加速。

1.2 FFmpeg的功能

1.2.1 视频采集功能

        FFmpeg视频采集功能非常强大,不仅可以采集视频采集卡或USB摄像头的图像,还可以进行屏幕录制,同时还支持以RTP方式将视频流传送给支持RTSP的流媒体服务器,支持直播应用。

1.2.2 视频格式转换功能

        FFmpeg视频转换功能。视频格式转换,比如可以将多种视频格式转换为flv格式,可不是视频信号转换 。

        FFmpeg可以轻易地实现多种视频格式之间的相互转换(wma,rm,avi,mod等),例如可以将摄录下的视频avi等转成视频网站所采用的flv格式。

1.2.3 视频截图功能

        对于选定的视频,截取指定时间的缩略图。视频抓图,获取静态图和动态图,不提倡抓gif文件;因为抓出的gif文件大而播放不流畅。

1.2.4 给视频加水印功能

        使用FFmpeg视频添加水印(watermark)。

1.3 项目组成

        FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库libavcodec等。

libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;

libavcodec:用于各种类型声音/图像编解码;

libavutil:包含一些公共的工具函数;

libswscale:用于视频场景比例缩放、色彩映射转换;

libpostproc:用于后期效果处理;

ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;

ffsever:一个 HTTP 多媒体即时广播串流服务器;

ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;

1.4 FFmpeg下载

Download FFmpegDownload FFmpegDownload FFmpeg

根据需求选择版本,免费开源。

二、学习笔记

2.1 视频文件解码流程

2.2 封装与编码格式

2.3 ffmpeg 视频解码 (解码为YUV)

2.3.1 方案流程图

注释:

1、av_register_all():注册所有组件。

2、avformat_open_input():打开输入视频文件。

3、avformat_find_stream_info():获取视频文件信息

4、avcodec_find_decoder():查找解码器。

5、avcodec_open2():打开解码器。

6、av_read_frame():从输入文件读取一帧压缩数据。

7、avcodec_decode_video2():解码一帧压缩数据。

8、avcodec_close():关闭解码器。

9、avformat_close_input():关闭输入视频文件。

2.3.2 视频解码程序

/**

​

* FFMPEG视频解码流程

* 1、av_register_all():注册所有组件。

* 2、avformat_open_input():打开输入视频文件。

* 3、avformat_find_stream_info():获取视频文件信息

* 4、avcodec_find_decoder():查找解码器。

* 5、avcodec_open2():打开解码器。

* 6、av_read_frame():从输入文件读取一帧压缩数据。

* 7、avcodec_decode_video2():解码一帧压缩数据。

* 8、avcodec_close():关闭解码器。

* 9、avformat_close_input():关闭输入视频文件。

*/

​

#include "stdafx.h"

#include <stdio.h>

​

#define __STDC_CONSTANT_MACROS

​

#ifdef _WIN32

​

//Windows

extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

#include "libswscale/swscale.h"

#include "libavutil/imgutils.h"

};

#else

​

//Linux...

#ifdef __cplusplus

extern "C"

{

#endif

​

#include <libavcodec/avcodec.h>

#include <libavformat/avformat.h>

#include <libswscale/swscale.h>

#include <libavutil/imgutils.h>

#ifdef __cplusplus

};

#endif

#endif

​

int main()

{

    //文件格式上下文

    AVFormatContext *pFormatCtx;    // 封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装 格式相关信息。

    int     i = 0, videoindex;

    AVCodecContext  *pCodecCtx;     // 编码器上下文结构体,保存了视频(音频)编解码相关信息。

    AVCodec         *pCodec;        // AVCodec是存储编解码器信息的结构体。

    AVFrame *pFrame, *pFrameYUV;    // AVFrame是包含码流参数较多的结构体

    unsigned char *out_buffer;

    AVPacket *packet;               // AVPacket是存储压缩编码数据相关信息的结构体

int y_size;

int ret, got_picture;

​

// struct SwsContext结构体位于libswscale类库中, 该类库主要用于处理图片像素数据, 可以完成图片像素格式的转换, 图片的拉伸等工作.

struct SwsContext *img_convert_ctx;

char filepath[] = "input.mkv";

FILE *fp_yuv = fopen("output.yuv", "wb+");

av_register_all();    // 注册所有组件

avformat_network_init();   // 对网络库进行全局初始化。

pFormatCtx = avformat_alloc_context();   // 初始化AVFormatContext结构体指针。使用avformat_free_context()释放内存。

if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)  // 打开输入流并读取header。必须使用avformat_close_input()接口关闭。

{

    printf("Couldn't open input stream.\n");

    return -1;

}

//读取一部分视音频数据并且获得一些相关的信息

if (avformat_find_stream_info(pFormatCtx, NULL) < 0) // 读取媒体文件的包以获取流信息

{

    printf("Couldn't find stream information.\n");

    return -1;

}

​

//查找视频编码索引

videoindex = -1;

for (i = 0; i < pFormatCtx->nb_streams; i++)

{

    if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)

    {

        videoindex = i;

        break;

    }

}

​

if (videoindex == -1)

{

    printf("Didn't find a video stream.\n");

    return -1;

}

​

//编解码上下文

pCodecCtx = pFormatCtx->streams[videoindex]->codec;

//查找解码器

pCodec = avcodec_find_decoder(pCodecCtx->codec_id); // 查找符合ID的已注册解码器

if (pCodec == NULL)

{

    printf("Codec not found.\n");

    return -1;

}

//打开解码器

if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)

{

    printf("Could not open codec.\n");

    return -1;

}

​

//申请AVFrame,用于原始视频

pFrame = av_frame_alloc();

//申请AVFrame,用于yuv视频

pFrameYUV = av_frame_alloc();

//分配内存,用于图像格式转换

out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));

// 根据指定的图像参数和提供的数组设置参数指针和linesize大小

av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

packet = (AVPacket *)av_malloc(sizeof(AVPacket));

//Output Info-----------------------------

printf("--------------- File Information ----------------\n");

//手工调试函数,输出tbn、tbc、tbr、PAR、DAR的含义

av_dump_format(pFormatCtx, 0, filepath, 0);

printf("-------------------------------------------------\n");

​

//申请转换上下文。 sws_getContext功能:初始化 SwsContext 结构体指针  

img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,

pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

​

//读取数据

while (av_read_frame(pFormatCtx, packet) >= 0) // 读取码流中的音频若干帧或者视频一帧

{

    if (packet->stream_index == videoindex)

    {

        // avcodec_decode_video2 功能:解码一帧视频数据

        ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);

        if (ret < 0)

        {

            printf("Decode Error.\n");

            return -1;

        }

​

        if (got_picture >= 1)

        {

            //成功解码一帧

            sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,

                pFrameYUV->data, pFrameYUV->linesize); // 转换图像格式

​

            y_size = pCodecCtx->width*pCodecCtx->height;

            // fwrite 功能:把 pFrameYUV 所指向数据写入到 fp_yuv 中。

            fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);    //Y

            fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U

            fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V

            printf("Succeed to decode 1 frame!\n");

        }

        else

        {

            //未解码到一帧,可能时结尾B帧或延迟帧,在后面做flush decoder处理

        }

    }

    av_free_packet(packet); // free

}

​

//flush decoder

//FIX: Flush Frames remained in Codec

while (true)

{

    if (!(pCodec->capabilities & CODEC_CAP_DELAY))

        return 0;

    // avcodec_decode_video2 功能:解码一帧视频数据

    ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);

    if (ret < 0)

    {

        break;

    }

    if (!got_picture)

    {

        break;

    }

​

    sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,

        pFrameYUV->data, pFrameYUV->linesize); // 转换图像格式

​

    int y_size = pCodecCtx->width*pCodecCtx->height;

    // fwrite 功能:把 pFrameYUV 所指向数据写入到 fp_yuv 中。

    fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);    //Y

    fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U

    fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V

    printf("Flush Decoder: Succeed to decode 1 frame!\n");

}

​

sws_freeContext(img_convert_ctx);

av_frame_free(&pFrameYUV);

av_frame_free(&pFrame);

avcodec_close(pCodecCtx);

avformat_close_input(&pFormatCtx);

fclose(fp_yuv);

​

return 0;

}

2.3.3 注意事项

解码后数据需要经过处理,去除无效数据,如下:

用sws_scale() 进行转换。

2.4 ffmpeg 视频编码 (YUV编码为H.264)

2.4.1 方案流程图

注释:

1、av_register_all():注册FFmpeg所有编解码器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值