热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ffmpeg视频编解码实例

视频解码ffmpegdemo.cpp:定义控制台应用程序的入口点。#includestdafx.h#include#define__STDC_CONST

视频解码

// ffmpegdemo.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include #define __STDC_CONSTANT_MACROS#ifdef _WIN32//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};
#else//Linux...
#ifdef __cplusplus
extern "C"
{
#endif#include
#include
#include
#include
#ifdef __cplusplus
};
#endif
#endifint main()
{//文件格式上下文AVFormatContext *pFormatCtx;int i &#61; 0, videoindex;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame, *pFrameYUV;unsigned char *out_buffer;AVPacket *packet;int y_size;int ret, got_picture;struct SwsContext *img_convert_ctx;char filepath[] &#61; "input.mkv";FILE *fp_yuv &#61; fopen("output.yuv", "wb&#43;");av_register_all();avformat_network_init();pFormatCtx &#61; avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) !&#61; 0) {printf("Couldn&#39;t open input stream.\n");return -1;}//读取一部分视音频数据并且获得一些相关的信息if (avformat_find_stream_info(pFormatCtx, NULL) <0) {printf("Couldn&#39;t find stream information.\n");return -1;}//查找视频编码索引videoindex &#61; -1;for (i &#61; 0; i nb_streams; i&#43;&#43;){if (pFormatCtx->streams[i]->codec->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO){videoindex &#61; i;break;}}if (videoindex &#61;&#61; -1){printf("Didn&#39;t find a video stream.\n");return -1;}//编解码上下文pCodecCtx &#61; pFormatCtx->streams[videoindex]->codec;//查找解码器pCodec &#61; avcodec_find_decoder(pCodecCtx->codec_id);if (pCodec &#61;&#61; NULL) {printf("Codec not found.\n");return -1;}//打开解码器if (avcodec_open2(pCodecCtx, pCodec, NULL) <0) {printf("Could not open codec.\n");return -1;}//申请AVFrame&#xff0c;用于原始视频pFrame &#61; av_frame_alloc();//申请AVFrame&#xff0c;用于yuv视频pFrameYUV &#61; av_frame_alloc();//分配内存&#xff0c;用于图像格式转换out_buffer &#61; (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);packet &#61; (AVPacket *)av_malloc(sizeof(AVPacket));//Output Info-----------------------------printf("--------------- File Information ----------------\n");//手工调试函数&#xff0c;输出tbn、tbc、tbr、PAR、DAR的含义av_dump_format(pFormatCtx, 0, filepath, 0);printf("-------------------------------------------------\n");//申请转换上下文img_convert_ctx &#61; sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//读取数据while (av_read_frame(pFormatCtx, packet) >&#61; 0) {if (packet->stream_index &#61;&#61; videoindex) {ret &#61; avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret <0) {printf("Decode Error.\n");return -1;}if (got_picture >&#61; 1) {//成功解码一帧sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameYUV->data, pFrameYUV->linesize);//转换图像格式y_size &#61; pCodecCtx->width*pCodecCtx->height;fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); //Y fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //Ufwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); //Vprintf("Succeed to decode 1 frame!\n");}else{//未解码到一帧&#xff0c;可能时结尾B帧或延迟帧&#xff0c;在后面做flush decoder处理}}av_free_packet(packet);}//flush decoder//FIX: Flush Frames remained in Codecwhile (true) {if (!(pCodec->capabilities & CODEC_CAP_DELAY))return 0;ret &#61; avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret <0){break;}if (!got_picture){break;}sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameYUV->data, pFrameYUV->linesize);int y_size &#61; pCodecCtx->width*pCodecCtx->height;fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); //Y fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //Ufwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); //Vprintf("Flush Decoder: Succeed to decode 1 frame!\n");}sws_freeContext(img_convert_ctx);av_frame_free(&pFrameYUV);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);fclose(fp_yuv);return 0;
}


视频编码

// ffmpegdemo.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include #define __STDC_CONSTANT_MACROS#ifdef _WIN32//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};
#else//Linux...
#ifdef __cplusplus
extern "C"
{
#endif#include
#include
#include
#include
#ifdef __cplusplus
};
#endif
#endifint flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY)){return 0;}while (true) {enc_pkt.data &#61; NULL;enc_pkt.size &#61; 0;av_init_packet(&enc_pkt);ret &#61; avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame);av_frame_free(NULL);if (ret <0){break;}if (!got_frame){ret &#61; 0;break;}printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);/* mux encoded frame */ret &#61; av_write_frame(fmt_ctx, &enc_pkt);if (ret <0){break;}}return ret;
}int main()
{AVFormatContext* pFormatCtx;AVOutputFormat* pOutputFmt;AVStream* video_st;AVCodecContext* pCodecCtx;AVCodec* pCodec;AVPacket pkt;uint8_t* picture_buf;AVFrame* pFrame;int picture_size;int y_size;int framecnt &#61; 0;int in_w &#61; 640, in_h &#61; 272; //视频图像宽高const char* out_file &#61; "output.h264";int i, j;int num;int index_y, index_u, index_v;uint8_t *y_, *u_, *v_, *in;int got_picture &#61; 0;int ret;av_register_all();//alloc avformat contextpFormatCtx &#61; avformat_alloc_context();//猜测类型,返回输出类型。pOutputFmt &#61; av_guess_format(NULL, out_file, NULL);pFormatCtx->oformat &#61; pOutputFmt;//打开FFmpeg的输入输出文件,成功之后创建的AVIOContext结构体。if (avio_open2(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE,NULL,NULL) <0) {//Failed to open output filereturn -1;}//除了以下方法&#xff0c;另外还可以使用avcodec_find_encoder_by_name()来获取AVCodec pCodec &#61; avcodec_find_encoder(pOutputFmt->video_codec);//获取编码器 if (!pCodec) {//cannot find encoder return -1;}pCodecCtx &#61; avcodec_alloc_context3(pCodec);//申请AVCodecContext&#xff0c;并初始化。 if (!pCodecCtx) {//failed get AVCodecContext return -1;}FILE * in_file &#61; fopen("640-272.yuv", "rb"); //打开原始yuv数据文件/* 创建输出码流的AVStream */video_st &#61; avformat_new_stream(pFormatCtx, 0);video_st->time_base.num &#61; 1;video_st->time_base.den &#61; 25;if (video_st &#61;&#61; NULL) {return -1;}//Param that must set pCodecCtx &#61; video_st->codec;//pCodecCtx->codec_id &#61;AV_CODEC_ID_HEVC; //H265pCodecCtx->codec_id &#61; AV_CODEC_ID_H264;pCodecCtx->codec_type &#61; AVMEDIA_TYPE_VIDEO;pCodecCtx->pix_fmt &#61; AV_PIX_FMT_YUV420P;pCodecCtx->width &#61; in_w;pCodecCtx->height &#61; in_h;pCodecCtx->b_frame_strategy &#61; true;/*码率bit_rate/-bt tolerance 设置视频码率容忍度kbit/s &#xff08;固定误差&#xff09;rc_max_rate/-maxrate bitrate设置最大视频码率容忍度 &#xff08;可变误差&#xff09;rc_min_rate/-minrate bitreate 设置最小视频码率容忍度&#xff08;可变误差&#xff09;rc_buffer_size/-bufsize size 设置码率控制缓冲区大小如何设置固定码率编码 ?bit_rate是平均码率&#xff0c;不一定能控制住c->bit_rate &#61; 400000;c->rc_max_rate &#61; 400000;c->rc_min_rate &#61; 400000;提示 [libx264 &#64; 00c70be0] VBV maxrate specified, but no bufsize, ignored再设置 c->rc_buffer_size &#61; 200000; 即可。如此控制后编码质量明显差了。*/pCodecCtx->bit_rate &#61; 400000; //采样码率越大&#xff0c;目标文件越大//pCodecCtx->bit_rate_tolerance &#61; 8000000; // 码率误差&#xff0c;允许的误差越大&#xff0c;视频越小//两个I帧之间的间隔pCodecCtx->gop_size &#61; 15;//编码帧率&#xff0c;每秒多少帧。下面表示1秒25帧pCodecCtx->time_base.num &#61; 1;pCodecCtx->time_base.den &#61; 25;//最小的量化因子pCodecCtx->qmin &#61; 10;//最大的量化因子pCodecCtx->qmax &#61; 30;//最大B帧数pCodecCtx->max_b_frames &#61; 3;// Set Option AVDictionary *param &#61; 0;//H.264 if (pCodecCtx->codec_id &#61;&#61; AV_CODEC_ID_H264) {/*preset的参数主要调节编码速度和质量的平衡&#xff0c;有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项&#xff0c;从快到慢。*/av_dict_set(¶m, "preset", "fast", 0);/* tune的参数主要配合视频类型和视觉优化的参数。tune的值有&#xff1a; film&#xff1a; 电影、真人类型&#xff1b;animation&#xff1a; 动画&#xff1b;grain&#xff1a; 需要保留大量的grain时用&#xff1b;stillimage&#xff1a; 静态图像编码时使用&#xff1b;psnr&#xff1a; 为提高psnr做了优化的参数&#xff1b;ssim&#xff1a; 为提高ssim做了优化的参数&#xff1b;fastdecode&#xff1a; 可以快速解码的参数&#xff1b;zerolatency&#xff1a;零延迟&#xff0c;用在需要非常低的延迟的情况下&#xff0c;比如电视电话会议的编码。*/av_dict_set(¶m, "tune", "zerolatency", 0);/*画质,分别是baseline, extended, main, high1、Baseline Profile&#xff1a;基本画质。支持I/P 帧&#xff0c;只支持无交错&#xff08;Progressive&#xff09;和CAVLC&#xff1b;2、Extended profile&#xff1a;进阶画质。支持I/P/B/SP/SI 帧&#xff0c;只支持无交错&#xff08;Progressive&#xff09;和CAVLC&#xff1b;(用的少)3、Main profile&#xff1a;主流画质。提供I/P/B 帧&#xff0c;支持无交错&#xff08;Progressive&#xff09;和交错&#xff08;Interlaced&#xff09;&#xff0c; 也支持CAVLC 和CABAC 的支持&#xff1b;4、High profile&#xff1a;高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV 格式&#xff1b;H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。在相同配置情况下&#xff0c;High profile&#xff08;HP&#xff09;可以比Main profile&#xff08;MP&#xff09;降低10%的码率。 根据应用领域的不同&#xff0c;Baseline profile多应用于实时通信领域&#xff0c;Main profile多应用于流媒体领域&#xff0c;High profile则多应用于广电和存储领域。*///av_dict_set(¶m, "profile", "main", 0); }else if (pCodecCtx->codec_id &#61;&#61; AV_CODEC_ID_H265 || pCodecCtx->codec_id &#61;&#61; AV_CODEC_ID_HEVC){av_dict_set(¶m, "preset", "fast", 0);av_dict_set(¶m, "tune", "zerolatency", 0);}//Output Info-----------------------------printf("--------------- out_file Information ----------------\n");//手工调试函数&#xff0c;输出tbn、tbc、tbr、PAR、DAR的含义av_dump_format(pFormatCtx, 0, out_file, 1); //最后一个参数&#xff0c;如果是输出文件时&#xff0c;该值为1&#xff1b;如果是输入文件时&#xff0c;该值为0printf("-----------------------------------------------------\n");/* 查找编码器 */pCodec &#61; avcodec_find_encoder(pCodecCtx->codec_id);if (!pCodec) {printf("Can not find encoder! \n");return -1;}/* 打开编码器 */if (avcodec_open2(pCodecCtx, pCodec,¶m) <0) {printf("Failed to open encoder! \n");return -1;}pFrame &#61; av_frame_alloc();picture_size &#61; avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);picture_buf &#61; (uint8_t *)av_malloc(picture_size);avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);//Write File Header /* 写文件头&#xff08;对于某些没有文件头的封装格式&#xff0c;不需要此函数。比如说MPEG2TS&#xff09; */avformat_write_header(pFormatCtx, NULL);/* Allocate the payload of a packet and initialize its fields with default values. */av_new_packet(&pkt, picture_size);j &#61; 1;y_size &#61; pCodecCtx->width * pCodecCtx->height;unsigned int uiReadSize &#61; 0;while (true){//Read raw YUV dataif (feof(in_file)) //文件结束{break;}else{uiReadSize &#61; fread(picture_buf, 1, y_size * 3 / 2, in_file);if (uiReadSize <&#61; 0){printf("Failed to read raw data! \n");break;}else if (uiReadSize data[0] &#61; picture_buf; // Y pFrame->data[1] &#61; picture_buf &#43; y_size; // U pFrame->data[2] &#61; picture_buf &#43; y_size * 5 / 4; // V //PTS pFrame->pts &#61; j*(video_st->time_base.den) / ((video_st->time_base.num) * 25);//Encode /* 编码一帧视频。即将AVFrame&#xff08;存储YUV像素数据&#xff09;编码为AVPacket&#xff08;存储H.264等格式的码流数据&#xff09; */int ret &#61; avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);if (ret <0){printf("Failed to encode! \n");return -1;}if (got_picture &#61;&#61; 1){printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size); framecnt&#43;&#43;;pkt.stream_index &#61; video_st->index;/* 将编码后的视频码流写入文件 */ret &#61; av_write_frame(pFormatCtx, &pkt);av_free_packet(&pkt);}j&#43;&#43;;}//Flush Encoder /* 输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket */ret &#61; flush_encoder(pFormatCtx, 0);if (ret <0) {printf("Flushing encoder failed\n");return -1;}//Write file trailer /* 写文件尾&#xff08;对于某些没有文件头的封装格式&#xff0c;不需要此函数。比如说MPEG2TS&#xff09; */av_write_trailer(pFormatCtx);//Clean if (video_st) {avcodec_close(video_st->codec);av_free(pFrame);av_free(picture_buf);}avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);av_free_packet(&pkt);fclose(in_file);return 0;
}

代码下载&#xff1a;点击打开链接



注意&#xff1a;
1、vs编译工具使用最新的ffmpeg库&#xff0c;ffmpeg调用avcodec库直接崩溃在avcodec_register_all处&#xff0c;不调用avcodec_register_all直接调用av_frame_alloc&#xff0c;也会崩溃。
解决方法&#xff1a;
工程属性&#xff1a;链接器-优化-引用的值由“是 (/OPT:REF)”改为“否 (/OPT:NOREF)”

2、ffmpeg视频编码参数&#xff0c;参考https://blog.csdn.net/byxdaz/article/details/80663718


参考资料&#xff1a;
https://blog.csdn.net/leixiaohua1020/article/details/25430425/


推荐阅读
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
author-avatar
捕鱼达人2502925105
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有