FFmpeg菜鸡互啄#第3篇#视频解码
时间:2022-05-03
本文章向大家介绍FFmpeg菜鸡互啄#第3篇#视频解码,主要内容包括解码过程、解码数据结构、Code、Github、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
解码过程
基本过程:打开输入文件,查找视频流,打开解码器,循环读帧解码帧,关闭解码器,关闭输入文件。
解码数据结构
Code
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
/*
#define __STDC_CONSTANT_MACROS
#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif
*/
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
}
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "postproc.lib")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib")
#define INPUT "in.flv"
#define OUTPUT "out.yuv"
int main()
{
int res = 0;
int videoStream = -1;//标记视频流的编号
char errBuf[BUFSIZ] = { 0 };
FILE* fp_out = fopen(OUTPUT, "wb+");
//初始化FFMPEG 调用了这个才能正常适用编码器和解码器
av_register_all();
printf("FFmpeg's version is: %dn", avcodec_version());
//FFMPEG所有的操作都要通过这个AVFormatContext来进行
AVFormatContext* pFormatCtx = NULL;
//打开输入视频文件
//Open an input stream and read the header. The codecs are not opened.
if ((res = avformat_open_input(&pFormatCtx, INPUT, NULL, NULL)) < 0)
{
av_strerror(res, errBuf, sizeof(errBuf));
printf("%sn", errBuf);
return -1;
}
//Read packets of a media file to get stream information. This is useful for file formats with no headers such as MPEG.
//相当于对输入进行 “预处理”
avformat_find_stream_info(pFormatCtx, NULL);
av_dump_format(pFormatCtx, 0, NULL, 0); //输出视频流的信息
//查找流
for (int i = 0; i < pFormatCtx->nb_streams; ++i)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
videoStream = i;
}
if (videoStream == -1)
{
printf("Didn't find a video stream.n");
return -1;
}
///查找解码器
AVCodecContext* pCodecCtx = pFormatCtx->streams[videoStream]->codec;
AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL)
{
printf("Codec not found.n");
return -1;
}
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("Could not open codec.");
return -1;
}
AVFrame Frame = { 0 };//不初始化,avcodec_decode_video2会报错
AVPacket packet;
int got_picture;
while (1)
{
//读取视频帧
//return 0 if OK, < 0 on error or end of file
if (av_read_frame(pFormatCtx, &packet) < 0)
{
break; //这里认为视频读取完了
}
if (packet.stream_index == videoStream)
{
//解码视频帧
if (avcodec_decode_video2(pCodecCtx, &Frame, &got_picture, &packet) < 0)
{
printf("decode error.n");
return -1;
}
if (got_picture)
{
if (Frame.format == PIX_FMT_YUV420P)
{
//解码后YUV格式的视频像素数据保存在AVFrame的data[0]、data[1]、data[2]中。
//但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。
//以亮度Y数据为例,data[0]中一共包含了linesize[0] * height个数据。
//但是出于优化等方面的考虑,linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。
fwrite(Frame.data[0], Frame.linesize[0] * Frame.height, 1, fp_out);
fwrite(Frame.data[1], Frame.linesize[1] * Frame.height / 2, 1, fp_out);
fwrite(Frame.data[2], Frame.linesize[2] * Frame.height / 2, 1, fp_out);
}
}
}
av_free_packet(&packet);//清除packet里面指向的缓冲区
}
fclose(fp_out);
avcodec_close(pCodecCtx);//关闭解码器
avformat_close_input(&pFormatCtx);//关闭输入视频文件。avformat_free_context(pFormatCtx);就不需要了
return 0;
}
保存的文件可以用bin目录下的YUVPlayer.exe查看,但是要设置好分辨率(Frame.linesize[0] * Frame.height)。我的30.9MB输入解码后得到一个3.8GB的数据!
注意最右边有无效数据,但是可以通过以后的转换操作去除。
Github
https://github.com/gongluck/FFmpegTest.git
- Oracle 12c ASM|专题配额组的配额真的管用吗?
- 即时通讯软件可以用GO语言实现吗
- Go语言-连接数据库SQLite、MySQL、Oracle - Mike_zh
- 连接redis
- glang文件读写
- Spring框架中的设计模式(三)
- go语言结构体定义使用
- Spring框架中的设计模式(四)
- Go语言单元测试
- go语言中json转成map结构
- Spring框架中的设计模式(五)
- Oracle 12c数据库优化器统计信息收集的最佳实践(二)
- hdu-----(4514)湫湫系列故事——设计风景线(树形DP+并查集)
- hdu----(1402)A * B Problem Plus(FFT模板)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- SAP Spartacus CmsService的CmsActions.LoadCmsComponent
- SAP Spartacus OccEndpointsService单元测试的依赖注入
- Python利用PyPDF2库获取PDF文件总页码实例
- TypeScript里get属性的实现
- SAP Spartacus OccEndpointsService单元测试之getBaseEndpoint
- html+css入门基础案例之圣诞那些事
- 建议收藏备查!MySQL 常见错误代码说明
- html+css入门基础案例之页面设计
- Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香...
- CSS3 2D转换
- CSS3 3D转换
- python传到前端的数据,双引号被转义的问题
- H5C3综合案例
- 移动web开发之rem适配布局
- Android面试题问答整理