vieyahn2017 / iBlog

44 stars 0 forks source link

11.27 FFMPEG学习(+opencv) #247

Closed vieyahn2017 closed 5 years ago

vieyahn2017 commented 5 years ago

FFMPEG学习

vieyahn2017 commented 5 years ago

利用ffmpeg和opencv进行视频的解码播放

2017年03月03日 09:00:21 lk989898 阅读数:1937

https://blog.csdn.net/u011430225/article/details/60100168

引子

OpenCV中有自己的用于处理图片和视频的类VideoCapture,可以很方便的读入文件和显示。 现在视频数据流是ffmpeg解码h264文件得到的,由于要依赖该数据源进行相应的后续处理,所以需要将ffmpeg中得到的数据缓存转换成可以被OpenCV处理的Mat类对象。

ffmpeg介绍

FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。 FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它包括了目前领先的音/视频编码库libavcodec。 FFmpeg是在Linux下开发出来的,但它可以在包括Windows在内的大多数操作系统中编译。

FFmpeg的组成结构

FFmpeg主要由一下几个部分组成:

libavcodec:一个包含了所有FFmpeg音视频编解码器的库。 为了保证最优性能和高可复用性,大多数编解码器从头开发的。 libavformat:一个包含了所有的普通音视格式的解析器和 产生器的库。 三个实例程序,这三个实例较为复杂,基本可以作为API使用手册: ffmpeg:命令行的视频格式转换程序。 ffplay:视频播放程序。(需要SDL支持) ffserver:多媒体服务器 了解完组成结构后,你会发现,如果你在寻找一种视频格式转换的方式,那FFmpeg绝对是你的第一选择,libavcodec 则又是重 中之重。如果遇上API不会使用的情况,可以参考ffmpeg.c、ffplay.c、 ffserver.c、apiexample.c(解码)和output_example.c(编码)。

ffmpeg使用说明

ffmpeg库的接口都是c函数,其头文件也没有extern "C"的声明,所以在cpp文件里调用ffmpeg函数要注意了。 一般来说,一个用C写成的库如果想被C/C++同时可以使用,那在头文件应该加上

#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} // endof extern "C"
#endif

如果在.cpp里调用av_register_all()在链接时将找到不符号,因为.cpp要求的符号名 和ffmpeg库提供的符号名不一致。 可以这么解决:

extern "C"
{
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

使用ffmpeg SDK解码流数据过程

以H264视频流为例,讲解解码流数据的步骤。

准备变量

定义AVCodec,AVCodec *变量为解码器指针。 定义AVCodecContext,使用该变量可以将其定义为ffmpeg解码类的类成员。 定义AVFrame,AVFrame描述一个多媒体帧。解码后的数据将被放在其中。 定义AVFormatContext变量,AVFormatContext用于保存视频流的有效信息。

AVCodec    *pCodec;
AVCodecContext * pCodecCtx;
AVFrame * pAvFrame;
AVFormatContext    *pFormatCtx;

初始化解码器

第一件事情就是初始化libavformat/libavcodec: ffmpeg注册复用器,编码器等的函数av_register_all()。

av_register_all();

这一步注册库中含有的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用 av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要这样做。

打开视频文件,取出包含在文件中的流信息

// 打开视频文件
if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
       handle_error(); // 不能打开此文件

这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。 最后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat 去自动探测文件格式并且使用默认的缓冲区大小。

// 取出流信息
if(av_find_stream_info(pFormatCtx)<0)
     handle_error(); // 不能够找到流信息

查找文件的流信息,avformat_open_input函数只是检测了文件的头部,接着要检查在文件中的流的信息。 这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满。作为一个可调试的诊断,我们会将这些信息全盘输出到标准错误输出中,不过你在一个应用程序的产品中并不用这么做。

我们仅仅处理视频流,而不是音频流。为了让这件事情更容易理解,我们只简单使用我们发现的第一种视频流。

//遍历文件的各个流,找到第一个视频流,并记录该流的编码信息
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;
}
pCodecCtx=pFormatCtx->streams[videoindex]->codec;

我们已经得到了一个指向视频流的称之为上下文的指针。接下来,我们需要找到真正的编码器打开它。

寻找视频流的解码器

在库里面查找支持该格式的解码器

pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
    handle_error(); // 找不到解码器

打开解码器

if(avcodec_open(pCodecCtx, pCodec)<0)
    handle_error();

给视频帧分配空间,以便存储解码后的图片数据

pAvFrame = avcodec_alloc_frame();

解码视频帧就像我前面提到过的,视频文件包含数个音频和视频流,并且他们各个独自被分开存储在固定大小的包里。我们要做的就是使用libavformat依次读取这些包,过滤掉所有那些视频流中我们不感兴趣的部分,并把它们交给libavcodec进行解码处理。

进行解码

通过该api读入一帧

result = av_read_frame(pFormatCtx, packet);

通过下面的api进行解码一帧数据,将有效的图像数据存储到pAvFrame成员变量中

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

下面是ffmpeg解码的API:

将YUV420p颜色编码转换成BGR颜色编码

首先得到图片转换上下文img_convert_ctx,这里注意的是,opencv的RGB编码顺序为BGR,所以选用AV_PIX_FMT_BGR24的编码方式。

//根据编码信息设置渲染格式
            if(img_convert_ctx == NULL){
                img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                    pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                    AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); 
            }

再得到为BGR格式帧分配内存

AVFrame    *pFrameRGB = NULL;
uint8_t  *out_bufferRGB = NULL;
pFrameRGB = avcodec_alloc_frame();

//给pFrameRGB帧加上分配的内存;
int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
out_bufferRGB = new uint8_t[size];
avpicture_fill((AVPicture *)pFrameRGB, out_bufferRGB, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

最后进行转换

sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

OpenCV Mat数据复制

cv::Mat对象中有data指针,指向内存中存放矩阵数据的一块内存 (uchar* data)。 所以,要将ffmpeg解码之后得到的RGB色彩的帧数据复制给该指针,这样就实现了ffmpeg解码数据到opencv中Mat格式的转换,进而就可以对Mat对象进行相应的处理。

代码示例

ffmpegDecode.h文件

#ifndef __FFMPEG_DECODE_H__
#define __FFMPEG_DECODE_H__

#include <opencv2/core/core.hpp>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
//图像转换结构需要引入的头文件
#include "libswscale/swscale.h"
};

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib  ")
#pragma comment(lib, "avutil.lib    ")
#pragma comment(lib, "avdevice.lib  ")
#pragma comment(lib, "avfilter.lib  ")
#pragma comment(lib, "postproc.lib  ")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib   ")

class ffmpegDecode
{
public:
    ffmpegDecode(char * file = NULL);
    ~ffmpegDecode();

    cv::Mat getDecodedFrame();
    cv::Mat getLastFrame();
    int readOneFrame();
    int getFrameInterval();

private:
    AVFrame    *pAvFrame;
    AVFormatContext    *pFormatCtx;
    AVCodecContext    *pCodecCtx;
    AVCodec            *pCodec;

    int    i; 
    int videoindex;

    char *filepath;
    int ret, got_picture;
    SwsContext *img_convert_ctx;
    int y_size;
    AVPacket *packet;

    cv::Mat *pCvMat;

    void init();
    void openDecode();
    void prepare();
    void get(AVCodecContext *pCodecCtx, SwsContext *img_convert_ctx,AVFrame    *pFrame);
};

#endif
ffmpegDecode.cpp文件

#include "ffmpegDecode.h"

ffmpegDecode :: ~ffmpegDecode()
{
    pCvMat->release();
    //释放本次读取的帧内存
    av_free_packet(packet);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

ffmpegDecode :: ffmpegDecode(char * file)
{
    pAvFrame = NULL/**pFrameRGB = NULL*/;
    pFormatCtx  = NULL;
    pCodecCtx   = NULL;
    pCodec      = NULL;

    pCvMat = new cv::Mat();
    i=0;
    videoindex=0;

    ret = 0;
    got_picture = 0;
    img_convert_ctx = NULL;
    y_size = 0;
    packet = NULL;

    if (NULL == file)
    {
        filepath =  "opencv.h264";
    }
    else
    {
        filepath = file;
    }

    init();
    openDecode();
    prepare();

    return;
}

void ffmpegDecode :: init()
{
    //ffmpeg注册复用器,编码器等的函数av_register_all()。
    //该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只有调用了该函数,才能使用复用器,编码器等。
    //这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用 av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要这样做。
    av_register_all();

    //pFormatCtx = avformat_alloc_context();
    //打开视频文件,通过参数filepath来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。
    //最后2个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。
    if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0)
    {
        printf("无法打开文件\n");
        return;
    }

    //查找文件的流信息,avformat_open_input函数只是检测了文件的头部,接着要检查在文件中的流的信息
    if(av_find_stream_info(pFormatCtx)<0)
    {
        printf("Couldn't find stream information.\n");
        return;
    }
    return;
}

void ffmpegDecode :: openDecode()
{
    //遍历文件的各个流,找到第一个视频流,并记录该流的编码信息
    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;
    }
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;

    //在库里面查找支持该格式的解码器
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        printf("Codec not found.\n");
        return;
    }

    //打开解码器
    if(avcodec_open2(pCodecCtx, pCodec,NULL) < 0)
    {
        printf("Could not open codec.\n");
        return;
    }
}

void ffmpegDecode :: prepare()
{
    //分配一个帧指针,指向解码后的原始帧
    pAvFrame=avcodec_alloc_frame();
    y_size = pCodecCtx->width * pCodecCtx->height;
    //分配帧内存
    packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    //输出一下信息-----------------------------
    printf("文件信息-----------------------------------------\n");
    av_dump_format(pFormatCtx,0,filepath,0);
    //av_dump_format只是个调试函数,输出文件的音、视频流的基本信息了,帧率、分辨率、音频采样等等
    printf("-------------------------------------------------\n");
}

int ffmpegDecode :: readOneFrame()
{
    int result = 0;
    result = av_read_frame(pFormatCtx, packet);
    return result;
}

cv::Mat ffmpegDecode :: getDecodedFrame()
{
    if(packet->stream_index==videoindex)
    {
        //解码一个帧
        ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);
        if(ret < 0)
        {
            printf("解码错误\n");
            return cv::Mat();
        }
        if(got_picture)
        {
            //根据编码信息设置渲染格式
            if(img_convert_ctx == NULL){
                img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                    pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                    AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); 
            }    
            //----------------------opencv
            if (pCvMat->empty())
            {
                pCvMat->create(cv::Size(pCodecCtx->width, pCodecCtx->height),CV_8UC3);
            }

            if(img_convert_ctx != NULL)  
            {  
                get(pCodecCtx, img_convert_ctx, pAvFrame);
            }
        }
    }
    av_free_packet(packet);

    return *pCvMat;
}

cv::Mat ffmpegDecode :: getLastFrame()
{
    ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);
    if(got_picture) 
    {  
        //根据编码信息设置渲染格式
        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); 

        if(img_convert_ctx != NULL)  
        {  
            get(pCodecCtx, img_convert_ctx,pAvFrame);
        }  
    } 
    return *pCvMat;
}

void ffmpegDecode :: get(AVCodecContext    * pCodecCtx, SwsContext * img_convert_ctx, AVFrame * pFrame)
{
    if (pCvMat->empty())
    {
        pCvMat->create(cv::Size(pCodecCtx->width, pCodecCtx->height),CV_8UC3);
    }

    AVFrame    *pFrameRGB = NULL;
    uint8_t  *out_bufferRGB = NULL;
    pFrameRGB = avcodec_alloc_frame();

    //给pFrameRGB帧加上分配的内存;
    int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
    out_bufferRGB = new uint8_t[size];
    avpicture_fill((AVPicture *)pFrameRGB, out_bufferRGB, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

    //YUV to RGB
    sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

    memcpy(pCvMat->data,out_bufferRGB,size);

    delete[] out_bufferRGB;
    av_free(pFrameRGB);
}

转载请注明作者Jason Ding及其出处

Github主页(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)

vieyahn2017 commented 5 years ago

ffmpeg+opencv实现画中画

https://blog.csdn.net/yuanchunsi/article/details/76590536

2017年08月02日 18:00:36 yuanchunsi 阅读数:1370 个人分类: 播放器 ffmpeg & SDL & OpenGL

用ffmpeg解码,并且将解码后的视频传入opencv。 通过查找相关资料进行快速学习实现了这个需求。现进行简单的记录和分享。

ffmpeg 解码函数:len = avcodec_decode_video2(pInputCodecContext, dst, &nComplete, &InPack);
dst 为 AVFrame dst,存放了解码后的数据。解码后存入dst的视频是yuv420格式。 dst->data[0],dst->data[1],dst->data[2] 中分别存了 yuv的数据。 dst->widthdst->height 是原始视频的分辨率。

opencv 结构体 Mat frame, frame=frame.create(dst->height1.5, dst->width, CV_8UC1); 用frame存储 yuv420 的数据, yuv 需要申请的内存大小为dst->widthdst->height *1.5

memcpy(frame.data,dst->data[0], dst->width*dst->height);  存入 y
memcpy(frame.data+640*480,dst->data[1], dst->width*dst->height/4);   存入 u
memcpy(frame.data+640*480*5/4,dst->data[2], dst->width*dst->height/4);  存入 v

这样解码后的yuv420 数据就存入了opencv中。可添加

cv::cvtColor(frame, rgbImg,CV_YUV2BGR_I420);  //yuv转成rgb
if(frame.empty())break;
imshow("frame",rgbImg);

waitKey(1); 显示解码后的视频

需求:把两路视频合成一路,即一个画面同时显示两路视频,其中一路缩小成小视频叠在大视频上面,和电视机的画中画效果类似。

思路:用h264编码的视频举例,文件中存储的es流是h264,经过解码成yuv,yuv可以转换成rgb格式。把小视频的rgb复制到大视频需要被覆盖的位置上。将重新合成的rgb转换成yuv,利用ffmpeg 或 x264重新编码出新的视频即可。

方法:编解码还是利用ffmpeg 。 ffmpeg 解码两路视频,解码后都是yuv。利用ffmpeg· 的sws_getContext 函数改变小图的大小。之后利用opencv完成两个图片的合成(opencv这种高大上的库被我用成了这样····实在汗颜。其实此处的合并rgb可以自己写算法实现,本质是把小图的rgb复制到大图的对应位置上。)合成好后将rbg转成yuv格式,利用x264重新编码成h264 ,就看到了大视频左上角有个小视频了。

代码思路:

两个线程,各自解码,主视频的线程解码一帧后通知副视频的线程进行解码并转化图片大小,副视频的线程解码完成后通知主线程合成视频并编码。合成视频的时候用opencv很简单,直接把yuv转化成rgb,之后在主视频上设置敏感区,把小视频叠加上就行。

主副线程解码套路一样,ffmpeg的基本使用套路。把yuv转成opencv mat类型的rgb套路也一样,上副视频解码线程的代码进行说明。

全局变量:

cv::Mat littlergb,bigrgb;//大小视频的rgb  
cv::Mat littleframe,bigframe;//大小视频的yuv 

副视频解码线程内的代码:

while(av_read_frame(pInputFormatContext, &InPack) >=0)  
    {  
        len = avcodec_decode_video2(pInputCodecContext, &OutFrame, &nComplete, &InPack);//解码视频  
        if (nComplete>0)  
        {     
            if (GetMessage(&msg, NULL, 0, 0))  
            {  
                switch(msg.message)  
                {  
                case MY_MSG_DECODE:  
                    sws_scale(m_pSwsContext,OutFrame.data,OutFrame.linesize, 0,OutFrame.height,dst->data,dst->linesize);//转换图片大小  

                    memcpy(littleframe.data,dst->data[0], 640*480);  //以下将ffmpeg的yuv数据存到opencv的mat类型中。即opencv存储的yuv数据  
                    memcpy(littleframe.data+640*480,dst->data[1], 640*480/4);    
                    memcpy(littleframe.data+640*480*5/4,dst->data[2], 640*480/4);   
                    SetEvent(hEncodeEvent);  

                    break;  
                }  
            }  

        }  
        av_free_packet(&InPack);  
    }  

合并图片直接用opencv,代码如下:

cv::cvtColor(littleframe, littlergb,CV_YUV2BGR_I420);   
cv::cvtColor(bigframe, bigrgb,CV_YUV2BGR_I420); //以上yuv转rgb  
Mat roi(bigrgb,Rect(0,0,640,480));//大图上设置敏感区  
littlergb.copyTo(roi);  //把小图拷贝过去  
Mat outframe;  
cv::cvtColor(bigrgb, outframe,CV_BGR2YUV_I420); //rgb到yuv  

这样就获得了合并后的图片的yuv。

之后进行编码即可。编码出来的视频就是画中画了。

vieyahn2017 commented 5 years ago

FFMPEG命令详解
日期:2017-11-23 08:49 浏览:240 回复:0

一、ffmpeg命令详解

ffmpeg非常强大,轻松几条命令就可以完成你的工作。

把darkdoor.[001-100].jpg序列帧和001.mp3音频文件利用mpeg4编码方式合成视频文件darkdoor.avi: $ ffmpeg -i 001.mp3 -i darkdoor.%3d.jpg -s 1024x768 -author skypp -vcodec mpeg4 darkdoor.avi

ffmpeg还支持mov格式: $ ffmpeg -i darkdoor.%3d.jpg darkdoor.mov

要查看你的ffmpeg支持哪些格式,可以用如下命令: $ ffmpeg -formats | less

还可以把视频文件导出成jpg序列帧: $ ffmpeg -i bc-cinematic-en.avi example.%d.jpg

debian下安装ffmpeg很简单: #apt-get install ffmpeg

######################################

下面是转来的使用说明,慢慢研究吧,嘿嘿

######################################

ffmpeg使用语法

ffmpeg使用语法:

ffmpeg [[options][`-i' input_file]]... {[options] output_file}...

如果没有输入文件,那么视音频捕捉就会起作用。

作为通用的规则,选项一般用于下一个特定的文件。如果你给 –b 64选项,改选会设置下一个视频速率。对于原始输入文件,格式选项可能是需要的。

缺省情况下,ffmpeg试图尽可能的无损转换,采用与输入同样的音频视频参数来输出。

3.选项

a) 通用选项

-L license

-h 帮助

-fromats 显示可用的格式,编解码的,协议的。。。

-f fmt 强迫采用格式fmt

-I filename 输入文件

-y 覆盖输出文件

-t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持

-ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持

-title string 设置标题

-author string 设置作者

-copyright string 设置版权

-comment string 设置评论

-target type 设置目标文件类型(vcd,svcd,dvd) 所有的格式选项(比特率,编解码以及缓冲区大小)自动设置,只需要输入如下的就可以了: ffmpeg -i myfile.avi -target vcd /tmp/vcd.mpg

-hq 激活高质量设置

-itsoffset offset 设置以秒为基准的时间偏移,该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset秒。 [-]hh:mm:ss[.xxx]的格式也支持

b) 视频选项

-b bitrate 设置比特率,缺省200kb/s

-r fps 设置帧频 缺省25

-s size 设置帧大小 格式为WXH 缺省160X128.下面的简写也可以直接使用: Sqcif 128X96 qcif 176X144 cif 252X288 4cif 704X576

-aspect aspect 设置横纵比 4:3 16:9 或 1.3333 1.7777

-croptop size 设置顶部切除带大小 像素单位

-cropbottom size –cropleft size –cropright size

-padtop size 设置顶部补齐的大小 像素单位

-padbottom size –padleft size –padright size –padcolor color 设置补齐条颜色(hex,6个16进制的数,红:绿:兰排列,比如 000000代表黑色)

-vn 不做视频记录

-bt tolerance 设置视频码率容忍度kbit/s

-maxrate bitrate设置最大视频码率容忍度

-minrate bitreate 设置最小视频码率容忍度

-bufsize size 设置码率控制缓冲区大小

-vcodec codec 强制使用codec编解码方式。如果用copy表示原始编解码数据必须被拷贝。

-sameq 使用同样视频质量作为源(VBR)

-pass n 选择处理遍数(1或者2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率

-passlogfile file 选择两遍的纪录文件名为file

c)高级视频选项

-g gop_size 设置图像组大小

-intra 仅适用帧内编码

-qscale q 使用固定的视频量化标度(VBR)

-qmin q 最小视频量化标度(VBR)

-qmax q 最大视频量化标度(VBR)

-qdiff q 量化标度间最大偏差 (VBR)

-qblur blur 视频量化标度柔化(VBR)

-qcomp compression 视频量化标度压缩(VBR)

-rc_init_cplx complexity 一遍编码的初始复杂度

-b_qfactor factor 在p和b帧间的qp因子

-i_qfactor factor 在p和i帧间的qp因子

-b_qoffset offset 在p和b帧间的qp偏差

-i_qoffset offset 在p和i帧间的qp偏差

-rc_eq equation 设置码率控制方程 默认tex^qComp

-rc_override override 特定间隔下的速率控制重载

-me method 设置运动估计的方法 可用方法有 zero phods log x1 epzs(缺省) full

-dct_algo algo 设置dct的算法 可用的有 0 FF_DCT_AUTO 缺省的DCT 1 FF_DCT_FASTINT 2 FF_DCT_INT 3 FF_DCT_MMX 4 FF_DCT_MLIB 5 FF_DCT_ALTIVEC

-idct_algo algo 设置idct算法。可用的有 0 FF_IDCT_AUTO 缺省的IDCT 1 FF_IDCT_INT 2 FF_IDCT_SIMPLE 3 FF_IDCT_SIMPLEMMX 4 FF_IDCT_LIBMPEG2MMX 5 FF_IDCT_PS2 6 FF_IDCT_MLIB 7 FF_IDCT_ARM 8 FF_IDCT_ALTIVEC 9 FF_IDCT_SH4 10 FF_IDCT_SIMPLEARM

-er n 设置错误残留为n 1 FF_ER_CAREFULL 缺省 2 FF_ER_COMPLIANT 3 FF_ER_AGGRESSIVE 4 FF_ER_VERY_AGGRESSIVE

-ec bit_mask 设置错误掩蔽为bit_mask,该值为如下值的位掩码 1 FF_EC_GUESS_MVS (default=enabled) 2 FF_EC_DEBLOCK (default=enabled)

-bf frames 使用frames B 帧,支持mpeg1,mpeg2,mpeg4

-mbd mode 宏块决策 0 FF_MB_DECISION_SIMPLE 使用mb_cmp 1 FF_MB_DECISION_BITS 2 FF_MB_DECISION_RD

-4mv 使用4个运动矢量 仅用于mpeg4

-part 使用数据划分 仅用于mpeg4

-bug param 绕过没有被自动监测到编码器的问题

-strict strictness 跟标准的严格性

-aic 使能高级帧内编码 h263+

-umv 使能无限运动矢量 h263+

-deinterlace 不采用交织方法

-interlace 强迫交织法编码仅对mpeg2和mpeg4有效。当你的输入是交织的并且你想要保持交织以最小图像损失的时候采用该选项。可选的方法是不交织,但是损失更大

-psnr 计算压缩帧的psnr

-vstats 输出视频编码统计到vstats_hhmmss.log

-vhook module 插入视频处理模块 module 包括了模块名和参数,用空格分开

D)音频选项

-ab bitrate 设置音频码率

-ar freq 设置音频采样率

-ac channels 设置通道 缺省为1

-an 不使能音频纪录

-acodec codec 使用codec编解码

E)音频/视频捕获选项

-vd device 设置视频捕获设备。比如/dev/video0

-vc channel 设置视频捕获通道 DV1394专用

-tvstd standard 设置电视标准 NTSC PAL(SECAM)

-dv1394 设置DV1394捕获

-av device 设置音频设备 比如/dev/dsp

F)高级选项

-map file:stream 设置输入流映射

-debug 打印特定调试信息

-benchmark 为基准测试加入时间

-hex 倾倒每一个输入包

-bitexact 仅使用位精确算法 用于编解码测试

-ps size 设置包大小,以bits为单位

-re 以本地帧频读数据,主要用于模拟捕获设备

-loop 循环输入流。只工作于图像流,用于ffserver测试

二、Ffmpeg使用语法

Ffmpeg使用语法 ffmpeg [[options][`-i' input_file]]... {[options] output_file}... 如果没有输入文件,那么视音频捕捉(只在Linux下有效,因为Linux下把音视频设备当作文件句柄来处理)就会起作用。作为通用的规则,选项一 般用于下一个特定的文件。如果你给 –b 64选项,改选会设置下一个视频速率。对于原始输入文件,格式选项可能是需要的。缺省情况下,ffmpeg试图尽可能的无损转换,采用与输入同样的音频视 频参数来输出。

ffmpeg支持多种文件格式和多种音频、视频编码器.

视频文件截图 截取一张352x240尺寸大小的,格式为jpg的图片 ffmpeg -i test.asf -y -f image2 -t 0.001 -s 352x240 a.jpg

把视频的前30帧转换成一个Animated Gif ffmpeg -i test.asf -vframes 30 -y -f gif a.gif

截取指定时间的缩微图 ffmpeg -i test.avi -y -f image2 -ss 8 -t 0.001 -s 350x240 test.jpg -ss后跟的时间单位为秒

转换文件为3GP格式 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或 ffmpeg -y -i test.wmv -ac 1 -acodec libamr_nb -ar 8000 -ab 12200 -s 176x144 -b 128 -r 15 test.3gp

视频格式转换 如何使用 ffmpeg 编码得到高质量的视频 ffmpeg.exe -i "D:\Video\Fearless\Fearless.avi" -target film-dvd -s 720x352 -padtop 64 -padbottom 64 -maxrate 7350000 -b 3700000 -sc_threshold 1000000000 -trellis -cgop -g 12 -bf 2 -qblur 0.3 -qcomp 0.7 -me full -dc 10 -mbd 2 -aspect 16:9 -pass 2 -passlogfile "D:\Video\ffmpegencode" -an -f mpeg2video "D:\Fearless.m2v"

转换指定格式文件格式 ffmpeg.exe -i test.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv ffmpeg.exe -i test.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv

转码解密的VOB ffmpeg -i snatch_1.vob -f avi -vcodec mpeg4 -b 800 -g 300 -bf 2 -acodec mp3 -ab 128 snatch.avi 上面的命令行将vob的文件转化成avi文件,mpeg4的视频和mp3的音频。注意命令中使用了B帧,所以mpeg4流是divx5兼容的。GOP大小是300意味着29.97帧频下每10秒就有INTRA帧。该映射在音频语言的DVD转码时候尤其有用。

同时编码到几种格式并且在输入流和输出流之间建立映射 ffmpeg -i /tmp/a.wav -ab 64 /tmp/a.mp2 -ab 128 /tmp/b.mp2 -map 0:0 -map 0:0 上面的命令行转换一个64Kbits 的a.wav到128kbits的a.mp2 ‘-map file:index’在输出流的顺序上定义了哪一路输入流是用于每一个输出流的。 转换文件为3GP格式 ffmpeg -i test.avi -y -b 20 -s sqcif -r 10 -acodec amr_wb -ab 23.85 -ac 1 -ar 16000 test.3gp 注:如果要转换为3GP格式,则ffmpeg在编译时必须加上–enable-amr_nb –enable-amr_wb.

屏幕录制 使用ffmpeg录像屏幕 ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s 1024x768 ~/test.avi :其中,-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0,-s 1024×768 指录制视频的大小为 1024×768。录制的视频文件为 test.avi,将保存到用户主目录中

如果你只想录制一个应用程序窗口或者桌面上的一个固定区域,那么可以指定偏移位置和区域大小。使用xwininfo -frame命令可以完成查找上述参数。 重新调整视频尺寸大小 ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -i ~/test.avi -s 800×600 ~/test-800-600.avi 注:ffmpeg的屏幕录制功能只能在Linux环境下有效。

视频采集 把摄像头的实时视频录制下来,存储为文件 ffmpeg -f video4linux -s 320*240 -r 10 -i /dev/video0 test.asf

三、ffmepg使用

The generic syntax is:

ffmpeg [[infile options][`-i' infile]]… {[outfile options] outfile}…

As a general rule, options are applied to the next specified file. Therefore, order is important, and you can have the same option on the command line multiple times. Each occurrence is then applied to the next input or output file.

ffmpeg -i input.avi -b 64k output.avi

ffmpeg -r 24 -i input.avi output.avi

ffmpeg -i input.avi -r 24 output.avi

ffmpeg -r 1 -i input.avi -r 24 output.avi

The format option may be needed for raw input files.

By default, FFmpeg tries to convert as losslessly as possible: It uses the same audio and video parameters for the outputs as the one specified for the inputs.

转换文件为3GP格式 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或 ffmpeg -y -i test.mpeg -ac 1 -acodec amr_nb -ar 8000 -s 176x144 -b 128 -r 15 test.3gp

转换指定格式文件到flv格式

ffmpeg.exe -i test.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv ffmpeg.exe -i test.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv


ffmpeg -i F:\tools\ffmpeg.rev10464\test.avi -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 F:\tools\ffmpeg.rev10464\test.flv ffmpeg -i "F:\tools\ffmpeg.rev10464\test.flv" -y -f image2 -ss 8 -t 0.001 -s 350x240 "test.jpg"

ffmpeg.exe -i F:\闪客之家\闪客之歌.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\11.flv ffmpeg -i F:\01.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv 使用-ss参数 作用(time_off set the start time offset),可以从指定时间点开始转换任务。如: 转换文件格式的同时抓缩微图: ffmpeg -i "test.avi" -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg' 对已有flv抓图: ffmpeg -i "test.flv" -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg' -ss后跟的时间单位为秒 Ffmpeg转换命令 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或者 ffmpeg -y -i test.mpeg -ac 1 -acodec amr_nb -ar 8000 -s 176x144 -b 128 -r 15 test.3gp

ffmpeg参数设定解说 -bitexact 使用标准比特率 -vcodec xvid 使用xvid压缩 -s 320x240 指定分辨率 -r 29.97 桢速率(可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97) 画面部分,选其一 -b <比特率> 指定压缩比特率,似乎ffmpeg是自动VBR的,指定了就大概是平均比特率,比如768,1500这样的 就是原来默认项目中有的 -qscale <数值> 以<数值>质量为基础的VBR,取值0.01-255,约小质量越好 -qmin <数值> 设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31 -sameq 使用和源同样的质量 声音部分 -acodec aac 设定声音编码 -ac <数值> 设定声道数,1就是单声道,2就是立体声,转换单声道的TVrip可以用1(节省一半容量),高品质 的DVDrip就可以用2 -ar <采样率> 设定声音采样率,PSP只认24000 -ab <比特率> 设定声音比特率,前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换 君默认比特率都较小,要听到较高品质声音的话建议设到160kbps(80)以上 -vol <百分比> 设定音量,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍 这样,要得到一个高画质音质低容量的MP4的话,首先画面最好不要用固定比特率,而用VBR参数让程序自己去 判断,而音质参数可以在原来的基础上提升一点,听起来要舒服很多,也不会太大(看情况调整

例子:

ffmpeg -y -i "1.avi" -title "Test" -vcodec xvid -s 368x208 -r 29.97 -b 1500 -acodec aac -ac 2 -ar 24000 -ab 128 -vol 200 -f psp -muxvb 768 "1.***" 

解释:以上命令可以在Dos命令行中输入,也可以创建到批处理文件中运行。不过,前提是:要在ffmpeg所在的目录中执行(转换君所在目录下面的cores子目录)。 参数: -y(覆盖输出文件,即如果1.文件已经存在的话,不经提示就覆盖掉了) -i "1.avi"(输入文件是和ffmpeg在同一目录下的1.avi文件,可以自己加路径,改名字) -title "Test"(在PSP中显示的影片的标题) -vcodec xvid(使用XVID编码压缩视频,不能改的) -s 368x208(输出的分辨率为368x208,注意片源一定要是16:9的不然会变形) -r 29.97(帧数,一般就用这个吧) -b 1500(视频数据流量,用-b xxxx的指令则使用固定码率,数字随便改,1500以上没效果;还可以用动态码率如:-qscale 4和-qscale 6,4的质量比6高) -acodec aac(音频编码用AAC) -ac 2(声道数1或2) -ar 24000(声音的采样频率,好像PSP只能支持24000Hz) -ab 128(音频数据流量,一般选择32、64、96、128) -vol 200(200%的音量,自己改) -f psp(输出psp专用格式) -muxvb 768(好像是给PSP机器识别的码率,一般选择384、512和768,我改成1500,PSP就说文件损坏了) "1."(输出文件名,也可以加路径改文件名) 机器强劲的话,可以多开几个批处理文件,让它们并行处理。

E:\ffmpeg.exe -i I:\1.wmv -b 360 -r 25 -s 320x240 -hq -deinterlace -ab 56 -ar 22050 -ac 1 D:\2.flv

=========================================== ffmpeg.exe -i F:\闪客之家\闪客之歌.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\11.flv ffmpeg -i F:\01.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv 使用-ss参数 作用(time_off set the start time offset),可以从指定时间点开始转换任务。如: 转换文件格式的同时抓缩微图: ffmpeg -i "test.avi" -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg' 对已有flv抓图: ffmpeg -i "test.flv" -y -f image2 -ss 8 -t 0.001 -s 350x240 'test.jpg' -ss后跟的时间单位为秒 Ffmpeg转换命令 ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp 或者 ffmpeg -y -i test.mpeg -ac 1 -acodec amr_nb -ar 8000 -s 176x144 -b 128 -r 15 test.3gp ffmpeg参数设定解说 -bitexact 使用标准比特率 -vcodec xvid 使用xvid压缩 -s 320x240 指定分辨率 -r 29.97 桢速率(可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97)

画面部分,选其一 -b <比特率> 指定压缩比特率,似乎ffmpeg是自动VBR的,指定了就大概是平均比特率,比如768,1500这样的就是原来默认项目中有的 -qscale <数值> 以<数值>质量为基础的VBR,取值0.01-255,约小质量越好 -qmin <数值> 设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31 -sameq 使用和源同样的质量 声音部分 -acodec aac 设定声音编码 -ac <数值> 设定声道数,1就是单声道,2就是立体声,转换单声道的TVrip可以用1(节省一半容量),高品质的DVDrip就可以用2 -ar <采样率> 设定声音采样率,PSP只认24000 -ab <比特率> 设定声音比特率,前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换君默认比特率都较小,要听到较高品质声音的话建议设到160kbps(80)以上 -vol <百分比> 设定音量,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍 这样,要得到一个高画质音质低容量的MP4的话,首先画面最好不要用固定比特率,而用VBR参数让程序自己去判断,而音质参数可以在原来的基础上提升一点,听起来要舒服很多,也不会太大(看情况调整 例子:ffmpeg -y -i "1.avi" -title "Test" -vcodec xvid -s 368x208 -r 29.97 -b 1500 -acodec aac -ac 2 -ar 24000 -ab 128 -vol 200 -f psp -muxvb 768 "1.***"

解释:以上命令可以在Dos命令行中输入,也可以创建到批处理文件中运行。不过,前提是:要在ffmpeg所在的目录中执行(转换君所在目录下面的cores子目录)。 参数: -y(覆盖输出文件,即如果1.文件已经存在的话,不经提示就覆盖掉了) -i "1.avi"(输入文件是和ffmpeg在同一目录下的1.avi文件,可以自己加路径,改名字) -title "Test"(在PSP中显示的影片的标题) -vcodec xvid(使用XVID编码压缩视频,不能改的) -s 368x208(输出的分辨率为368x208,注意片源一定要是16:9的不然会变形) -r 29.97(帧数,一般就用这个吧) -b 1500(视频数据流量,用-b xxxx的指令则使用固定码率,数字随便改,1500以上没效果;还可以用动态码率如:-qscale 4和-qscale 6,4的质量比6高) -acodec aac(音频编码用AAC) -ac 2(声道数1或2) -ar 24000(声音的采样频率,好像PSP只能支持24000Hz) -ab 128(音频数据流量,一般选择32、64、96、128) -vol 200(200%的音量,自己改) -f psp(输出psp专用格式) -muxvb 768(好像是给PSP机器识别的码率,一般选择384、512和768,我改成1500,PSP就说文件损坏了) "1."(输出文件名,也可以加路径改文件名)

P.S. 版主机器强劲的话,可以多开几个批处理文件,让它们并行处理。 E:\ffmpeg.exe -i I:\1.wmv -b 360 -r 25 -s 320x240 -hq -deinterlace -ab 56 -ar 22050 -ac 附件: ffmpeg-20140515-git-09cd228-win32-shared.rar ( 11.33 MB ,16 downs )
附件: FFMPEG命令详解.docx ( 89.43 KB ,46 downs ) 预览 标签: 未定义标签 分类:工具应用

vieyahn2017 commented 5 years ago

gstreamer,vlc,ffmpeg比较

https://blog.csdn.net/rocvfx/article/details/51577322 2016年06月03日 11:04:40 rocvfx 阅读数:10074 个人分类: Gstreamer 转自http://www.tiege.me/?p=312

gstreamer,vlc,ffmpeg都是开源处理流媒体的软件,这里的比较不涉及功能,仅仅比较源代码。

大约看了一个星期,对这三个 软件都略有了解,简单的谈谈。

0、开发语言

都是使用c语言开发,ffmpeg最为简洁;gstreamer使用了gobject,晦涩;

1、注释

vlc的代码完全没有注释,官网也没有任何文档;

gstreamer的官网上有开发者指引,看完后大概了解gstreamer的设计框架,如element,pad,filter等概念,感觉设计的相当不错;

ffmpeg官网上文档不多,不过注释真是太详细了,超级赞;

2、代码量

vlc代码量算比较少的;

gstreamer分为core,plugin,等等,代码量巨大;而且,gstreamer使用glib2,感觉很不好,可读性差;

ffmpeg文件很多,但组织的很棒,一下子都能找到要领;

代码量没有精确统计,仅仅是个人感觉;

3、代码质量

我个人非常欣赏ffmpeg,代码太棒了;

整个框架结构分为两个主要部分,一个是codec,一个是format,在两个大的目录中,顾名思义,一个是处理编解码,一个是获取原始数据;

format中,只需要看几个文件就可以了解整个系统的运作过程了:

avformat.h,avio.h,avio.c等

4、图形界面

vlc有图形界面,在windows下很好用;

gstreamer不是很清楚;

ffmpeg没有图形界面,仅仅提供了三个命令行工具,这三个工具的选项狂多。三个命令分别是ffmpeg,ffplay,ffprobe

ffmpeg是我非常推崇的代码。

在使用测试的过程中,发现ffmpeg不能播放helix server的rtsp源,应该是ffmpeg解析rtp与helix不兼容造成的,有空的时候查找一下原因,fix一下。

vieyahn2017 commented 5 years ago

FFmpeg使用小结

2018年01月25日 15:09:30 FromNowOnUntilTheEnd 阅读数:22321 标签: FFmpeg 个人分类: FFmpeg解码器 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36688143/article/details/79162121

接下来进入正文:

视频播放器的原理

封装格式

作用:视频码流和音频码流按照一定的格式储存在一个文件汇总

视频编码数据

作用:将视频像素数据(RGB,YUV等)压缩成为视频码流,从而降低视频的数据量

音频编码数据

作用:将音频采样数据(PCM等)压缩成为音频码流,从而降低音频的数据量

视频像素数据

作用:保存了屏幕上每一个像素点的像素值

格式:常见的像素数据格式有RGB24, RGB32, YUV420P,YUV422P,YUV444P等。压缩编码中一般使用的是YUV格式的像素数据,最为常见的格式为YUV420P。

特点:视频像素数据体积很大,一般情况下一小时高清视频的RGB24格式的数据体积为:360025192010803=559.9GB(PS:这里假定帧率为25HZ,取样精度8bit)

音频采样数据

作用:保存了音频中每个采样点的值。

特点:音频采样数据体积很大,一般情况下一首4分钟的PCM格式的 歌曲体积为: 4604410022=42.3MByte PS:这里假定采样率为44100Hz,采样精度为16bit

1 术语: 什么是影片?其实就是一组(很多张)图片,时间间隔很小的连续展示出来,人们就觉得画面中的人物在动,这就是影片。那电影的实质就是N多张图片的集合。那 每张图片和帧又有什么关系呢?事实上,如果一部影片里面的图片,我们原封不动的全部存起来,空间会很大很大很大,但是如果通过一定的算法(这里不讲相关算 法),把每一张图片压缩(编码_encode)一下,变成 帧。再把帧连起来变成流,再把不同的流放到某个容器里面,这就是我们平常看见的电影文件了,文件 碟中谍4.H264.ACC.mkv,他为什么要这样命名呢? mkv表达了它的容器是.mkv的,且包含至少两个流,h264的视频流,ACC的音频流。这是一种典型的 牺牲时间来换取空间的做法。

容器(Container)——容器就是一种文件格式,比如flv,mkv等。包含下面5种流以及文件头信息。

流(Stream)——是一种视频数据信息的传输方式,5种流:音频,视频,字幕,附件,数据。

帧(Frame)——帧代表一幅静止的图像,分为I帧,P帧,B帧。

编解码器(Codec)——是对视频进行压缩或者解压缩,CODEC =COde (编码) +DECode(解码)

复用/解复用(mux/demux)——把不同的流按照某种容器的规则放入容器,这种行为叫做复用(mux)

                           把不同的流从某种容器中解析出来,这种行为叫做解复用(demux)

附1:I帧,P帧,B帧 (http://blog.csdn.net/abcjennifer/article/details/6577934

视频压缩中,每帧代表一幅静止的图像。而在实际压缩时,会采取各种算法减少数据的容量,其中IPB就是最常见的。

(1)I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)

(2)P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)

(3)B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累。 从上面的解释看,我们知道I和P的解码算法比较简单,资源占用也比较少,I只要自己完成就行了,P呢,也只需要解码器把前一个画面缓存一下,遇到P时就使用之前缓存的画面就好了,如果视频流只有I和P,解码器可以不管后面的数据,边读边解码,线性前进,大家很舒服。但网络上的电影很多都采用了B帧,因为B帧记录的是前后帧的差别,比P帧能节约更多的空间,但这样一来,文件小了,解码器就麻烦了,因为在解码时,不仅要 用之前缓存的画面,还要知道下一个I或者P的画面(也就是说要预读预解码),而且,B帧不能简单地丢掉,因为B帧其实也包含了画面信息,如果简单丢掉,并 用之前的画面简单重复,就会造成画面卡(其实就是丢帧了),并且由于网络上的电影为了节约空间,往往使用相当多的B帧,B帧用的多,对不支持B帧的播放器 就造成更大的困扰,画面也就越卡。 一般平均来说,I的压缩率是7(跟JPG差不多),P是20,B可以达到50,可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。

附2: 编解码过程

  1. 注册所有容器格式和CODEC:av_register_all()

  2. 打开文件:av_open_input_file()

  3. 从文件中提取流信息:av_find_stream_info()

  4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO

  5. 查找对应的解码器:avcodec_find_decoder()

  6. 打开编解码器:avcodec_open()

  7. 为解码帧分配内存:avcodec_alloc_frame()

  8. 不停地从码流中提取出帧数据:av_read_frame()

  9. 判断帧的类型,对于视频帧调用:avcodec_decode_video()

  10. 解码完后,释放解码器:avcodec_close()

  11. 关闭输入文件:av_close_input_file()

  12. 简介(http://derekzhan.iteye.com/blog/1989274

资料

FFmpeg官网: http://www.ffmpeg.org

FFmpeg doc : http://www.ffmpeg.org/documentation.html

FFmpeg wiki : https://trac.ffmpeg.org/wiki

FFmpeg基础: http://wenku.baidu.com/view/296eefcaf90f76c661371af1.html

FFmpeg的名称来自MPEG视频编码标准,前面的“FF”代表“Fast Forward”,FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。可以轻易地实现多种视频格式之间的相互转换。FFmpeg的用户有Google,Facebook,Youtube,优酷,爱奇艺,土豆等。

组成

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

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

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

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

5、libpostproc:用于后期效果处理;

6、ffmpeg:是一个命令行工具,用来对视频文件转换格式,也支持对电视卡实时编码;

7、ffsever:是一个HTTP多媒体实时广播流服务器,支持时光平移;

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

2.1 过滤器(Filter)

在多媒体处理中,filter的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具。如:视频翻转,旋转,缩放等。

语法:[input_link_label1][input_link_label2]… filter_name=parameters [output_link_label1][output_link_label2]…

过滤器图link label :是标记过滤器的输入或输出的名称

(1).视频过滤器 -vf

如testsrc视频按顺时针方向旋转90度ffplay -f lavfi -i testsrc -vf transpose=1

如testsrc视频水平翻转(左右翻转)ffplay -f lavfi -i testsrc -vf hflip

(2).音频过滤器 -af

实现慢速播放,声音速度是原始速度的50%ffplay p629100.mp3 -af atempo=0.5

(3)如何实现顺时针旋转90度并水平翻转?

过滤器链(Filterchain)

基本语法 Filterchain = 逗号分隔的一组filter

语法:“filter1,filter2,filter3,…filterN-2,filterN-1,filterN”

顺时针旋转90度并水平翻转 ffplay -f lavfi -i testsrc -vf transpose=1,hflip

(4)如何实现水平翻转视频和源视频进行比较?

方法一: 过滤器链(Filterchain)

第一步: 源视频宽度扩大两倍ffmpeg -i jidu.mp4 -t 10 -vf pad=2*iw output.mp4

第二步:源视频水平翻转ffmpeg -i jidu.mp4 -t 10 -vf hflip output2.mp4

第三步:水平翻转视频覆盖output.mp4ffmpeg -i output.mp4 -i output2.mp4 -filter_complex overlay=w compare.mp4

方法二:过滤器图(Filtergraph)

基本语法 Filtergraph = 分号分隔的一组filterchain

“filterchain1;filterchain2;…filterchainN-1;filterchainN”

用ffplay直接观看结果:fplay -f lavfi -i testsrc -vf split[a][b];[a]pad=2*iw[1];[b]hflip[2];[1][2]overlay=w

F1: split过滤器创建两个输入文件的拷贝并标记为[a],[b]

F2: [a]作为pad过滤器的输入,pad过滤器产生2倍宽度并输出到[1].

F3: [b]作为hflip过滤器的输入,vflip过滤器水平翻转视频并输出到[2].

F4: 用overlay过滤器把 [2]覆盖到[1]的旁边.

2.2 选择媒体流

一些多媒体容器比如AVI,mkv,mp4等,可以包含不同种类的多个流,如何从容器中抽取各种流呢?

语法:-map file_number:stream_type[:stream_number]

这有一些特别流符号的说明:

1、-map 0 选择第一个文件的所有流

2、-map i:v 从文件序号i(index)中获取所有视频流, -map i:a 获取所有音频流,-map i:s 获取所有字幕流等等。

3、特殊参数-an,-vn,-sn分别排除所有的音频,视频,字幕流。

注意:文件序号和流序号从0开始计数。

2.3 查看帮助

可用的bit流 :ffmpeg –bsfs

可用的编解码器:ffmpeg –codecs

可用的解码器:ffmpeg –decoders

可用的编码器:ffmpeg –encoders

可用的过滤器:ffmpeg –filters

可用的视频格式:ffmpeg –formats

可用的声道布局:ffmpeg –layouts

可用的license:ffmpeg –L

可用的像素格式:ffmpeg –pix_fmts

可用的协议:ffmpeg -protocals

2.4 码率、帧率和文件大小

码率和帧率是视频文件的最重要的基本特征,对于他们的特有设置会决定视频质量。如果我们知道码率和时长那么可以很容易计算出输出文件的大小。

帧率:帧率也叫帧频率,帧率是视频文件中每一秒的帧数,肉眼想看到连续移动图像至少需要15帧。 fps

码率:比特率(也叫码率,数据率)是一个确定整体视频/音频质量的参数,秒为单位处理的字节数,码率和视频质量成正比,在视频文件中中比特率用bps来表达

帧率

1、用 -r 参数设置帧率 (fps 每秒传输帧数(Frames Per Second))

ffmpeg –i input –r fps output

2、用fps filter设置帧率

ffmpeg -i clip.mpg -vf fps=fps=25 clip.webm

例如设置帧率为29.97fps,下面三种方式具有相同的结果:

ffmpeg -i input.avi -r 29.97 output.mpg

ffmpeg -i input.avi -r 30000/1001 output.mpg

ffmpeg -i input.avi -r netsc output.mpg

码率、文件大小

设置码率 –b 参数ffmpeg -i film.avi -b 1.5M film.mp4

音频:-b:a 视频: - b:v设置视频码率为1500kbpsffmpeg -i input.avi -b:v 1500k output.mp4

控制输出文件大小

-fs (file size首字母缩写) ffmpeg -i input.avi -fs 1024K output.mp4

计算输出文件大小(视频码率+音频码率) * 时长 /8 = 文件大小K

  1. 调整视频分辨率

3.1 调整视频分辨率

用-s参数设置视频分辨率,参数值wxh,w宽度单位是像素,h高度单位是像素ffmpeg -i input_file -s 320x240 output_file

3.2 预定义的视频尺寸

下面两条命令有相同效果

ffmpeg -i input.avi -s 640x480 output.avi

ffmpeg -i input.avi -s vga output.avi

Scale filter调整分辨率 ,Scale filter的优点是可以使用一些额外的参数 , 语法: Scale=width:height[:interl={1|-1}]

下面两条命令有相同效果

ffmpeg -i input.mpg -s 320x240 output.mp4

ffmpeg -i input.mpg -vf scale=320:240 output.mp4

对输入视频成比例缩放

改变为源视频一半大小   ffmpeg -i input.mpg -vf scale=iw/2:ih/2 output.mp4

改变为原视频的90%大小 ffmpeg -i input.mpg -vf scale=iw0.9:ih0.9 output.mp4

在未知视频的分辨率时,保证调整的分辨率与源视频有相同的横纵比。

例如宽度固定400,高度成比例:

ffmpeg -i input.avi -vf scale=400:400/a

ffmpeg -i input.avi -vf scale=400:-1

相反地,高度固定300,宽度成比例:

ffmpeg -i input.avi -vf scale=-1:300

ffmpeg -i input.avi -vf scale=300*a:300

  1. 裁剪/填充视频

4.1 裁剪视频crop filter

从输入文件中选取你想要的矩形区域到输出文件中,常见用来去视频黑边。 语法:crop:ow[:oh[:x[:y:[:keep_aspect]]]]

裁剪输入视频的左三分之一,中间三分之一,右三分之一:

ffmpeg -i input -vf crop=iw/3:ih :0:0 output

ffmpeg -i input -vf crop=iw/3:ih :iw/3:0 output

ffmpeg -i input -vf crop=iw/3:ih :iw/3*2:0 output

裁剪帧的中心, 当我们想裁剪区域在帧的中间时,裁剪filter可以跳过输入x和y值,他们的默认值是

Xdefault = ( input width - output width)/2 , Ydefault = ( input height - output height)/2

ffmpeg -i input_file -v crop=w:h output_file

裁剪中间一半区域: ffmpeg -i input.avi -vf crop=iw/2:ih/2 output.avi

比较裁剪后的视频和源视频比较

ffplay -i jidu.mp4 -vf split[a][b];[a]drawbox=x=(iw-300)/2:(ih-300)/2:w=300:h=300:c=yellow[A];[A]pad=2*iw[C];[b]crop=300:300:(iw-300)/2:(ih-300)/2[B];

[C][B]overlay=w*2.4:40

自动检测裁剪区域 , cropdetect filter 自动检测黑边区域

ffplay jidu.mp4 -vf cropdetect

然后用检测到的值来裁剪视频

ffplay jidu.mp4 –vf crop=672:272:0:54

填充视频(pad) , 在视频帧上增加一快额外额区域,经常用在播放的时候显示不同的横纵比语法:pad=width[:height:[:x[:y:[:color]]]]

创建一个30个像素的粉色宽度来包围一个SVGA尺寸的图片: ffmpeg -i photo.jpg -vf pad=860:660:30:30:pink framed_photo.jpg

同理可以制作testsrc视频用30个像素粉色包围视频:ffplay -f lavfi -i testsrc -vf pad=iw+60:ih+60:30:30:pink

4:3到16:9---------- 一些设备只能播放16:9的横纵比,4:3的横纵比必须在水平方向的两边填充成16:9,高度被保持,宽度等于高度乘以16/9,x(输入文件水平位移)值由表达式(output_width - input_width)/2来计算。

4:3到16:9的通用命令是:ffmpeg -i input -vf pad=ih*16/9:ih :(ow-iw)/2:0:color output

举例: ffplay -f lavfi -i testsrc -vf pad=ih*16/9:ih:(ow-iw)/2:0:pink

16:9到4:3----------为了用4:3的横纵比来显示16:9的横纵比,填充输入文件的垂直两边,宽度保持不变,高度是宽度的3/4,y值(输入文件的垂直偏移量)是由一个表达式(output_height-input_height)/2计算出来的。

16:9到4:3的通用命令:ffmpeg -i input -vf pad=iw :iw*3/4:0:(oh-ih)/2:color output

举例:ffplay -f lavfi -i testsrc=size=320x180 -vf pad=iw:iw*3/4:0:(oh-ih)/2:pink

  1. 翻转和旋转

翻转

水平翻转语法: -vf hflip: ffplay -f lavfi -i testsrc -vf hflip

垂直翻转语法:-vf vflip:ffplay -f lavfi -i testsrc -vf vflip

旋转

语法:transpose={0,1,2,3}

0:逆时针旋转90°然后垂直翻转

1:顺时针旋转90°

2:逆时针旋转90°

3:顺时针旋转90°然后水平翻转

  1. 模糊,锐化

模糊, 语法:boxblur=luma_r:luma_p[:chroma_r:chram_p[:alpha_r:alpha_p]] , fplay -f lavfi -i testsrc -vf boxblur=1:10:4:10

注意:luma_r和alpha_r半径取值范围是0~min(w,h)/2, chroma_r半径的取值范围是0~min(cw/ch)/2

锐化, 语法:-vf unsharp=l_msize_x:l_msize_y:l_amount:c_msize_x:c_msize_y:c_amount, 所有的参数是可选的,默认值是5:5:1.0:5:5:0.0

l_msize_x:水平亮度矩阵,取值范围3-13,默认值为5

l_msize_y:垂直亮度矩阵,取值范围3-13,默认值为5

l_amount:亮度强度,取值范围-2.0-5.0,负数为模糊效果,默认值1.0

c_msize_x:水平色彩矩阵,取值范围3-13,默认值5

c_msize_y:垂直色彩矩阵,取值范围3-13,默认值5

c_amount:色彩强度,取值范围-2.0-5.0,负数为模糊效果,默认值0.0

举例

使用默认值,亮度矩阵为5x5和亮度值为1.0ffmpeg -i input -vf unsharp output.mp4

高斯模糊效果(比较强的模糊):ffplay -f lavfi -i testsrc -vf unsharp=13:13:-2

  1. 覆盖(画中画)

覆盖, 语法:overlay[=x[:y], 所有的参数都是可选,默认值都是0

举例

Logo在左上角:ffmpeg -i pair.mp4 -i logo.png -filter_complex overlay pair1.mp4

右上角: ffmpeg -i pair.mp4 -i logo.png -filter_complex overlay=W-w pair2.mp4

左下角: ffmpeg -i pair.mp4 -i logo.png -filter_complex overlay=0:H-h pair2.mp4

右下角: ffmpeg -i pair.mp4 -i logo.png -filter_complex overlay=W-w:H-h pair2.mp4

删除logo,语法:-vf delogo=x:y:w:h[:t[:show]]

x:y 离左上角的坐标

w:h logo的宽和高

t: 矩形边缘的厚度默认值4

show:若设置为1有一个绿色的矩形,默认值0.

ffplay -i jidu.mp4 -vf delogo=50:51:60:60:100:0

  1. 添加文本

语法:drawtext=fontfile=font_f:text=text1[:p3=v3[:p4=v4[…]]]

常用的参数值

x:离左上角的横坐标y: 离左上角的纵坐标fontcolor:字体颜色fontsize:字体大小text:文本内容

textfile:文本文件t:时间戳,单位秒n:帧数开始位置为0draw/enable:控制文件显示,若值为0不显示,1显示,可以使用函数

简单用法

1、在左上角添加Welcome文字

ffplay -f lavfi -i color=c=white -vf drawtext=fontfile=arial.ttf:text=Welcom

2、在中央添加Good day

ffplay -f lavfi -i color=c=white -vf drawtext="fontfile=arial.ttf:text='Goodday':x=(w-tw)/2:y=(h-th)/2"

3、设置字体颜色和大小

ffplay -f lavfi -i color=c=white -vf drawtext="fontfile=arial.ttf:text='Happy Holidays':x=(w-tw)/2:y=(h-th)/2:fontcolor=green:fontsize=30"

动态文本

用 t (时间秒)变量实现动态文本

1、顶部水平滚动

ffplay -i jidu.mp4 -vf drawtext="fontfile=arial.ttf:text='Dynamic RTL text':x=w-t*50:fontcolor=darkorange:fontsize=30"

2、底部水平滚动

ffplay -i jidu.mp4 -vf drawtext="fontfile=arial.ttf:textfile=textfile.txt:x=w-t*50:y=h-th:fontcolor=darkorange:fontsize=30"

3、垂直从下往上滚动

ffplay jidu.mp4 -vf drawtext="textfile=textfile:fontfile=arial.ttf:x=(w-tw)/2:y=h-t*100:fontcolor=white:fontsize=30“

4.实现右上角显示当前时间?

动态文本

在右上角显示当前时间 localtime

ffplay jidu.mp4 -vf drawtext="fontfile=arial.ttf:x=w-tw:fontcolor=white:fontsize=30:text='%{localtime\:%H\\:%M\\:%S}'“

每隔3秒显示一次当前时间

ffplay jidu.mp4 -vf drawtext="fontfile=arial.ttf:x=w-tw:fontcolor=white:fontsize=30:text='%{localtime\:%H\\:%M\\:%S}':enable=lt(mod(t\,3)\,1)"

  1. 图片处理

图片支持: FFmpeg支持绝大多数图片处理, 除LJPEG(无损JPEG)之外,其他都能被解码,除了EXR,PIC,PTX之外,所有的都能被编码。

截取一张图片使用 –ss(seek from start)参数:ffmpeg -ss 01:23:45 -i jidu.mp4 image.jpg

从视频中生成GIF图片:ffmpeg -i jidu.mp4 -t 10 -pix_fmt rgb24 jidu.gif

转换视频为图片(每帧一张图):ffmpeg -i clip.avi frame%4d.jpg

图片转换为视频:ffmpeg -f image2 -i img%4d.jpg -r 25 video.mp4

裁剪:ffmpeg -f lavfi -i rgbtestsrc -vf crop=150:150 crop_rg.png

填充:ffmpeg -f lavfi -i smptebars -vf pad=360:280:20:20:orange pad_smpte.jpg

翻转:ffmpeg -i orange.jpg -vf hflip orange_hfilp.jpg

ffmpeg -i orange.jpg -vf vflip orange_vfilp.jpg

旋转:ffmpeg -i image.png -vf transpose=1 image_rotated.png

覆盖:ffmpeg -f lavfi -i rgbtestsrc -s 400x300 rgb .png

ffmpeg -f lavfi -i smptebars smpte.png

ffmpeg -i rgb .png -i smpte.png -filter_complex overlay= (W-w)/2:(H-h)/2 rgb_smpte.png

10.其他高级技巧

屏幕录像

显示设备名称:ffmpeg -list_devices 1 -f dshow -i dummy

调用摄像头: ffplay -f dshow -i video="Integrated Camera"

保存为文件: ffmpeg -y -f dshow -s 320x240 -r 25 -i video="Integrated Camera" -b:v 800K -vcodec mpeg4 new.mp4

添加字幕subtitles:语法 –vf subtitles=file, ffmpeg -i jidu.mp4 -vf subtitles=rgb.srt output.mp4

视频颤抖:ffplay –i jidu.mp4 -vf crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)sin(n/10):(in_h-out_h)/2 +((in_h-out_h)/2)sin(n/7)

色彩平衡:ffplay -i jidu.mp4 -vf curves=vintage

色彩变幻:fplay -i jidu.mp4 -vf hue="H=2PIt: s=sin(2PIt)+1“

彩色转换黑白:ffplay -i jidu.mp4 -vf lutyuv="u=128:v=128"

设置音频视频播放速度:

3倍视频播放视频: ffplay -i jidu.mp4 -vf setpts=PTS/3

?速度播放视频: ffplay -i jidu.mp4 -vf setpts=PTS/(3/4)

2倍速度播放音频:ffplay -i speech.mp3 -af atempo=2

截图

每隔一秒截一张图:ffmpeg -i input.flv -f image2 -vf fps=fps=1 out%d.png

每隔20秒截一张图:ffmpeg -i input.flv -f image2 -vf fps=fps=1/20 out%d.png

多张截图合并到一个文件里(2x3) ?每隔一千帧(秒数=1000/fps25)即40s截一张图

ffmpeg? -i jidu.mp4 -frames 3 -vf "select=not(mod(n\,1000)),scale=320:240,tile=2x3" out.png

马赛克视频

用多个输入文件创建一个马赛克视频:

ffmpeg -i jidu.mp4 -i jidu.flv -i "Day By Day SBS.mp4" -i "Dangerous.mp4" -filter_complex "nullsrc=size=640x480 [base]; [0:v] setpts=PTS-STARTPTS, scale=320x240 [upperleft]; [1:v] setpts=PTS-STARTPTS, scale=320x240 [upperright]; [2:v] setpts=PTS-STARTPTS, scale=320x240 [lowerleft]; [3:v] setpts=PTS-STARTPTS, scale=320x240 [lowerright]; [base][upperleft] overlay=shortest=1 [tmp1]; [tmp1][upperright] overlay=shortest=1:x=320 [tmp2]; [tmp2][lowerleft] overlay=shortest=1:y=240 [tmp3]; [tmp3][lowerright] overlay=shortest=1:x=320:y=240" -c:v libx264 output.mkv

Logo动态移动

2秒后logo从左到右移动:ffplay -i jidu.mp4 -vf movie=logo.png[logo];[in][logo]overlay=x='if(gte(t\,2)\,((t-2)*80)-w\,NAN)':y=0

2秒后logo从左到右移动后停止在左上角:ffplay -i jidu.mp4 -vf movie=logo.png[logo];[in][logo]overlay=x='if(gte(((t-2)80)-w\,W)\,0\,((t-2)80)-w)':y=0

每隔10秒交替出现logo: ffmpeg -y -t 60 -i jidu.mp4 -i logo.png -i logo2.png -filter_complex "overlay=x=if(lt(mod(t\,20)\,10)\,10\,NAN ):y=10,overlay=x=if(gt(mod(t\,20)\,10)\,W-w-10\,NAN ) :y=10" overlay.mp4

感谢这两篇文档的作者:

http://blog.csdn.net/coloriy/article/details/47337729

http://blog.csdn.net/Bobsweetie/article/details/50935109

vieyahn2017 commented 5 years ago

ffmpeg参数中文详细解释 日期:2014-09-20 16:49浏览:68评论:0 http://blog.csdn.net/leixiaohua1020/article/details/12751349 a) 通用选项

-L license -h 帮助 -fromats 显示可用的格式,编解码的,协议的... -f fmt 强迫采用格式fmt -I filename 输入文件 -y 覆盖输出文件 -t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持 -ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持 -title string 设置标题 -author string 设置作者 -copyright string 设置版权 -comment string 设置评论 -target type 设置目标文件类型(vcd,svcd,dvd) 所有的格式选项(比特率,编解码以及缓冲区大小)自动设置,只需要输入如下的就可以了:ffmpeg -i myfile.avi -target vcd /tmp/vcd.mpg -hq 激活高质量设置 -itsoffset offset 设置以秒为基准的时间偏移,该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset秒。 [-]hh:mm:ss[.xxx]的格式也支持

b) 视频选项

-b bitrate 设置比特率,缺省200kb/s -r fps 设置帧频 缺省25 -s size 设置帧大小 格式为WXH 缺省160X128.下面的简写也可以直接使用: Sqcif 128X96 qcif 176X144 cif 252X288 4cif 704X576 -aspect aspect 设置横纵比 4:3 16:9 或 1.3333 1.7777 -croptop size 设置顶部切除带大小 像素单位 -cropbottom size –cropleft size –cropright size -padtop size 设置顶部补齐的大小 像素单位 -padbottom size –padleft size –padright size –padcolor color 设置补齐条颜色(hex,6个16进制的数,红:绿:兰排列,比如 000000代表黑色) -vn 不做视频记录 -bt tolerance 设置视频码率容忍度kbit/s -maxrate bitrate设置最大视频码率容忍度 -minrate bitreate 设置最小视频码率容忍度 -bufsize size 设置码率控制缓冲区大小 -vcodec codec 强制使用codec编解码方式。如果用copy表示原始编解码数据必须被拷贝。 -sameq 使用同样视频质量作为源(VBR) -pass n 选择处理遍数(1或者2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率 -passlogfile file 选择两遍的纪录文件名为file

c)高级视频选项

-g gop_size 设置图像组大小 -intra 仅适用帧内编码 -qscale q 使用固定的视频量化标度(VBR) -qmin q 最小视频量化标度(VBR) -qmax q 最大视频量化标度(VBR) -qdiff q 量化标度间最大偏差 (VBR) -qblur blur 视频量化标度柔化(VBR) -qcomp compression 视频量化标度压缩(VBR) -rc_init_cplx complexity 一遍编码的初始复杂度 -b_qfactor factor 在p和b帧间的qp因子 -i_qfactor factor 在p和i帧间的qp因子 -b_qoffset offset 在p和b帧间的qp偏差 -i_qoffset offset 在p和i帧间的qp偏差 -rc_eq equation 设置码率控制方程 默认tex^qComp -rc_override override 特定间隔下的速率控制重载 -me method 设置运动估计的方法 可用方法有 zero phods log x1 epzs(缺省) full -dct_algo algo 设置dct的算法 可用的有 0 FF_DCT_AUTO 缺省的DCT 1 FF_DCT_FASTINT 2 FF_DCT_INT 3 FF_DCT_MMX 4 FF_DCT_MLIB 5 FF_DCT_ALTIVEC -idct_algo algo 设置idct算法。可用的有 0 FF_IDCT_AUTO 缺省的IDCT 1 FF_IDCT_INT 2 FF_IDCT_SIMPLE 3 FF_IDCT_SIMPLEMMX 4 FF_IDCT_LIBMPEG2MMX 5 FF_IDCT_PS2 6 FF_IDCT_MLIB 7 FF_IDCT_ARM 8 FF_IDCT_ALTIVEC 9 FF_IDCT_SH4 10 FF_IDCT_SIMPLEARM -er n 设置错误残留为n 1 FF_ER_CAREFULL 缺省 2 FF_ER_COMPLIANT 3 FF_ER_AGGRESSIVE 4 FF_ER_VERY_AGGRESSIVE -ec bit_mask 设置错误掩蔽为bit_mask,该值为如下值的位掩码 1 FF_EC_GUESS_MVS (default=enabled) 2 FF_EC_DEBLOCK (default=enabled) -bf frames 使用frames B 帧,支持mpeg1,mpeg2,mpeg4 -mbd mode 宏块决策 0 FF_MB_DECISION_SIMPLE 使用mb_cmp 1 FF_MB_DECISION_BITS 2 FF_MB_DECISION_RD -4mv 使用4个运动矢量 仅用于mpeg4 -part 使用数据划分 仅用于mpeg4 -bug param 绕过没有被自动监测到编码器的问题 -strict strictness 跟标准的严格性 -aic 使能高级帧内编码 h263+ -umv 使能无限运动矢量 h263+ -deinterlace 不采用交织方法 -interlace 强迫交织法编码仅对mpeg2和mpeg4有效。当你的输入是交织的并且你想要保持交织以最小图像损失的时候采用该选项。可选的方法是不交织,但是损失更大 -psnr 计算压缩帧的psnr -vstats 输出视频编码统计到vstats_hhmmss.log -vhook module 插入视频处理模块 module 包括了模块名和参数,用空格分开

D)音频选项

-ab bitrate 设置音频码率 -ar freq 设置音频采样率 -ac channels 设置通道 缺省为1 -an 不使能音频纪录 -acodec codec 使用codec编解码

E)音频/视频捕获选项

-vd device 设置视频捕获设备。比如/dev/video0 -vc channel 设置视频捕获通道 DV1394专用 -tvstd standard 设置电视标准 NTSC PAL(SECAM) -dv1394 设置DV1394捕获 -av device 设置音频设备 比如/dev/dsp

F)高级选项

-map file:stream 设置输入流映射 -debug 打印特定调试信息 -benchmark 为基准测试加入时间 -hex 倾倒每一个输入包 -bitexact 仅使用位精确算法 用于编解码测试 -ps size 设置包大小,以bits为单位 -re 以本地帧频读数据,主要用于模拟捕获设备 -loop 循环输入流(只工作于图像流,用于ffserver测试) 分类:

vieyahn2017 commented 5 years ago

ffmpeg命令机制分析--参数如何被设置
日期:2016-11-28 16:00 浏览:23 回复:0 http://blog.csdn.net/leixiaohua1020/article/details/44279329(结构体成员管理系统-AVOptionhttp://blog.csdn.net/leixiaohua1020/article/details/44268323(结构体成员管理系统-AVClass

需要了解option结构体的定义 ————–简化版

typedef struct AVOption { 
const char *name; 
const char *help; 
int offset; —————–记录偏移量 
enum AVOptionType type; ————-参数类型,根据类型调用不同接口 
union { ——————-一般是保存默认值 
int64_t i64; 
double dbl; 
const char *str; 
AVRational q; 
} default_val; 
double min; ///< minimum valid value for the option 
double max; ///< maximum valid value for the option 
int flags; 
const char *unit; ————–标志是同一类 
} AVOption;

常见的模块里面的命令:以nvenc为例

#define OFFSET(x) offsetof(NvencContext, x)
    { "preset",   "Set the encoding preset",              OFFSET(preset),      AV_OPT_TYPE_INT,    { .i64 = PRESET_MEDIUM }, PRESET_DEFAULT, PRESET_LOSSLESS_HP, VE, "preset" },
    { "default",    "",                                   0,                   AV_OPT_TYPE_CONST,  { .i64 = PRESET_DEFAULT }, 0, 0, VE, "preset" },
    { "slow",       "hq 2 passes",                        0,                   AV_OPT_TYPE_CONST,  { .i64 = PRESET_SLOW }, 0, 0, VE, "preset" },
    ......
    { "medium",     "hq 1 pass",                          0,                   AV_OPT_TYPE_CONST,  { .i64 = PRESET_MEDIUM }, 0, 0, VE, "preset" },
      { "rc",       "Override the preset rate-control",     OFFSET(rc),          AV_OPT_TYPE_INT,    { .i64 = -1 },                   -1, INT_MAX, VE, "rc" },
    { "constqp",          "Constant QP mode",                                                            0, AV_OPT_TYPE_CONST,  { .i64 = NV_ENC_PARAMS_RC_CONSTQP },              0, 0, VE, "rc" },
    ....
{ NULL }

实际上ffmpeg的机制是模块化的,命令会下发到具体一个模块,然后模块会根据这些命令执行相应的操作。那么问题是,这些命令是如何准确下发到模块去?也就是上层是通过什么方式关联到模块的私有数据?这就是命令机制的其中一部分功能。其中利用到了一个关键的结构体AVClass来做一个承接。

总的来说,设置命令的基本思路就是:使用AVClass,关联指定的结构体,通过option数组,经过查找匹配,再利用私有数据成员,把命令值设置。命令的设置关键是通过AVClass中保存了Option,这个option中的一个成员记录的偏移量很重要。以h264_nvenc编码器为例,在上层是这样赋值给私有指针priv

(const AVClass*)s->priv_data = codec->priv_class; 

其中s是结构体AVCodecContext,codec是结构体AVCodec,codec->priv_class是h264_nvenc_class,这个class关键是保存了options信息

根据偏移量,也就是记录NvencContext 每个成员的相对偏移地址,并在这个地址进行赋值操作,然后在具体的底层(插件)代码中:

NvencContext *ctx = avctx->priv_data; ```
这样就确定了NvencContext每个成员 相对的地址的偏移量,每个NvencContext 的数据成员值就确定了

为什么priv_data就是NvencContext想要的内容,这里面还有一个关键的技巧就是用到了(const AVClass*)s这样的指针转换。
这就是为什么NvencContext结构体一个成员是AVClass。几乎很多具有类似继承关系的结构体都有这样的结构体。AVClass可以说是一个管理类型的结构体。

那么,命令的参数是如何被设置上的?这里需要了解命令参数机制,如结构体,类型等

简单地,数值类一般命令有这两种方式–gpu 3 或者–gpu ls。设置命令的接口有几种,下面简单分析av_opt_set
```c
int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
{
    int ret = 0;
    void *dst, *target_obj;
    /*查重匹配命令:里面调用av_opt_next*/
    const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
    if (!o || !target_obj)
        return AVERROR_OPTION_NOT_FOUND;
    if (!val && (o->type != AV_OPT_TYPE_STRING &&
                 o->type != AV_OPT_TYPE_PIXEL_FMT && o->type != AV_OPT_TYPE_SAMPLE_FMT &&
                 o->type != AV_OPT_TYPE_IMAGE_SIZE && o->type != AV_OPT_TYPE_VIDEO_RATE &&
                 o->type != AV_OPT_TYPE_DURATION && o->type != AV_OPT_TYPE_COLOR &&
                 o->type != AV_OPT_TYPE_CHANNEL_LAYOUT && o->type != AV_OPT_TYPE_BOOL))
        return AVERROR(EINVAL);

    if (o->flags & AV_OPT_FLAG_READONLY)
        return AVERROR(EINVAL);

    /*确定偏移位置,转换是为了一个一个字节偏移*/
    dst = ((uint8_t *)target_obj) + o->offset;
    /*根据不同的参数类型调用不同的参数接口*/
    switch (o->type) {
    case AV_OPT_TYPE_BOOL:
        return set_string_bool(obj, o, val, dst);
    case AV_OPT_TYPE_STRING:
        return set_string(obj, o, val, dst);
    case AV_OPT_TYPE_BINARY:
        return set_string_binary(obj, o, val, dst);
    case AV_OPT_TYPE_FLAGS:
    case AV_OPT_TYPE_INT:
    case AV_OPT_TYPE_INT64:
    case AV_OPT_TYPE_FLOAT:
    case AV_OPT_TYPE_DOUBLE:
    case AV_OPT_TYPE_RATIONAL:
        return set_string_number(obj, target_obj, o, val, dst);
    case AV_OPT_TYPE_IMAGE_SIZE:
        return set_string_image_size(obj, o, val, dst);
    case AV_OPT_TYPE_VIDEO_RATE: {
        AVRational tmp;
        ret = set_string_video_rate(obj, o, val, &tmp);
        if (ret < 0)
            return ret;
        return write_number(obj, o, dst, 1, tmp.den, tmp.num);
    }
    case AV_OPT_TYPE_PIXEL_FMT:
        return set_string_pixel_fmt(obj, o, val, dst);
    case AV_OPT_TYPE_SAMPLE_FMT:
        return set_string_sample_fmt(obj, o, val, dst);
    case AV_OPT_TYPE_DURATION:
        if (!val) {
            *(int64_t *)dst = 0;
            return 0;
        } else {
            if ((ret = av_parse_time(dst, val, 1)) < 0)
                av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", val);
            return ret;
        }
        break;
    case AV_OPT_TYPE_COLOR:
        return set_string_color(obj, o, val, dst);
    case AV_OPT_TYPE_CHANNEL_LAYOUT:
        if (!val || !strcmp(val, "none")) {
            *(int64_t *)dst = 0;
        } else {
            int64_t cl = av_get_channel_layout(val);
            if (!cl) {
                av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as channel layout\n", val);
                ret = AVERROR(EINVAL);
            }
            *(int64_t *)dst = cl;
            return ret;
        }
        break;
    }

    av_log(obj, AV_LOG_ERROR, "Invalid option type.\n");
    return AVERROR(EINVAL);
}
const AVOption *av_opt_next(const void *obj, const AVOption *last)
{
    const AVClass *class;
    if (!obj)

    /*建立联系 ——为了拿到结构体第一个成员的地址*/
    /*分析:获取一级指针内容就是对二级指针的解引用 指针本身是一个变量,需要一个地址储存 结构体第一个成员是指针的话,那么这个结构体的首地址就是指向这个成 员。也就是储存这个成员的地址*/
    class = *(const AVClass**)obj;

    if (!last && class && class->option && class->option[0].name)
        return class->option;
    if (last && last[1].name)
        return ++last;
    return NULL;
}
static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst)
{
    int ret = 0;
    int num, den;
    char c;
    /*这里的正则表达式作用还没弄懂,猜测是处理这一类的命令:–gpu 3 */
    if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) == 2) {
        if ((ret = write_number(obj, o, dst, 1, den, num)) >= 0)
            return ret;
        ret = 0;
    }

    for (;;) {
        int i = 0;
        char buf[256];
        int cmd = 0;
        double d;
        int64_t intnum = 1;

        if (o->type == AV_OPT_TYPE_FLAGS) {
            if (*val == '+' || *val == '-')
                cmd = *(val++);
            for (; i < sizeof(buf) - 1 && val[i] && val[i] != '+' && val[i] != '-'; i++)
                buf[i] = val[i];
            buf[i] = 0;
        }

        {
            /*实质上就是引入了unit的处理,将value转换成name,o->unit 为值,做一个中转,继续调用av_opt_find2 */
            const AVOption *o_named = av_opt_find(target_obj, i ? buf : val, o->unit, 0, 0);
            int res;
            int ci = 0;
            double const_values[64];
            const char * const_names[64];
            if (o_named && o_named->type == AV_OPT_TYPE_CONST)
                d = DEFAULT_NUMVAL(o_named);
            else {
                if (o->unit) {
                    for (o_named = NULL; o_named = av_opt_next(target_obj, o_named); ) {
                        if (o_named->type == AV_OPT_TYPE_CONST &&
                            o_named->unit &&
                            !strcmp(o_named->unit, o->unit)) {
                            if (ci + 6 >= FF_ARRAY_ELEMS(const_values)) {
                                av_log(obj, AV_LOG_ERROR, "const_values array too small for %s\n", o->unit);
                                return AVERROR_PATCHWELCOME;
                            }
                            const_names [ci  ] = o_named->name;
                            const_values[ci++] = DEFAULT_NUMVAL(o_named);
                        }
                    }
                }
                const_names [ci  ] = "default";
                const_values[ci++] = DEFAULT_NUMVAL(o);
                const_names [ci  ] = "max";
                const_values[ci++] = o->max;
                const_names [ci  ] = "min";
                const_values[ci++] = o->min;
                const_names [ci  ] = "none";
                const_values[ci++] = 0;
                const_names [ci  ] = "all";
                const_values[ci++] = ~0;
                const_names [ci] = NULL;
                const_values[ci] = 0;

                res = av_expr_parse_and_eval(&d, i ? buf : val, const_names,
                                            const_values, NULL, NULL, NULL, NULL, NULL, 0, obj);
                if (res < 0) {
                    av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\"\n", val);
                    return res;
                }
            }
        }
        if (o->type == AV_OPT_TYPE_FLAGS) {
            read_number(o, dst, NULL, NULL, &intnum);
            if (cmd == '+')
                d = intnum | (int64_t)d;
            else if (cmd == '-')
                d = intnum &~(int64_t)d;
        }

        if ((ret = write_number(obj, o, dst, d, 1, 1)) < 0)
            return ret;
        val += i;
        if (!i || !*val)
            return 0;
    }

    return 0;
}

static int set_string_image_size(void *obj, const AVOption *o, const char *val, int *dst)
{
    int ret;

    if (!val || !strcmp(val, "none")) {
        dst[0] =
        dst[1] = 0;
        return 0;
    }
    ret = av_parse_video_size(dst, dst + 1, val);
    if (ret < 0)
        av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as image size\n", val);
    return ret;
}

static int set_string_video_rate(void *obj, const AVOption *o, const char *val, AVRational *dst)
{
    int ret;
    if (!val) {
        ret = AVERROR(EINVAL);
    } else {
        ret = av_parse_video_rate(dst, val);
    }
    if (ret < 0)
        av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as video rate\n", val);
    return ret;
}

抽离上面的程序,简单总结下面的流程:

av_opt_set———————简单分析av_opt_set-的程序流程 
av_opt_find2 —–查重匹配命令 
av_opt_next ———会在一个循环里被调用,直到遇到NULL 
如果是第一次查找就会返回第一个option 
class = (const AVClass*)obj; ——–建立联系 ——为了拿到结构体第一个成员的地址

dst = ((uint8_t *)target_obj) + o->offset; ——-确定偏移位置,转换是为了一个一个字节偏移 
根据type,调用相应的处理接口,如数值类 
set_string_number(obj, target_obj, o, val, dst); 
如果是-gpu ls这种类型,需要进一步处理, 
具体处理方式: 
(sscanf(val, “%d%*1[:/]%d%c”, &num, &den, &c) ——-正则表达式匹配,如果不是ls这类的,调用write_number 
如果上述条件不满足 
执行av_opt_find(target_obj, i ? buf : val, o->unit, 0, 0);- 
–实质上就是引入了unit的处理,将value转换成name,o->unit为值,做一个中转,继续调用av_opt_find2 
write_number(obj, o, dst, d, 1, 1)) ———赋值

ret = av_opt_set(s->priv_data, key, value, 0)) 
av_opt_set(void *obj, const char *name, const char *val, int search_flags) 
好了,联系上面说的(const AVClass*)s->priv_data = codec->priv_class; 

总体思路:
把一个私有数据s->priv_data传进去,通过转换class = (const AVClass*)obj,
指针转化类型AVClass,这个结构体含有option结构体。
这个option保存具体的偏移量。通过这个option查找匹配命令。
找到命令,确定偏移量,对这个地址赋值操作。
也就是说私有数据保存的是偏移量和这个地址对应的值。

用一个图简单的总结一下:

(const AVClass*)s->priv_data = codec->priv_class; 
NvencContext *ctx = avctx->priv_data; 

这里写图片描述

标签: 未定义标签 分类:互联网