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

FFmpeg 深入精讲(三)FFmpeg 中级开发

1 FFmpeg H264 解码

1.1 添加头文件

libavcodec/avcodec.h

1.2 常用数据结构

1.2.1 AVCodec 编码器结构体

1.2.2 AVCodecContext 编码器上下文

1.2.3 AVFrame 解码后的帧

1.3 结体体内存的分配与释放

av_frame_alloc()
av_frame_free()avcodec_alloc_context3()
avcode_free_context()

1.4 解码步骤

1.4.1 查找解码器

avcodec_find_decoder

1.4.2 打开解码器

avcodec_open2

1.4.3 解码

avcodec_decode_video2

2 FFmpeg H264 编码

2.1 实例

#include <libavutil/log.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>static int encodec(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *out_file)
{int ret = -1;ret = avcodec_send_frame(ctx, frame);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Failed to send frame to encodec: %s \n", av_err2str(ret));goto _END;}while (ret >= 0){ret = avcodec_receive_packet(ctx, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){return 0;}else if (ret < 0){return -1;}fwrite(packet->data, 1, packet->size, out_file);av_packet_unref(packet);}_END:return 0;
}int main(int argc, char* argv[])
{FILE *fd = NULL;int ret = -1;char *dst = NULL;char *codec_name = NULL;const AVCodec *codec = NULL;AVCodecContext *ctx = NULL;AVFrame *frame = NULL;AVPacket *packet = NULL;av_log_set_level(AV_LOG_DEBUG);// 1 输入参数if (argc < 3){av_log(NULL, AV_LOG_ERROR, "arguments must be more than 3 \n");goto _ERROR;}dst = argv[1];codec_name = argv[2];// 2 查找编码器//codec = avcodec_find_decoder_by_name(codec_name);codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec){av_log(NULL, AV_LOG_ERROR, "do not find Codec: %s \n", codec_name);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, "Do not open codec: %s \n", av_err2str(ret));goto _ERROR;}// 6 创建输出文件fd = fopen(dst, "wb");if (!fd){av_log(NULL, AV_LOG_ERROR, "Do not 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, "Could not allocate the video frame: %s \n", av_err2str(ret));goto _ERROR;}// 8 创建 AVPacketpacket = av_packet_alloc();if (!packet){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;}}// UVfor (int y = 0; y < ctx->height / 2; y++){for (int x = 0; x < ctx->width / 2; x++){frame->data[1][y*frame->linesize[1] + x] = 128 + y + i * 2;frame->data[2][y*frame->linesize[2] + x] = 64 + y + i * 2;}}frame->pts = i;}// 10 编码ret = encodec(ctx, frame, packet, fd);if (ret < 0){goto _ERROR;}// 11 清缓冲区数据encodec(ctx, NULL, packet, fd);_ERROR:if (ctx){avcodec_free_context(&ctx);ctx = NULL;}if (frame){av_frame_free(&frame);frame = NULL;}if (packet){av_packet_free(&packet);packet = NULL;}if (fd){fclose(fd);fd = NULL;}return 0;
}

3 FFmpeg AAC 解码

4 FFmpeg AAC 编码

#include <libavutil/log.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>static int select_best_sample_rate(const AVCodec *codec)
{const int *p = NULL;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;
}static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{const enum AVSampleFormat *p = codec->sample_fmts;while (*p != AV_SAMPLE_FMT_NONE){if (*p == sample_fmt){return 1;}p++;}return 0;
}static int encodec(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *out_file)
{int ret = -1;ret = avcodec_send_frame(ctx, frame);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Failed to send frame to encodec: %s \n", av_err2str(ret));goto _END;}while (ret >= 0){ret = avcodec_receive_packet(ctx, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){return 0;}else if (ret < 0){return -1;}fwrite(packet->data, 1, packet->size, out_file);av_packet_unref(packet);}_END:return 0;
}int main(int argc, char* argv[])
{FILE *fd = NULL;int ret = -1;char *dst = NULL;const AVCodec *codec = NULL;AVCodecContext *ctx = NULL;AVFrame *frame = NULL;AVPacket *packet = NULL;int32_t *samples = NULL;av_log_set_level(AV_LOG_DEBUG);// 1 输入参数if (argc < 2){av_log(NULL, AV_LOG_ERROR, "arguments must be more than 2 \n");goto _ERROR;}dst = argv[1];// 2 查找编码器//codec = avcodec_find_decoder_by_name("libfdk-aac");codec = avcodec_find_encoder(AV_CODEC_ID_AAC);if (!codec){av_log(NULL, AV_LOG_ERROR, "do not find Codec: \n");goto _ERROR;}// 3 创建编码器上下文ctx = avcodec_alloc_context3(codec);if (!ctx){av_log(NULL, AV_LOG_ERROR, "no memory. \n");goto _ERROR;}// 4 设置编码器参数ctx->bit_rate = 64000;ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;if (!check_sample_fmt(codec, ctx->sample_fmt)){av_log(NULL, AV_LOG_ERROR, "Encoder does not support sample format. \n");goto _ERROR;}ctx->sample_rate = select_best_sample_rate(codec);av_channel_layout_copy(&ctx->ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO);// 5 将编码器与编码器上下文绑定一起ret = avcodec_open2(ctx, codec, NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Do not open codec: %s \n", av_err2str(ret));goto _ERROR;}// 6 创建输出文件fd = fopen(dst, "wb");if (!fd){av_log(NULL, AV_LOG_ERROR, "Do not 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->nb_samples = ctx->frame_size;frame->format = ctx->sample_fmt;frame->sample_rate = ctx->sample_rate;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, "Could not allocate the video frame: %s \n", av_err2str(ret));goto _ERROR;}// 8 创建 AVPacketpacket = av_packet_alloc();if (!packet){av_log(NULL, AV_LOG_ERROR, "no memory. \n");goto _ERROR;}// 9 生成 audio 内容float t = 0;float tiner = 2 * M_PI * 440 / ctx->sample_rate;for (int i = 0; i < 200; i++){ret = av_frame_make_writable(frame);if (ret < 0){goto _ERROR;}samples = (int32_t*)frame->data[0]; for (int j = 0; j < ctx->frame_size; j++){samples[4*j] = (int)(sin(t) * 10000);for (int k  = 1; k < ctx->ch_layout.nb_channels; k++){samples[4*j + k] = samples[4*j];}t += tiner;}ret = encodec(ctx, frame, packet, fd);if (ret < 0){break;}}// 11 清缓冲区数据encodec(ctx, NULL, packet, fd);_ERROR:if (ctx){avcodec_free_context(&ctx);ctx = NULL;}if (frame){av_frame_free(&frame);frame = NULL;}if (packet){av_packet_free(&packet);packet = NULL;}if (fd){fclose(fd);fd = NULL;}return 0;
}

5 生成图片BMP

#include <stdio.h>
#include <libavutil/log.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {WORD  bfType;DWORD bfSize;WORD  bfReserved1;WORD  bfReserved2;DWORD bfOffBits;
} BITMAPFILEHEADER, *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;typedef struct tagBITMAPINFOHEADER {DWORD biSize;LONG  biWidth;LONG  biHeight;WORD  biPlanes;WORD  biBitCount;DWORD biCompression;DWORD biSizeImage;LONG  biXPelsPerMeter;LONG  biYPelsPerMeter;DWORD biClrUsed;DWORD biClrImportant;
} BITMAPINFOHEADER, *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;static void saveBMP(struct SwsContext *swsCtx, AVFrame *frame, int w, int h,char *name){FILE *f = NULL;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. 构造 BITMAPINFOHEADERBITMAPINFOHEADER infoHeader;infoHeader.biSize = sizeof(BITMAPINFOHEADER);infoHeader.biWidth = w;infoHeader.biHeight = h * (-1);infoHeader.biBitCount = 24;infoHeader.biCompression = 0;infoHeader.biSizeImage = 0;infoHeader.biClrImportant = 0;infoHeader.biClrUsed = 0;infoHeader.biXPelsPerMeter = 0;infoHeader.biYPelsPerMeter = 0;infoHeader.biPlanes = 1;//3. 构造 BITMAPFILEHEADERBITMAPFILEHEADER fileHeader = {0, };fileHeader.bfType = 0x4d42; //'BM'fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dataSize;fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//4. 将数据写到文件f = fopen(name, "wb");fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f);fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f);fwrite(frameBGR->data[0], 1, dataSize, f);//5. 释放资源fclose(f);av_freep(&frameBGR->data[0]);av_free(frameBGR);}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 decode(AVCodecContext *ctx, struct SwsContext *swsCtx, 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 frame to decoder!\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.bmp", fileName, ctx->frame_num);saveBMP(swsCtx, frame, 640, 360, buf);/*savePic(frame->data[0],frame->linesize[0],frame->width,frame->height,buf);*/if(pkt) {av_packet_unref(pkt);}}
_END:return 0;
}int main(int argc, char *argv[]){int ret = -1;int idx = -1;//1. 处理一些参数;char* src;char* dst;const AVCodec *codec = NULL;AVCodecContext *ctx = NULL;AVFormatContext *pFmtCtx = NULL;AVStream *inStream = NULL;AVFrame *frame = NULL;AVPacket *pkt = NULL;struct SwsContext *swsCtx = NULL;av_log_set_level(AV_LOG_DEBUG);if(argc < 3){ //argv[0], extra_audio av_log(NULL, AV_LOG_INFO, "arguments must be more than 3!\n");exit(-1);}src = argv[1];dst = argv[2];//2. 打开多媒体文件if((ret = avformat_open_input(&pFmtCtx, src, NULL, NULL)) < 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 audio stream!\n");goto _ERROR;}inStream = pFmtCtx->streams[idx];//4. 查找解码器codec = avcodec_find_decoder(inStream->codecpar->codec_id);if(!codec){av_log(NULL, AV_LOG_ERROR, "Could not find libx264 Codec");goto _ERROR;}//5. 创建解码器上下文ctx = avcodec_alloc_context3(NULL);if(!ctx){av_log(NULL, AV_LOG_ERROR, "NO MEMRORY\n");goto _ERROR;}ret = avcodec_parameters_to_context(ctx, inStream->codecpar);if(ret < 0){av_log(ctx, AV_LOG_ERROR, "Could not copyt codecpar to codec ctx!\n");goto _ERROR;}//5. 解码器与解码器上下文绑定到一起ret = avcodec_open2(ctx, codec , NULL);if(ret < 0) {av_log(ctx, AV_LOG_ERROR, "Don't open codec: %s \n", av_err2str(ret));goto _ERROR;}//5.1 获得SWS上下文swsCtx = sws_getContext(ctx->width,  //src widthctx->height, //src heightAV_PIX_FMT_YUV420P,//src pix fmt640,  //dst width360, //dst heightAV_PIX_FMT_BGR24, //dst pix fmtSWS_BICUBIC, NULL, NULL, NULL);if(!swsCtx){av_log(NULL, AV_LOG_ERROR, "Could not get Swscale Context!\n");goto _ERROR;}//6. 创建AVFrameframe = av_frame_alloc();if(!frame){av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");goto _ERROR;}//7. 创建AVPacketpkt = av_packet_alloc();if(!pkt){av_log(NULL, AV_LOG_ERROR, "NO MEMORY!\n");goto _ERROR;}//8. 从源多媒体文件中读到视频数据while(av_read_frame(pFmtCtx, pkt) >= 0) {if(pkt->stream_index == idx) {decode(ctx, swsCtx, frame, pkt, dst);}}decode(ctx, swsCtx, frame, NULL, dst);//9. 将申请的资源释放掉
_ERROR:if(pFmtCtx){avformat_close_input(&pFmtCtx);pFmtCtx = NULL;}if(ctx){avcodec_free_context(&ctx);ctx = NULL;}if(frame){av_frame_free(&frame);frame = NULL;}if(pkt){av_packet_free(&pkt);pkt = NULL;}if(swsCtx){sws_freeContext(swsCtx);swsCtx = NULL;}printf("hello, world!\n");return 0;
}

http://www.dtcms.com/a/394008.html

相关文章:

  • AI驱动下的蛋白质设计
  • ARM基本汇编操作指令
  • 电商搜索 API 的优化与性能提升:从瓶颈突破到体验升级
  • 使用DeepSeek辅助测试一个rust编写的postgresql协议工具包convergence
  • 【00】EPGF 架构搭建教程之 总揽篇
  • 深度剖析 vector 底层原理:从手写实现到核心技术点全解析
  • 嵌入式开发学习日志29——stm32之定时器中断
  • 通俗范畴论17.3 向量空间的对偶与双对偶
  • 表格 表头增加悬浮提示内容
  • emacs段落重排快捷键
  • 第九届人单合一模式引领论坛举行 构建AI时代的智能交互生态
  • 不用搜驱动!惠普官方工具:自动适配,坏了直接重装
  • JAVA八股文——java虚拟机栈
  • 华为MindSpeed 训练加速库:架构解析
  • Java的Stream实现对list实用操作【持续更新】
  • 【AI智能体】Dify集成 Echarts实现数据报表展示实战详解
  • 【01】EPGF 架构搭建教程之 Anaconda 安装指南
  • 深度学习周报(9.15~9.21)
  • MCP实战:使用 LangGraph 和 MCP 协议无缝集成外部工具
  • 【嵌入式总线通信协议库】
  • 06.【Linux系统编程】命令行参数(给main传参)、环境变量(概念+使用)、进程的虚拟地址空间(用户实际访问的空间)
  • esp32墨水屏天气预测学习
  • LabelImg 操作指南:提高标注速度
  • redhat7.2迁移ssh免密到麒麟v10
  • Linux基操
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘protobuf’ 问题
  • EXCEL中公式和文字混合和数字自动变成大写金额
  • Linux软件安装与项目部署
  • Config-配置中心2.0
  • Meta 开源 MobileLLM-R1 系列小参数高效模型,颠覆大模型竞赛