都0202年了,本文基于FFmpeg4.2.1,将使用最新版的api。让av_register_all()见鬼去吧!
FFmpeg的文章绝大多数都是3.X的,不少方法都过期了。对于喜新厌旧的洁癖君而言,满屏飘黄的警告、运行一下全是过期的警告是多么糟心。本文根据源码中的exsample进行改编,删繁就简,对于判空,校验返回值,打印错误什么的,本身在使用时注意一下,自行处理。java
为了让你明白这篇文章是在干吗,讲个小故事先:ios
捷特有两个护体神兽:白皇和黑皇
白皇善鸣,声震天地。身长千尺,振翅遮天蔽日。
黑皇善视,目之所见录于脑中,万世不灭。身高万丈,举手可握云擎天
像这种巨无霸级别的神兽是没法随身携带的。男主不被打到锁血,是不会召唤出来的。
那如何凸显主角的特别:动漫里的桥段是萌化成两个黑白服饰的漂亮妹子守护男主。
但两我的在面前晃晃悠悠也很差带回家,将两者封印在一个挂坠中,使用时进行召唤。
打BOSS怎么打的:
捷特握着挂坠-->召唤黑白萌皇-->萌皇巨大化 --> 打Boss
复制代码
上面的故事包含音视频的数据概念:编程
很是大的原始数据: 音频pcm --> 巨兽白皇
编码后较小数据: 音频aac --> 人型萌白皇
很是大的原始数据: 视频YUV --> 巨兽黑皇
编码后较小数据: 视频h264 --> 人型萌黑皇
mp四、ts、avi等封装体格式: aac + h264 ---->封印挂坠
播放器怎么播放的:
拆封封装格式-->召唤寻找编码流-->解码流 --> 渲染呈现
复制代码
这篇的目标是将挂坠
(sh.ts)
中的黑萌娘(sh.h264)
召唤出来,而且转换成神兽黑皇(sh_768x432.yuv)
api
为何说YUV是巨兽,看下面的数据就知道了。视频3分30秒,YUV数据飙到2.48G。也许你会以为为何会有YUV这样逆天的存在,其实渲染层须要YUV,给它压缩后的数据人家不认识。播放时会先进行解码, 你能在手机里存几百部片,都要感谢压缩格式。bash
这里介绍主要的流程,这大白话的注释你要再看不懂我也没办法了。ide
#include <iostream>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};
using namespace std;
int main() {
const char *rec_path = "/Volumes/coder/Projects/Media/TolyFF/res/sh.ts";
AVFormatContext *fmt_ctx;//格式化上下文--召唤场地
fmt_ctx = avformat_alloc_context();// 准备召唤场地
avformat_open_input(&fmt_ctx, rec_path, nullptr, nullptr);//打开封印
avformat_find_stream_info(fmt_ctx, nullptr);//找到召唤物
int v_idx = av_find_best_stream(//寻找视频流索引 -- 萌黑
fmt_ctx, AVMEDIA_TYPE_VIDEO,-1, -1, nullptr, 0);
AVCodecParameters *c_par;//声明-召唤器参数
AVCodecContext *cc_ctx;//声明-召唤器环境
const AVCodec *codec;//声明-召唤器
c_par = fmt_ctx->streams[v_idx]->codecpar;//实例化-召唤器参数
codec = avcodec_find_decoder(c_par->codec_id);//实例化-召唤器
//用参数c_par实例化编解码器上下文,,并打开编解码器
cc_ctx = avcodec_alloc_context3(codec);//实例化-召唤器环境
avcodec_parameters_to_context(cc_ctx, c_par);//召唤器环境参数填充
avcodec_open2(cc_ctx, codec, nullptr);//打开召唤器
AVPacket *pkt; //声明 萌黑-数据包
AVFrame *frame;//声明 黑皇-帧
pkt = av_packet_alloc();//准备 黑皇-帧
frame = av_frame_alloc();//准备 萌黑-数据包
FILE *dst_f=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh.h264","wb+");//萌黑实体sh.h264
FILE *dst_f_yuv=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh_768x432.yuv","wb+");//黑皇实体sh_768x432.yuv
while (av_read_frame(fmt_ctx, pkt) >= 0) {//持续读帧
if (pkt->stream_index == v_idx) {
avcodec_send_packet(cc_ctx, pkt);//发送 萌黑-数据包
fwrite(pkt->data,1,pkt->size,dst_f);// 萌黑-数据包拼合集结
vcodec_receive_frame(cc_ctx, frame);//接收解码 -- 巨大化
fwrite(frame->data[0],1,cc_ctx->width*cc_ctx->height,dst_f_yuv);// 黑皇Y-数据包拼合集结
fwrite(frame->data[1],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇U-数据包拼合集结
fwrite(frame->data[2],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇V-数据包拼合集结
}
}
fclose(dst_f);// 萌黑OK , 拔掉链接头
fclose(dst_f_yuv);// 萌黑OK , 拔掉链接头
avcodec_free_context(&cc_ctx);//关闭环境
av_frame_free(&frame);//释放本体
av_packet_free(&pkt);//释放载体
}
复制代码
运行后会生成h264和YUV两个文件,经过ffplay能够播放
二者分别是纯视频的压缩流和原始流,因此播放起来是没有声音的。
你也许会问为嘛要解码出这两个流,有一种神技叫作融合,有一种操做叫作变换。少年呦,你对于力量一无所知。post
ffplay sh.h264
ffplay -video_size 768x432 sh_768x432.yuv
复制代码
下图是新旧的示意图:编码
注册方法,去了吧spa
attribute_deprecated
void av_register_all(void);
复制代码
AVStream中的codec方法能够获取AVCodecContext,已过期。
取而代之的是codecpar的AVCodecParameters,在根据参数去建立上下文。如上3d
cc_ctx = fmt_ctx->streams[v_idx]->codec;
/**
* @deprecated use the codecpar struct instead
*/
attribute_deprecated
AVCodecContext *codec;
复制代码
avcodec_decode_video2 方法被拆成了两个:
avcodec_send_packet()
和avcodec_receive_frame()
分别用于包和帧的处理。透露一下packet用来召唤萌娘,frame用来激活巨兽。
* @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
*/
attribute_deprecated
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt);
复制代码
AVFormatContext:
封装格式上下文,包含一些介绍信息,及最重要的码流stream,这就是数据的源泉。AVInputFormat记录了封装格式的信息。
AVCodecParameters:
编解码器的参数,从AVStream的codecpar获取,取代codec属性。能够获取流的参数,如视频流宽、高、编解码器类型、编解码器id等。
AVCodecContext:
编解码器的上下文,可经过avcodec_parameters_to_context使用AVCodecParameters进行参数填充。也是记录着视频流的信息,不一样的是他包含了编解码器对象codec。
AVCodec:
编解码器的上下文,至关于大古的神光棒,能让大古变成光的东西。
AVPacket: 编码后的码流,对应现视频,data字段也就是以及压缩后的h264数据。初次以外还有其余信息:
AVFrame:
解码后的原始流,YUV份量合并可造成巨大的YUV神兽。
@张风捷特烈 2019.11.26 未允禁转
个人公众号:编程之王
~ END ~