FFmpeg学习记录(三)—— ffmpeg编解码实战

解码步骤

  • 查找解码器 (avcodec_find_decoder)
  • 打开解码器 (avcodec_open2)
  • 解码 (avcodec_decode_video2)

1.视频编码

编码的详细步骤:

  • 1.输入参数
  • 2.查找编码器
  • 3.创建编码器上下文
  • 4.设置编码器参数
  • 5.编码器与编码器上下文绑定到一起
  • 6.创建输出文件
  • 7.创建AVFrame
  • 8.创建AVPacket
  • 9.生成视频内容
  • 10.编码
// 1.输入参数if(argc < 3) {av_log(NULL, AV_LOG_ERROR, "arguments must be more than 3\n");goto _ERROR;}dst = argv[1];codecName = argv[2];// 2.查找编码器codec = avcodec_find_encoder_by_name(codecName);if(!codec) {av_log(NULL, AV_LOG_ERROR, "don't find Codec: %s\n", codecName);goto _ERROR;}
// 3.创建编码器上下文ctx = avcodec_alloc_context3(codec);if(!ctx) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}// 4.设置编码器参数ctx->width = 640;ctx->height = 480;ctx->bit_rate = 500000;ctx->time_base = (AVRational){1, 25};ctx->framerate = (AVRational){25, 1};ctx->gop_size = 10;ctx->max_b_frames = 1;ctx->pix_fmt = AV_PIX_FMT_YUV420P;if(codec->id == AV_CODEC_ID_H264) {av_opt_set(ctx->priv_data, "preset", "slow"0);}
// 5.编码器与编码器上下文绑定到一起ret = avcodec_open2(ctx, codec, NULL);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Dont't open codec: %s\n", av_err2str(ret));goto _ERROR;}// 6.创建输出文件f = fopen(dst, "wb");if(!f) {av_log(NULL, AV_LOG_ERROR, "Dont't open file: %s\n", dst);goto _ERROR;}// 7.创建AVFrameframe = av_frame_alloc();if(!frame) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}frame->width = ctx->width;frame->height = ctx->height;frame->format = ctx->pix_fmt;ret = av_frame_get_buffer(frame, 0);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Dont't get buffer: %s\n", av_err2str(ret));goto _ERROR;}// 8.创建AVPacketpkt = av_packet_alloc();if(!pkt) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}
    // 9.生成视频内容for(int i = 0; i < 25; i++) {ret = av_frame_make_writable(frame);if(ret < 0) break;// Y分量for(int y = 0; y < ctx->height; y++) {for(int x = 0; x < ctx->width; x++) {frame->data[0][y*frame->linesize[0] + x] = x + y + i * 3;}}// UV分量for(int y = 0; y < ctx->height; y++) {for(int x = 0; x < ctx->width; x++) {frame->data[1][y*frame->linesize[1] + x] = 128 + y + i * 2;frame->data[2][y*frame->linesize[2] + x] = 64 + x + i * 5;}}frame->pts = i;// 10.编码ret = encode(ctx, frame, pkt, f);if(ret == -1) {goto _ERROR;}}// 10.编码encode(ctx, NULL, pkt, f);static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *out) {int ret = -1;ret = avcodec_send_frame(ctx, frame);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to send\n");goto _END;}while(ret >= 0) {ret = avcodec_receive_frame(ctx, pkt);if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if(ret < 0) {return -1;}fwrite(pkt->data, 1, pkt->size, out);av_packet_unref(pkt);}_END:return 0;
}

这里需要注意:

  • 一定要在外面再执行一次 encode 函数,并把第二个参数置为 NULL,因为有可能缓冲区中还有剩余的数据,如果不执行可能会产生花屏等现象。
  • 在使用av_frame_get_buffer函数分配data的空间之前,一定要给frame分配宽、 高、 格式,只有有了这些参数,才能正确的分配空间

2.音频编码

与视频编码类似,主要修改的地方如下:

  • 输入参数的个数
  • 分配data空间之前,frame参数的设置
  • 生成音频的内容
    // 1.输入参数if(argc < 2) {av_log(NULL, AV_LOG_ERROR, "arguments must be more than 2\n");goto _ERROR;}dst = argv[1];
    // 7.创建AVFrameframe = av_frame_alloc();if(!frame) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}frame->nb_samples = ctx->frame_size;frame->format = ctx->sample_fmt;av_channel_layout_copy(&frame->ch_layout, &ctx->ch_layout);ret = av_frame_get_buffer(frame, 0);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Dont't get buffer: %s\n", av_err2str(ret));goto _ERROR;}
    // 9.生成音频内容float t = 0;float tincr = 2 * M_PI * 440 / ctx->sample_rate;for(int i = 0; i < 200; i++) {ret = av_frame_make_writable(frame);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Could not allocate space\n");goto _ERROR;}samples = (uint16_t *)frame->data[0];for(int j = 0; j < ctx->frame_size; j++) {samples[2*j] = (int)(sin(t) * 10000);for(int k = 1; k < ctx->ch_layout.nb_channels; k++) {samples[2*j+k] = samples[2*j];}t += tincr;}encode(ctx, frame, pkt, f);}

相关的2个函数:

static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) {const enum AVSampleFormat *p = codec->sample_fmt;while(*p != AV_SAMPLE_FMT_NONE) {if(*p == sample_fmt) {return 1;}*p++;}return 0;
}static int select_sample_rate(const AVCodec *codec)
{const int *p;int best_samplerate = 0;if (!codec->supported_samplerates)return 44100;p = codec->supported_samplerates;while (*p) {if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))best_samplerate = *p;p++;}return best_samplerate;
}

3.生成图片

实际上是把提取视频文件和视频编码进行融合和修改,是视频编码的逆向操作,也就是视频解码。

将视频解码成一帧一帧的图片。

核心代码:

static void savePic(unsigned char *buf, int linesize, int width, int height, char* name) {FILE *f;f = fopen(name, "wb");fprintf(f, "P5\n%d %d\n%d\n", width, height, 255);for(int i = 0; i < height; i++) {fwrite(buf + i * linesize, 1, width, f);}fclose(f);
}static int dencode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, const char *fileName) {int ret = -1;char buf[1024];ret = avcodec_send_packet(ctx, pkt);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to send dencode\n");goto _END;}while(ret >= 0) {ret = avcodec_receive_frame(ctx, frame);if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {return 0;} else if(ret < 0) {return -1;}snprintf(buf, sizeof(buf), "%s-%d", fileName, ctx->frame_number);savePic(frame->data[0],frame->linesize[0],frame->width,frame->height,buf);if(pkt) {av_packet_unref(pkt);}}_END:return 0;
}
 // 2.打开多媒体文件ret = avformat_open_input(&pFmtCtx, src, NULL, NULL);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));exit(-1);}// 3.从多媒体文件中找到视频流idx = av_find_best_stream(pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if(idx < 0) {av_log(pFmtCtx, AV_LOG_ERROR, "Does not include VIDEO\n");goto _ERROR;}inStream = pFmtCtx->streams[idx];// 4.查找解码器codec = avcodec_find_encoder_by_name(inStream->codecpar->codec->id);if(!codec) {av_log(NULL, AV_LOG_ERROR, "Could not find Codec\n");goto _ERROR;}// 5.创建解码器上下文ctx = avcodec_alloc_context3(codec);if(!ctx) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}avcodec_parameters_to_context(ctx, inStream->codecpar);// 6.解码器与解码器上下文绑定到一起ret = avcodec_open2(ctx, codec, NULL);if(ret < 0) {av_log(NULL, AV_LOG_ERROR, "Dont't open codec: %s\n", av_err2str(ret));goto _ERROR;}// 7.创建AVFrameframe = av_frame_alloc();if(!frame) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}// 8.创建AVPacketpkt = av_packet_alloc();if(!pkt) {av_log(NULL, AV_LOG_ERROR, "No Memory\n");goto _ERROR;}// 9.从源多媒体文件中读到视频数据到目的文件中while(av_read_frame(pFmtCtx, pkt) >= 0) {if(pkt->stream_index == idx) {decode(ctx, frame, pkt, dst);}}decode(ctx, frame, NULL, dst);

注意:

  • 查找解码器的时候,不要直接写 codec = avcodec_find_encoder_by_name("libx264");,而是应该先获取到输入流,然后根据流查找对应的解码器。
  • 在进行绑定操作之前,也是应该将 inStream中的参数拷贝到 ctx
  • 出现的段错误,是因为再次进行解码的时候,pkt传入的是NULL,此时如果再次释放就会出错,所以应该添加判断

4.生成彩色的BMP图片

上面第3部分生成的图片是黑白的,在这个部分,我们进一步优化,生成彩色的图片。

主要就是在进行解码的时候,传入了一个转换的上下文 swsCtx, 把yuv转换成了 bmp仅此而已,原理很简单。接下来看具体的代码

核心代码:

    // 6.1获得sws上下文swsCtx = sws_getCachedContext(NULL,ctx->width,ctx->height,AV_PIX_FMT_YUV420P,//  ctx->width,//  ctx->height,640, 360,AV_PIX_FMT_BGR24,SWS_BICUBIC, NULL, NULL, NULL);... ... // 9.从源多媒体文件中读到视频数据到目的文件中while(av_read_frame(pFmtCtx, pkt) >= 0) {if(pkt->stream_index == idx) {decode(ctx, swsCtx, frame, pkt, dst);}}decode(ctx, swsCtx, frame, NULL, dst);
static void saveBMP(struct SwsContext *swsCtx, AVFrame *frame, int w, int h, char *name) {int dataSize = w * h * 3;// 1.先进行转换,将YUV frame转换成BGR24 frameAVFrame *frameBGR = av_frame_alloc();frameBGR->width  = w;frameBGR->height = h;frameBGR->format = AV_PIX_FMT_BGR24;av_frame_get_buffer(frameBGR, 0);sws_scale(swsCtx, (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height, frameBGR->data, frameBGR->linesize);// 2.构造 BITMAOINFOHEADERBITMAPINFOHEADER header;header.biSize = sizeof(BITMAPINFOHEADER);header.biWidth = w;header.biHeight = h*(-1);header.biBitCount = 24;header.biCompression = 0;header.biSizeImage = 0;header.biClrImportant = 0;header.biClrUsed = 0;header.biXPelsPerMeter = 0;header.biYPelsPerMeter = 0;header.biPlanes = 1;// 3.构造 BITMAOFILEHEADERBITMAPFILEHEADER bmpFileHeader;bmpFileHeader.bfType = 0x4d42; //'BM';bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+ dataSize;bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);// 4.将数据写到文件FILE* pf = fopen(filename, "wb");fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);fwrite(&header, sizeof(BITMAPINFOHEADER), 1, pf);fwrite(pFrameRGB->data[0], 1, dataSize, pf);fclose(pf);// 5.释放资源av_freep(&pFrameRGB[0]);av_free(pFrameRGB);fclose(pf);
}

注意:

  • 在创建swsCtx上下文的时候,一定要明确转换前的格式和转换后的格式,直接使用ctx里面的内容有可能会出现错误,可以直接明确写出来
  • 输出的图片格式一定要是符合标准的4:3 , 16:9等,原始数据有可能会不符合要求,在这里也是明确给出了,640 x 360
  • 需要构造构造 BITMAOINFOHEADERBITMAOFILEHEADER 这两个头并填充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/8559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入了解 NumPy:深度学习中的数学运算利器

文章目录 1. 导入NumPy2. 创建NumPy数组3. 数组的算术运算4. N维数组4.1 创建和操作多维数组4.2 高维数组 5. NumPy的广播功能5.1 基本广播示例5.2 更复杂的广播示例 6. 访问数组元素6.1 基于索引的访问6.2 遍历数组6.3 基于条件的访问6.4 高级索引6.5 性能考虑 在深度学习和数…

【idea-sprongboot项目】在linux服务器上纯远程开发方式

继上一篇博客【idea-sprongboot项目】SSH连接云服务器进行远程开发-CSDN博客 目录 五、远程开发方式 2&#xff09;纯远程开发方式 步骤 五、远程开发方式 2&#xff09;纯远程开发方式 实现原理&#xff0c; 步骤 &#xff08;1&#xff09;首先&#xff0c;关闭当前正在…

springMVC入门学习

目录 1、 什么是springmvc 2、springmvc工作流程 3、 springmvc快速入门&#xff08;XML版本&#xff09; 4、加载自定义目录下的springmvc.xml配置文件 5、 解析器InternalResourceViewResolver 6、 映射器BeanNameUrlHandlerMapping 7、 适配器SimpleControllerHandle…

el-select 点击按钮滚动到选择框顶部

主要代码是在visibleChange 在这个 popper 里面找到 .el-select-dropdown__list let popper ref.$refs.popper const ref this.$refs.select let dom popper.querySelector(.el-select-dropdown__list) setTimeout(() > { dom.scrollIntoView() }, 800) <templat…

vue3+vite+js 使用pinia -- vue2 vuex的plus版

先交代下基础版本&#xff1a; “node”&#xff1a;“V16.14.1” “vue”: “^3.4.21” “vite”: “^5.2.0” 接入状态store 即 vuex 呃(⊙﹏⊙)vuex这里可以略过了&#xff0c;我在研究完后&#xff0c;才发现vue3出来个pinia&#xff0c;是vuex的升级&#xff0c;体积更小…

JavaScript 数组方法详解

JavaScript数组方法详解 在JavaScript中&#xff0c;数组&#xff08;Array&#xff09;是一种用于存储多个有序值的数据结构。为了方便开发者高效地操作数组&#xff0c;JavaScript提供了一系列内置的方法。本文将详细介绍这些常用的数组方法&#xff0c;帮助读者更好地理解和…

【算法刷题day47】Leetcode:198. 打家劫舍、213. 打家劫舍 II、337. 打家劫舍 III

文章目录 Leetcode 198. 打家劫舍解题思路代码总结 Leetcode 213. 打家劫舍 II解题思路代码总结 Leetcode 337. 打家劫舍 III解题思路代码总结 草稿图网站 java的Deque Leetcode 198. 打家劫舍 题目&#xff1a;198. 打家劫舍 解析&#xff1a;代码随想录解析 解题思路 偷&am…

Python 正则表达式 re.findall()

Python re.findall 正文示例一示例二示例三 正文 调用方法&#xff1a; re.findall(pattern, string, flags0)用法说明&#xff1a; 扫描整个 字符串&#xff0c;找到所有满足匹配样式的字符&#xff0c;将它们集合在一起以列表形式返回。其中这个返回的列表包含空的结果(没有…

CSF(Cloth Simulation Filter)点云地面点滤波

CSF[Cloth Simulation Filter]点云地面点滤波 0.引言1.布料模拟2.修改布料模拟3.布料滤波4.CSF总体过程 0.引言 code 基本原理&#xff1a;原始点云被倒置过来&#xff0c;然后⼀块布从上⽅掉落到倒置的表⾯。通过分析布的节点与相应的LIDAR点之间的相互作⽤&#xff0c;可以确…

2024年华为OD机试真题-数的分解-(C++)-OD统一考试(C卷D卷)

题目描述: 给定一个正整数n,如果能够分解为m(m > 1)个连续正整数之和,请输出所有分解中,m最小的分解。 如果给定整数无法分解为连续正整数,则输出字符串"N"。 输入描述: 输入数据为一整数,范围为(1, 2^30] 输出描述: 比如输入为: 21 输出: 21=10+11 补…

C++中使用Multimap和Vector管理和展示数据

一&#xff1a; 在本文中&#xff0c;我们将探讨如何在C中使用vector和multimap容器来管理一个简单的员工数据系统。我们将创建一个员工类&#xff0c;随机生成员工数据&#xff0c;将员工分组&#xff0c;并展示各组员工的详细信息。此示例展示了C标准模板库&#xff08;STL&…

【中级软件设计师】上午题15-计算机网络

上午题15-计算机网络 1 网络设备2 协议簇3 TCP和UDP4 SMTP和POP35 ARP和RARP6 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;7 URL8 浏览器9 IP地址和子网划分10 IPv611 Windows命令12 路由器 1 网络设备 物理层设备&#xff1a;中继器、集线器&#xff0…

Mysql基础(五)外键约束

一 外键 激励&#xff1a; 每天进步一点点即可 ① 思考 1、在MySQL中,我们知道主键 PRIMARY KEY的主要作用是唯一区分表中的各个行 [记录];思考&#xff1a;但是对于外键 foreign key比较陌生? 那么外键作用以及限制条件和目的呢? ② 外键的定义 1、外键是某个表 A中…

图像处理之SVD检测显示屏缺陷(C++)

图像处理之SVD检测显示屏缺陷&#xff08;C&#xff09; 文章目录 图像处理之SVD检测显示屏缺陷&#xff08;C&#xff09;前言一、SVD算法简介二、代码实现总结 前言 显示屏缺陷检测是机器视觉领域的一处较广泛的应用场景&#xff0c;显示屏主要有LCD和OLED&#xff0c;缺陷类…

基于java,SpringBoot和Vue的智慧校园在线考试留言讨论系统设计

摘要 基于Java, SpringBoot和Vue的智慧校园在线考试留言讨论系统是一个为现代教育需求定制的Web应用&#xff0c;它结合了最新的前后端技术来提供一个互动性强、用户友好的学习和交流平台。该系统旨在通过提供实时留言和讨论功能&#xff0c;增进学生间的互动以及师生之间的沟…

算法打卡day44

今日任务&#xff1a; 1&#xff09;309.最佳买卖股票时机含冷冻期 2&#xff09;714.买卖股票的最佳时机含手续费 3&#xff09;复习day19 309.最佳买卖股票时机含冷冻期 题目链接&#xff1a;309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 给定一…

快来看 2024年ICLR杰出论文奖揭晓啦 值得关注

会议之眼 快讯 在人工智能的浪潮中&#xff0c;国际学习表征会议&#xff08;ICLR&#xff09;无疑是引领学术前沿的重要会议之一&#xff01;ICLR是深度学习领域的顶级会议之一&#xff0c;由深度学习领域的两位巨头Yoshua Bengio和Yann LeCun于2013年创办。 2024年5月6日&a…

NSSCTF中的web

目录 [第五空间 2021]WebFTP [LitCTF 2023]PHP是世界上最好的语言&#xff01;&#xff01; [SWPUCTF 2021 新生赛]PseudoProtocols [LitCTF 2023]导弹迷踪 [NISACTF 2022]easyssrf [第五空间 2021]WebFTP 1.进入页面&#xff0c;发现是登录页面&#xff0c;想到 弱口令&…

反射技术介绍以及底层逻辑使用

反射概述&#xff1a; 1.反射是指对于任何一个Class类&#xff0c;在"运行的时候"都可以直接得到这个类全部成分。 2.在运行时,可以直接得到这个类的构造器对象&#xff1a;Constructor 3.在运行时,可以直接得到这个类的成员变量对象&#xff1a;Field 4.在运行时,可…

大模型微调之 在亚马逊AWS上实战LlaMA案例(六)

大模型微调之 在亚马逊AWS上实战LlaMA案例&#xff08;六&#xff09; 通过 SageMaker Python SDK 进行微调Llama2 可以使用 SageMaker Python SDK 微调 Llama 2 模型。以下是在数据集上微调 Llama 2 7B 的示例代码&#xff1a; import os import boto3 from sagemaker.sessi…