当前位置: 首页 > news >正文

FFMPEG 解码过程初步学习

1. 视频文件解码过程

解码过程

在这里插入图片描述

步骤如下:

  1. 视频文件(封装格式,MP4/FLV/AVI 等)获取视频格式信息等
  2. 解复用为Stream 流, 准备解码用的Codec
  3. 将Stream 流 使用解码器解为Raw 格式针
    在这里插入图片描述
    数据结构:
    AVFormatContext: 整个过程非常重要的数据结果,通过avformat_open_input 从文件获取format 信息,一般封装信息在这一步就都可以获取到了,在通过avformat_find_stream_info 接口,获取读入视频文件中的stream 信息(这里的stream 一般包含视频流、音频流、字幕流(subtitile) 等)
    AVCodecContext: 解码过程中需要使用的codec context, 在avcodec_send_packet 和avcodec_receive_frame 中都要使用的数据结构,里面包含了解码必要的信息,比如图像width, height, format 等信息
    AVPacket: 从文件里面读取出来的包数据
    AVFrame: avcodec_send_packet 将AVPacket 数据送给指定的解码器后,通过avcodec_receive_frame 获取到解码后的帧数据封装在AVFrame 中

1.1 音视频格式填充:

int ret = avformat_open_input(&format_ctx, filename, nullptr, nullptr);  // 打开输入文件并将格式上下文赋给 format_ctx
// 填充stream 信息
if (avformat_find_stream_info(format_ctx, NULL) < 0) {std::cout << "no stream in files : " << std::endl;return -1;
}// find the video stream information
ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (ret < 0) {fprintf(stderr, "Cannot find a video stream in the input file\n");return -1;
}
video_stream_index = ret;
video_stream = format_ctx->streams[video_stream_index];ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (ret < 0) {std::cout<< "no audio stream " << ret << std::endl;
} else {audio_stream_index = ret;audio_stream = format_ctx->streams[audio_stream_index];
}

1.2 准备解码用的codec

const AVCodec * video_codec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (video_codec == nullptr) {std::cout << "we not found the code: " << video_decoder->id << std::endl;
} else {std::cout << "we found the video codec: " << video_codec->name << std::endl;
}
const AVCodec * audio_codec = avcodec_find_decoder(audio_stream->codecpar->codec_id);
if (audio_codec == nullptr) {std::cout << "we not found the code: " << audio_decoder->id << std::endl;
} else {std::cout << "we found the audio codec: " << audio_codec->name << std::endl;
}// codec context for decoder 
AVCodecContext* video_dec_context = avcodec_alloc_context3(video_codec);
if (!video_dec_context) {std::cout << "video dec context alloc failed" << std::endl;
}
if ((ret = avcodec_parameters_to_context(video_dec_context, video_stream->codecpar)) < 0) {std::cerr << "codec copy failed" << std::endl;
}
std::cout<< "video format width: " << video_dec_context->width << std::endl;
std::cout<< "video format height: " << video_dec_context->height << std::endl;
std::cout << "video pixel format: " << video_dec_context->pix_fmt << std::endl;
video_width = video_dec_context->width;
video_height = video_dec_context->height;
video_fmt = video_dec_context->pix_fmt;
// open codec
ret = avcodec_open2(video_dec_context, video_codec, NULL);
if (ret < 0) {std::cerr << "video codec open failed" << std::endl;
}

Audio 的流程和video 的流程相近:
通过stream 信息->AVCodec->AVCodecContext , 再通过AVCodecContext 打开解码器(avcodec_open2)

1.3 解码过程:

在打开解码器后,创建AVPacket AVFrame
AVPacket 是解码前的包, AVFrame 是解码后的帧

AVPacket *packet;
packet = av_packet_alloc();AVFrame* frame = av_frame_alloc();while(1) {// 从里面获取一个packetret1 = av_read_frame(format_ctx, packet);if (ret1 < 0) {break;} else {/*if (packet->stream_index == video_stream_index) {std::cout << "Video: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;} else {if (packet->stream_index != -1 && audio_stream_index == packet->stream_index) {std::cout << "Audio: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;}}*/if (packet->stream_index == video_stream_index) {int result = decode_packet(video_dec_context, packet);if (result == 0) {//video_frame_count++;}} else {decode_packet(audio_dec_context, packet);av_frame_unref(frame);}}av_packet_unref(packet);}
// 解包过程
static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
{int ret = 0;// submit the packet to the decoderret = avcodec_send_packet(dec, pkt);if (ret < 0) {fprintf(stderr, "Error submitting a packet for decoding (%d)\n", ret);return ret;}// get all the available frames from the decoderwhile (ret >= 0) {ret = avcodec_receive_frame(dec, frame);if (ret < 0) {// those two return values are special and mean there is no output// frame available, but there were no errors during decodingif (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))return 0;// fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret));std::cerr << "Error during decoding " << ret << std::endl;return ret;}// // write the frame data to output fileif (dec->codec->type == AVMEDIA_TYPE_VIDEO) {static int video_frame_count = 0;/* copy decoded frame to destination buffer:* this is required since rawvideo expects non aligned data */// std::cout << "frame size: " << frame->linesize << std::endl;av_image_copy2(video_dst_data, video_dst_linesize,frame->data, frame->linesize,video_fmt, video_width, video_height);/* write to rawvideo file */size_t size =  fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);if (size == 0) {std::cerr << "write failed" << std::endl;return -1;} else {video_frame_count++;std::cout << " write to video frame count: " << video_frame_count << std::endl;}}//ret = output_video_frame(frame);// else//     ret = output_audio_frame(frame);av_frame_unref(frame);}return ret;
}

相关文章:

  • 重学java 51.Collections集合工具类、泛型
  • robosuite导入自定义机器人
  • SpringBoot+Vue开发记录(六)-- 后端配置mybatis
  • MySQL之创建高性能的索引(六)
  • SpringBoot整合WebSocket实现聊天室
  • MySQL数据库入门之视图、存储过程、触发器
  • 智能除螨—wtn6040-8s语音芯片方案引领除螨仪新时代
  • windows系统电脑外插键盘驱动出现感叹号或者显示未知设备,键盘无法输入的解决办法
  • GeoScene产品学习视频收集
  • 【UML用户指南】-02-UML的14种图
  • 二叉树链式结构的前序_中序_后续_层序遍历【详细图解】
  • leetCode-hot100-数组专题之子数组+二维数组
  • SSD图、用例描述
  • React Native 之 ToastAndroid(提示语)(二十一)
  • I2C协议详解
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • Android交互
  • C++类中的特殊成员函数
  • gcc介绍及安装
  • in typeof instanceof ===这些运算符有什么作用
  • passportjs 源码分析
  • react 代码优化(一) ——事件处理
  • Spring Boot MyBatis配置多种数据库
  • 猴子数据域名防封接口降低小说被封的风险
  • 回顾2016
  • 将 Measurements 和 Units 应用到物理学
  • 开源SQL-on-Hadoop系统一览
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 正则表达式小结
  • raise 与 raise ... from 的区别
  • 国内开源镜像站点
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​Linux·i2c驱动架构​
  • ​学习一下,什么是预包装食品?​
  • ###项目技术发展史
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • $.ajax,axios,fetch三种ajax请求的区别
  • (1)Nginx简介和安装教程
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (2)leetcode 234.回文链表 141.环形链表
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (c语言+数据结构链表)项目:贪吃蛇
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (k8s)kubernetes集群基于Containerd部署
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (四) 虚拟摄像头vivi体验
  • (限时免费)震惊!流落人间的haproxy宝典被找到了!一切玄妙尽在此处!
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)