ffmpeg推流器

av_strerror(errorCode, char *buf, int buf_len); //可以用于转换ffmpeg返回的错误码,转换为字符信息``


int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd); //它的作用是计算 "a * b / c" 的值并分五种方式来取整.
用在FFmpeg中,
则是将以 "时钟基c" 表示的 数值a 转换成以 "时钟基b" 来表示
AV_ROUND_ZERO     = 0, // Round toward zero.      趋近于0  
AV_ROUND_INF      = 1, // Round away from zero.   趋远于0  
AV_ROUND_DOWN     = 2, // Round toward -infinity. 趋于更小的整数  
AV_ROUND_UP       = 3, // Round toward +infinity. 趋于更大的整数  
AV_ROUND_NEAR_INF = 5, // Round to nearest and halfway cases away from zero.  
                       //                         四舍五入,小于0.5取值趋向0,大于0.5取值趋远于0  




int av_rescale_q(a,b,c); //返回值是一个很大的值,用于把时间戳从一个时基切换到另一个时基 原理:a*b/c
//一切时间戳都是以时基计算为准
AV_TIME_BASE -- 时基
AV_TIME_BASE_Q -- 时基的倒数


从上可以得到一下公式:
AV_TIME_BASE * time_seconds = timestamp
AV_TIME_BASE_Q * timestamp = time_seconds


现在可以根据pts来计算一桢在整个视频中的时间位置:


timestamp(秒) = pts * av_q2d(st->time_base)


计算视频长度的方法:


time(秒) = st->duration * av_q2d(st->time_base)


P帧  I帧 和 B帧:
I帧: 关键帧,包含了一副图像的完整信息
P帧: 预测帧,根据前后的I帧和P帧预测得到的一副图像
B帧: 双向帧,和P帧有点类似,根据前后的图像预测而得的


AVPacket表示解码前或编码后的数据包,AVFrame表示解码后或编码前的数据包


AVPacket结构体深度解析:
从用途上分:
1. 输入: 视频文件->demux(分解为多个流),其中包含AVPacket,它是压缩的数据 --> decoder解码器解码为可以播放的数据
2. 输出: 原始图像数据->encode压缩为AVPacket -> muxing合成为视频文件


看下面结构体前需要,了解一下时间戳转换:
ffmpeg有个内部的时间基数AV_TIME_BASE,所有的时间戳都是以此为准的;
#define AV_TIME_BASE 1000000
时基的倒数AV_TIME_BASE_Q,
#define         AV_TIME_BASE_Q   (AVRational){1, AV_TIME_BASE}
typedef struct AVRational{
int num; //numerator
int den; //denominator
} AVRational;
ffmpeg提供了一个把AVRatioal结构转换成double的函数:


static inline double av_q2d(AVRational a){
/**
* Convert rational to double.
* @param a rational to convert
**/
    return a.num / (double) a.den;
}


typedef struct AVPacket {
    /**
     * A reference to the reference-counted buffer where the packet data is
     * stored.
     * May be NULL, then the packet data is not reference-counted.
     */
    AVBufferRef *buf;
     /*
时间戳,若为AV_NOPTS_VALUE,则该文件需要重新计算填写时间戳
时间戳的理解:视频和音频流都有时间戳,相对于同一时间点
解码时间戳*/
    int64_t pts;
    /**
     * Decompression timestamp in AVStream->time_base units; the time at which
     * the packet is decompressed.
     * Can be AV_NOPTS_VALUE if it is not stored in the file.
     显示时间戳*/
    int64_t dts;
    uint8_t *data;
    int   size;
    int   stream_index;
    /**
     * 关键帧标志位 -- 1
     */
    int   flags;
    /**
     * Additional packet data that can be provided by the container.
     * Packet can contain several types of side information.
     */
    AVPacketSideData *side_data;
    int side_data_elems;


    /**
     * Duration of this packet in AVStream->time_base units, 0 if unknown.
     * Equals next_pts - this_pts in presentation order.
     */
    int   duration;
#if FF_API_DESTRUCT_PACKET
    attribute_deprecated
    void  (*destruct)(struct AVPacket *);
    attribute_deprecated
    void  *priv;
#endif
    int64_t pos;                            ///< byte position in stream, -1 if unknown


    /**
     * Time difference in AVStream->time_base units from the pts of this
     * packet to the point at which the output from the decoder has converged
     * independent from the availability of previous frames. That is, the
     * frames are virtually identical no matter if decoding started from
     * the very first frame or from this keyframe.
     * Is AV_NOPTS_VALUE if unknown.
     * This field is not the display duration of the current packet.
     * This field has no meaning if the packet does not have AV_PKT_FLAG_KEY
     * set.
     *
     * The purpose of this field is to allow seeking in streams that have no
     * keyframes in the conventional sense. It corresponds to the
     * recovery point SEI in H.264 and match_time_delta in NUT. It is also
     * essential for some types of subtitle streams to ensure that all
     * subtitles are correctly displayed after seeking.
     */
    int64_t convergence_duration;
} AVPacket;




int av_write_frame(AVFormatContext *s, AVPacket *pkt); //用于输出一帧视音频帧
//s:用于输出的AVFormatContext。
//pkt:等待输出的AVPacket。
//函数正常执行后返回值等于0。




推流器核心总结:
1. 初始化流媒体的工作,比如流媒体协议连接前的资源、缓冲和连接等工作
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename); 初始化输出的结构体、输出格式  但是流媒体协议的初始化部在这儿,继续看后面
//参数解释:ctx:函数调用成功之后创建的AVFormatContext结构体。


oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式。




PS:使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。


format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。


函数执行成功的话,其返回值大于等于0
//真正初始化流媒体在这儿
int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
s:函数调用成功之后创建的AVIOContext结构体。
url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。
AVIO_FLAG_READ:只读。
AVIO_FLAG_WRITE:只写。
AVIO_FLAG_READ_WRITE:读写。
int_cb:目前还没有用过。
options:目前还没有用过。
2. 写流媒体的头head --- avformat_write_header(ofmt_ctx, NULL);


3. 从输入的总结构体(AVFormatContxt)里面读取AVPacket  -----  av_read_frame(AVFormatContext *,AVPacket *);


4. 就是对读取出来的AVPacket里面的内容更改了,显示时间戳/解码时间戳和时间间隔等,怎么改,我也没弄明白,贴段别人的代码吧
if(pkt.pts==AV_NOPTS_VALUE){  
            //Write PTS  
            AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;  
            //Duration between 2 frames (us)  
            int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);  
            //Parameters  
            pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
            pkt.dts=pkt.pts;  
            pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);  
        }  
修改另一种时基的时间戳
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));  
      pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);

5. 将packet写到输出的AVFormatContext里面输出去
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);




至此,大部分的工作已经完成,最后还有一点收尾工作。  
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值