ffmpeg学习——基本的解码流程
由于工作需要,所以基本了解了一下视频的解码流程。
参考教程为:
1、王纲的《跟我一起学FFmpeg》系列
2、雷霄骅雷神的博客
原理部分暂时没有整理,后期可以补充一下知识。
ffmepg的api使用方面
1、打开一个输入流
2、设置解码器
3、读取每一个包,并获取到一帧的数据
4、交给解码器解码
下面的代码就是,读取一个视频或者文件,将其中的一帧图片保存为BGR24格式的文件,该文件加上BMP文件头即可使用图片浏览器打开。需要注意的是,这样会将图片上下翻转。所以图片会倒着显示。
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
#include <libavutil/file.h>
#include <libavutil/imgutils.h>#include <stdio.h>#define myprintf(x) printf(x)unsigned char *filecontent;
unsigned char *content; static int parse_cmd(int argc, char * argv[])
{int ret = 0;if (argc < 2)return -1;return ret;
}static void show_help()
{printf("Input error!\n");printf("Usage: ./decodec-and-display-ffmpeg <filepath/file>\n");
}void add_header()
{filecontent = (unsigned char*) malloc (0x5eec36);content = filecontent + 0x36;FILE *file = fopen("./1920*1080.bmp", "rb");if (file < 0){myprintf("file can not open!\n");return;}printf("1920*1080.bmp open success!\n");fread(filecontent, 1, 0x36, file);printf("finish reading 1920*1080.bmp header!\n");fclose(file);file = fopen("./fpsave.bgr24", "rb");fread(content, 1, 0x5eec00, file);myprintf("finish reading ./fpsave.bgr24!\n");fclose(file);file = fopen("./fpsave-bgr24.bmp", "wb+");fwrite(filecontent, 1, 0x5eec36, file);fclose(file);myprintf("Add header finish!\n");
}int main(int argc, char *argv[])
{int ret, got_picture;int i, videoindex = -1; //这两个变量,主要是从流中找到视频流的编号,而非音频流char filepath[128]; //输入文件路径int dst_bytes_num; //一幅解码后的BGR24格式图片存储所需的内存AVFormatContext *pFormatCtx; //上下文结构体,包含了所有的信息AVCodecContext *pCodecCtx; //解码器用到的上下文结构体AVCodec *pCodec; //解码器实例AVFrame *pFrame,*pFrameBGR; //存放帧和BGR数据的结构体AVPacket *avpacket = NULL; //这个结构体很关键,其中包含了ffmpeg从输入流中得到的裸流的数据和大小FILE *fpSave; //先将解码后的数据保存到这个文件中,以便检验uint8_t *out_buffer; //解码后的帧数据缓存struct SwsContext *img_convert_ctx; //格式转换需要用到的结构体,暂时不对视频进行格式转换/* 0、简单判断输入否合法 */ret = parse_cmd(argc, argv);if (ret < 0){show_help();return 0;}else {printf("%s\n", argv[1]);}/* 1、初始化ffmpeg */av_register_all();/* 2、打开输入流 *//* 2.1打开网络流或文件流 */pFormatCtx = avformat_alloc_context(); //分配一个输入流的上下文结构体,用以存储相关的数据 ret = avformat_open_input(&pFormatCtx, argv[1], NULL, NULL); //以设定的方式(参数)打开输入流,并与pFormatCtx结构体关联,但是此时未填充if(ret) {printf("Couldn't open input stream.\n");return -1;}/* 2.2获取流信息,并保存在上下文pFormatCtx中 */ret = avformat_find_stream_info(pFormatCtx, NULL); //通过该函数,填充pFormatCtx结构体,如编码方式,流(音频、视频)的个数,宽高,帧率等if(ret){printf("Couldn't find stream information.\n");return -1;}/* 2.3打印出输入文件信息 */printf("---------------- File Information ---------------\n");av_dump_format(pFormatCtx, 0, argv[1], 0);printf("-------------------------------------------------\n");/* 2.4从流中,找出视频流编号 */videoindex=-1; for( i = 0; i < pFormatCtx->nb_streams; i++) {if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) //我们不关心音频AVMEDIA_TYPE_AUDIO,所以找出来视频流AVMEDIA_TYPE_VIDEO的编号,通常为0{videoindex=i;//printf("videoindex = %d\n", videoindex);break;}}if(-1 == videoindex){printf("Didn't find a video stream.\n");return -1;}/* 3、解码输入流,获取到一帧的数据 *//* 3.1根据编码类型查找解码器 */pCodecCtx = pFormatCtx->streams[videoindex]->codec; //获取到视频的编码类型,对于MP4,pFormatCtx->streams[videoindex]->codec->codec_id = 13,即AV_CODEC_ID_MPEG4//printf("pCodecCtx = pFormatCtx->streams[videoindex]->codec->codec_id = %d \n", pFormatCtx->streams[videoindex]->codec->codec_id);pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //用于查找FFmpeg的AV_CODEC_ID_MPEG4格式对应的解码器if(pCodec==NULL){printf("Codec not found.\n");return -1;}/* 3.2打开解码器 */if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Could not open codec.\n");return -1;}/* 3.2为解码工作,分配存储空间 *///为一帧编码的数据分配内存空间,这个函数只是分配AVFrame结构体,但data指向的内存并没有分配,需要我们指定,这个内存的大小就是一张特定格式图像所需的大小。pFrame=av_frame_alloc(); //为一帧编解码后的BGR24数据分配一个结构体,这个函数只是分配AVFrame结构体,但data指向的内存并没有分配,需要我们指定,这个内存的大小就是一张特定格式图像所需的大小。pFrameBGR=av_frame_alloc(); //分配的out_buffer大小是宽*高*3(RGB24)对于测试视频320*240*3=230400Byte,该buffer只是一个缓存dst_bytes_num = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);out_buffer=(uint8_t *)av_malloc(dst_bytes_num);if (out_buffer){printf("out_buffer alloc success!\n");printf("address of out_buffer = %p\n", out_buffer);}else{printf("out_buffer alloc failed!\n");return 0;}//前面的av_frame_alloc函数,只是为这个AVFrame结构体分配了内存,而该类型的指针指向的内存还没分配。这里把av_malloc得到的内存和AVFrame关联起来,当然,其还会设置AVFrame的其他成员//该函数会初始化pFrameBGR->linesizeret = av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, out_buffer, AV_PIX_FMT_BGR24,pCodecCtx->width, pCodecCtx->height,1);//经过测试,pFrameBGR->linesize[4]的初始值均为0,经过上面的初始化后,分别被赋值;pFrameBGR->data[4]为4个指针,初值均为NULL,后续被分配到内存//如果参数是YUV420P,pFrameBGR->linesize[0] = width, pFrameBGR->linesize[1] = width / 2, pFrameBGR->linesize[2] = width / 2//如果参数是BGR24,pFrameBGR->linesize[0] = width * 3, pFrameBGR->linesize[1] = 0, pFrameBGR->linesize[2] = 0//printf("pFrameBGR->linesize[0] = %d, pFrameBGR->linesize[1] = %d, pFrameBGR->linesize[2] = %d, pFrameBGR->linesize[3] = %d\naddress of pFrameBGR->data[0] = %p, address of pFrameBGR->data[1] = %p, address of pFrameBGR->data[2] = %p\n", pFrameBGR->linesize[0], pFrameBGR->linesize[1], pFrameBGR->linesize[2], pFrameBGR->linesize[3], pFrameBGR->data[0], pFrameBGR->data[1], pFrameBGR->data[2]);//为AVPacket结构体分配缓存avpacket=(AVPacket *)av_malloc(sizeof(AVPacket));//对转换进行设置,这里要设置转换源的大小、格式和转换目标的大小、格式,以及转换所用的算法//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);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);if (img_convert_ctx == NULL) { printf("sws_getContext failed!\n "); return -1; }if ((fpSave = fopen("./fpsave.bgr24", "wb+")) == NULL) //data数据保存的文件名return 0; int cnt = 0;int cnt_decode = 0;while(av_read_frame(pFormatCtx, avpacket)>=0){if(avpacket->stream_index==videoindex){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, avpacket); //真正的解码工作,将压缩的数据解码为yuv420p格式if(ret < 0) { printf("解码错误\n"); return -1; }else{cnt_decode++;}if(got_picture){printf("解码一帧所需的次数cnt_decode = %d\n", cnt_decode);printf("解码成功\n"); sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameBGR->data, pFrameBGR->linesize);printf("YUV420P->BGR24格式转换完成\n"); //y_size=pCodecCtx->width*pCodecCtx->height*3; if (cnt == 0)fwrite(pFrameBGR->data[0], 1, dst_bytes_num, fpSave); //BGR24//fwrite(pFrameBGR->data[0], 1, y_size, fpSave); //Y //fwrite(pFrameBGR->data[1], 1, y_size/4,fpSave); //U//fwrite(pFrameBGR->data[2], 1, y_size/4,fpSave); //Vprintf("Succeed to decode 1 frame!\n");printf("avpacket->stream_index = %d\n", avpacket->stream_index);printf("avpacket->size = %d, cnt = %d\n", avpacket->size, cnt);//fwrite(avpacket->data,1,avpacket->size,fpSave);//写数据到文件中cnt++;if (cnt == 1)break;}}av_free_packet(avpacket);}fclose(fpSave);add_header();av_frame_free(&pFrameBGR);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;
}
ffmpeg学习——基本的解码流程相关推荐
- FFmpeg学习 avcodec软解码函数分析
前言 本文分析ffmpeg软解码流程,相关函数如下,以find_stream_info中的try_decode_frame为例: 相关函数都在libavcodec包下. 基本调用流程如下: const ...
- FFmpeg学习笔记之ffplay流程分析
背景说明 FFmpeg是一个开源,免费,跨平台的视频和音频流方案,它提供了一套完整的录制.转换以及流化音视频的解决方案.而ffplay是有ffmpeg官方提供的一个基于ffmpeg的简单播放器.学习f ...
- 【FFmpeg】详解FFmpeg解封装、解码流程
目录 1.获取媒体信息头 2.获取媒体流信息 3.准备解码器 3.1 获取视频.音频.字幕流在解封装上下文 AVFormatContext 的流列表 AVStream **streams 中的索引 3 ...
- 【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)
一.前言 在正式编写 FFmpeg 播放器前,我们需要先简单了解下所要用到的 FFmpeg 库.播放与解码流程.函数和相关结构体. 二.FFmpeg 库简介 库 介绍 avcodec 音视频编解码核心 ...
- FFmpeg解码流程简介
本文基于雷神的<基于 FFmpeg + SDL 的视频播放器的制作>课程的视频 ,本文就是基于该系列文章的学习后,总结出来的学习经验.如果想细致了解更多方法的使用,可以参考雷神的FFmpe ...
- 基于Intel 集成显卡的 FFmpeg 调用 VAAPI 硬件解码零数据拷贝链接推理引擎工作流程的实现
概述 在视频处理流程中,视频的解码通常在 CPU 中进行,若用户需要使用集成显卡进行深度学习推理,解码数据需要从 CPU的缓存中拷贝至集成显卡中进行推理.本文旨在通过集成显卡进行硬件解码,使用FFmp ...
- ffmpeg解码流程 turorial5详解
From: http://www.360doc.com/content/11/1117/09/8050095_165108638.shtml FFMPEG解码流程 1. 注册所有容器格式和CODEC: ...
- 264编码基本概念 FFMpeg的解码流程
下面转自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8楼 1.NAL.Slice与fr ...
- Intel MediaSDK sample_decode 官方GPU解码流程学习(二) - 在双显卡机器上实现DirectX11 D3D11和OpenCL共享资源
很久以前写过有关D3D11和OCL直接共享显存的代码, Intel MediaSDK sample_decode 官方GPU解码流程学习 - DirectX11 D3D11和OpenCL共享资源 这段 ...
最新文章
- AlphaCode惊世登场!编程版“阿法狗”悄悄参赛,击败一半程序员
- swoole原生mysql进程池_swoole的mysql连接池怎么弄
- 关于产品经理如何准备面试,我有三点想法
- 概率论和数理统计 - 01
- 【IDEA】IDEA 单元测试 System.in 没办法输入
- centos 低版本出现fatal: unable to access 'https://github.com/XXXX': SSL connect error
- tesseract如何在Linux下卸载,Tesseract装配
- Carryon 数数字(x^n ≡1 mod(x-1))
- ETOKEN 身份认证 电子证书
- 计算机学院运动会解说词,学院运动会入场解说词
- Android 版本更新,支持增量更新
- python turtle画简易的太极图
- 云计算作者姚宏宇1月26日中关村图书大厦讲座
- Unity常见插件汇总
- 纺织再生产品申请grs注意标准
- PowerDesigner 生成数据字典
- linux运维管理系统培训,Linux运维教程之Linux系统用户与组管理
- EndNote20.5 for Mac安装教程
- Java学习网站有哪些
- 【命令】如何将文件夹(永久)映射成虚拟硬盘