目录
- 1. sws_getContext
- 2. sws_scale
- 3. 编码流程
-
- 3.1 打开视频文件
- 3.2 初始化解码器
- 3.3 初始化音视频码流解析器
- 3.4 初始化SDL部分
- 3.5 初始化图像转换上下文
- 3.6 读取文件数据
- 3.7 解析音视频裸流数据
- 3.8 解码
- 3.9 像素格式转换
- 3.10 SDL渲染
- 3.11 冲刷解码器
- 3.12 内存回收
- 4. 代码合集
- 5. 视频效果截图
- 6. 进阶
-
- 6.1 实现源码
- 6.2 运行截图
在ffmpeg中如果需要修改图像的像素格式,或者修改图像的宽高大小,最简单的就是使用sws_scale函数进行转换!
先前学习了ffmpeg的 编码 和 解码 操作,基于此,再来了解一下像素格式的转换!
调用流程:
sws_getContext() → 初始化转换上下文(只调用一次)
↓
sws_scale() → 执行实际转换(每帧调用)
↓
sws_freeContext() → 释放上下文(结束时调用)
运用流程:
解析出一帧 发送解码 接收解码 像素格式转换
av_parser_parse2() → avcodec_send_packet() → avcodec_receive_frame() → sws_scale()
1. sws_getContext
sws_getContext 是 FFmpeg 的 libswscale 库中用于初始化图像转换上下文的核心函数。它的主要任务是创建一个 SwsContext 结构体。
①函数原型
struct SwsContext *sws_getContext(
int srcW, // 源图像宽度
int srcH, // 源图像高度
enum AVPixelFormat srcFormat, // 源像素格式
int dstW, // 目标宽度
int dstH, // 目标高度
enum AVPixelFormat dstFormat, // 目标像素格式
int flags, // 缩放算法标志
SwsFilter *srcFilter, // 源滤波器(通常 NULL)
SwsFilter *dstFilter, // 目标滤波器(通常 NULL)
const double *param // 算法参数(通常 NULL)
);
②参数说明
| srcW | int | 源图像宽度(像素) |
| srcH | int | 源图像高度(像素) |
| srcFormat | enum AVPixelFormat | 源像素格式(如 AV_PIX_FMT_YUV420P、AV_PIX_FMT_RGB24) |
| dstW | int | 目标图像宽度(像素) |
| dstH | int | 目标图像高度(像素) |
| dstFormat | enum AVPixelFormat | 目标像素格式(如 AV_PIX_FMT_YUV420P、AV_PIX_FMT_RGB24) |
| flags | int | 缩放算法标志(如 SWS_BILINEAR、SWS_BICUBIC、SWS_LANCZOS) |
| srcFilter | SwsFilter * | 源滤波器,一般不用,填 NULL |
| dstFilter | SwsFilter * | 目标滤波器,一般不用,填 NULL |
| param | const double * | 算法参数,一般不用,填 NULL |
常用 flags 算法
| SWS_FAST_BILINEAR | 快速双线性 | 快,质量尚可 |
| SWS_BILINEAR | 双线性 | 中等 |
| SWS_BICUBIC | 双三次 | 较慢,质量好 |
| SWS_LANCZOS | Lanczos | 慢,质量最好 |
| SWS_POINT | 最近邻 | 最快,缩放时用 |
| SWS_AREA | 面积法缩小 | 缩小图片推荐 |
③返回值
- 成功:返回一个指向 SwsContext 结构体的指针
- 失败:返回 NULL
④使用示例
// yuv420转rgb24
SwsContext *sws_ctx = sws_getContext(800, 600, AV_PIX_FMT_YUV420P, // 源格式
640, 480, AV_PIX_FMT_RGB24, // 目标格式
SWS_BILINEAR, NULL, NULL, NULL); // SWS_BILINEAR 双线性插值算法
if (!sws_ctx) {
std::cout << "sws_getContext failed!" << std::endl;
return –1;
}
2. sws_scale
使用 sws_getContext 创建的上下文,将源图像数据转换为目标图像数据。
①函数原型
int sws_scale(
struct SwsContext *c, // 上下文
const uint8_t *const srcSlice[], // 源数据指针数组
const int srcStride[], // 源每行字节数(linesize)
int srcSliceY, // 从第几行开始处理
int srcSliceH, // 处理多少行
uint8_t *const dst[], // 目标数据指针数组
const int dstStride[] // 目标每行字节数
);
②参数说明
| c | SwsContext * | 由 sws_getContext 创建的转换上下文 |
| srcSlice[] | const uint8_t *const [] | 源图像各平面数据指针数组(对应 frame->data[]) |
| srcStride[] | const int [] | 源各平面每行字节数(对应 frame->linesize[],注意是字节不是像素) |
| srcSliceY | int | 从源图像的第几行开始处理(通常填 0) |
| srcSliceH | int | 从源图像处理多少行(通常填 srcH,即全部) |
| dst[] | uint8_t *const [] | 目标图像各平面数据指针数组(对应 dst_frame->data[]) |
| dstStride[] | const int [] | 目标各平面每行字节数(对应 dst_frame->linesize[]) |
③返回值
- 成功:返回输出的行数
- 失败:返回一个负数错误码
④使用示例 目标接收的frame_rgb24,需要由使用者分配内存;
// 定义并且分配内存,用于接收转换后的一帧图像
AVFrame *frame_rgb24 = av_frame_alloc();
frame_rgb24->width = 640;
frame_rgb24->height = 480;
frame_rgb24->format = AV_PIX_FMT_RGB24;
av_frame_get_buffer(frame_rgb24, 32);// 分配内存
// 像素格式转换
int height = sws_scale(sws_ctx, // 由 sws_getContext 创建的 SwsContext 指针
frame->data, // 源图像数据指针数组
frame->linesize, // 源图像每行的字节数
0, // 处理起始行,通常为 0,表示从第一行开始
frame->height, // 要处理的行数,通常为源图像的高度
frame_rgb24->data, // 目标图像数据指针数组
frame_rgb24->linesize // 目标图像每行的字节数
);
if (0 > height) {
std::cout << "sws_scale failed! ret = " << ret << std::endl;
goto FAILED;
}
3. 编码流程
3.1 打开视频文件
std::ifstream ifs;
ifs.open("800_600_25.h264", std::ios::binary);
if (!ifs.is_open()) {
std::cout << "ifs open file failed!\\n";
std::cout << "Error code: " << errno << std::endl;
std::cout << "Error message: " << strerror(errno) << std::endl;
return –1;
}
3.2 初始化解码器
// 查找解码器
const AVCodec *decodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!decodec) {
std::cout << "avcodec_find_decoder failed!" << std::endl;
return –1;
}
// 创建解码器上下文
AVCodecContext *decodec_ctx = avcodec_alloc_context3(decodec);
if (!decodec_ctx) {
std::cout << "avcodec_alloc_context3 failed!" << std::endl;
return –1;
}
// 打开解码器上下文
int ret = avcodec_open2(decodec_ctx, decodec, 0);
if (0 != ret) {
std::cout << "avcodec_open2 failed!" << std::endl;
PrintErr(ret);
return –1;
}
3.3 初始化音视频码流解析器
AVCodecParserContext *parser_ctx = av_parser_init(AV_CODEC_ID_H264);
if (!parser_ctx) {
std::cout << "av_parser_init failed!" << std::endl;
return –1;
}
3.4 初始化SDL部分
SDL_Init(SDL_INIT_VIDEO);
// 创建窗口
SDL_Window *sdl_win = SDL_CreateWindow("xxxxxxxx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480, SDL_WINDOW_OPENGL);
// 创建渲染器
SDL_Renderer *render = SDL_CreateRenderer(sdl_win, –1, SDL_RENDERER_TARGETTEXTURE);
// 创建 纹理|材质
SDL_Texture *texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING,
640, 480);
// 设置线性插值(抗锯齿)
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
3.5 初始化图像转换上下文
SwsContext *sws_ctx = sws_getContext(800, 600, AV_PIX_FMT_YUV420P, // 源格式
640, 480, AV_PIX_FMT_RGB24, // 目标格式
SWS_BILINEAR, NULL, NULL, NULL); // SWS_BILINEAR 双线性插值算法
if (!sws_ctx) {
std::cout << "sws_getContext failed! ret = " << ret << std::endl;
PrintErr(ret);
return –1;
}
3.6 读取文件数据
while (1) {
// 读取文件数据
ifs.read((char *)buf, sizeof(buf));
size_in = ifs.gcount();
// 读取失败或读取完毕
if (0 >= size_in) {
std::cout << "ifs.read end!" << std::endl;
break;
}
// other code …
}
3.7 解析音视频裸流数据
data_in = buf;
while (0 < size_in) {
// 解析音视频裸流数据,从中分离出一帧一帧的压缩编码数据
int re = av_parser_parse2(parser_ctx, decodec_ctx, &pkt->data, &pkt->size,
data_in, size_in, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
data_in += re; // 处理了多少,指针移位多少
size_in -= re; // 处理了多少,减去多少,小于等于0,则说明读取的字节已经处理完毕
if (pkt->size) {
// other code …
}
}
3.8 解码
// 发送解码
ret = avcodec_send_packet(decodec_ctx, pkt);
if (0 != ret) {
std::cout << "avcodec_send_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
while (0 <= ret) {
// 接收解码
ret = avcodec_receive_frame(decodec_ctx, frame);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
av_packet_unref(pkt);
break;
}
if (0 > ret) {
std::cout << "avcodec_receive_frame failed! ret = " << ret << std::endl;
PrintErr(ret);
av_packet_unref(pkt);
goto FAILED;
}
// other code …
}
3.9 像素格式转换
int height = sws_scale(sws_ctx, // 由 sws_getContext 创建的 SwsContext 指针
frame->data, // 源图像数据指针数组
frame->linesize, // 源图像每行的字节数
0, // 处理起始行,通常为 0,表示从第一行开始
frame->height, // 要处理的行数,通常为源图像的高度
frame_rgb24->data, // 目标图像数据指针数组
frame_rgb24->linesize // 目标图像每行的字节数
);
if (0 > height) {
std::cout << "sws_scale failed! ret = " << ret << std::endl;
goto FAILED;
}
frame_rgb24->pts = pts++;
3.10 SDL渲染
SDL_UpdateTexture(texture, NULL, frame_rgb24->data[0], frame_rgb24->linesize[0]);
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);
3.11 冲刷解码器
ret = avcodec_send_packet(decodec_ctx, NULL);
if (0 != ret) {
std::cout << "avcodec_send_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
while (0 <= ret) {
ret = avcodec_receive_frame(decodec_ctx, frame);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
break;
}
if (0 > ret) {
std::cout << "avcodec_receive_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
// 像素格式转换
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, frame_rgb24->data, frame_rgb24->linesize);
frame_rgb24->pts = pts++;
std::cout << pts << "- ";
// 渲染rgb24类型
SDL_UpdateTexture(texture, NULL, frame_rgb24->data[0], frame_rgb24->linesize[0]);
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);
}
3.12 内存回收
FAILED:
// 内存回收
ifs.close();
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&decodec_ctx);
av_parser_close(parser_ctx);
av_frame_free(&frame_rgb24);
sws_free_context(&sws_ctx);
if (texture) {
SDL_DestroyTexture(texture);
}
if (render) {
SDL_DestroyRenderer(render);
}
if (sdl_win) {
SDL_DestroyWindow(sdl_win);
}
SDL_Quit();
4. 代码合集
读取本地h.264视频文件,解码后,由800×600,YUV420P转换为640×480,RGB24,再通过SDL2渲染;
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
}
#include "SDL.h"
// 打印错误信息
static void PrintErr(int ret) {
char buf[1024] = { 0 };
av_strerror(ret, buf, sizeof(buf));
std::cout << buf << std::endl;
}
#undef main
int main(void)
{
std::cout << av_version_info() << std::endl;
// 打开视频文件
std::ifstream ifs;
ifs.open("800_600_25.h264", std::ios::binary);
if (!ifs.is_open()) {
std::cout << "ifs open file failed!\\n";
std::cout << "Error code: " << errno << std::endl;
std::cout << "Error message: " << strerror(errno) << std::endl;
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化解码器
// 查找解码器
const AVCodec *decodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!decodec) {
std::cout << "avcodec_find_decoder failed!" << std::endl;
return –1;
}
// 创建解码器上下文
AVCodecContext *decodec_ctx = avcodec_alloc_context3(decodec);
if (!decodec_ctx) {
std::cout << "avcodec_alloc_context3 failed!" << std::endl;
return –1;
}
// 打开解码器上下文
int ret = avcodec_open2(decodec_ctx, decodec, 0);
if (0 != ret) {
std::cout << "avcodec_open2 failed!" << std::endl;
PrintErr(ret);
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化音视频码流解析器
AVCodecParserContext *parser_ctx = av_parser_init(AV_CODEC_ID_H264);
if (!parser_ctx) {
std::cout << "av_parser_init failed!" << std::endl;
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// 初始化SDL部分
SDL_Init(SDL_INIT_VIDEO);
// 创建窗口
SDL_Window *sdl_win = SDL_CreateWindow("xxxxxxxx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480, SDL_WINDOW_OPENGL);
// 创建渲染器
SDL_Renderer *render = SDL_CreateRenderer(sdl_win, –1, SDL_RENDERER_TARGETTEXTURE);
// 创建 纹理|材质
SDL_Texture *texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING,
640, 480);
// 设置线性插值(抗锯齿)
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化图像转换上下文
// yuv420转rgb24
SwsContext *sws_ctx = sws_getContext(800, 600, AV_PIX_FMT_YUV420P, // 源格式
640, 480, AV_PIX_FMT_RGB24, // 目标格式
SWS_BILINEAR, NULL, NULL, NULL); // SWS_BILINEAR 双线性插值算法
if (!sws_ctx) {
std::cout << "sws_getContext failed! ret = " << ret << std::endl;
PrintErr(ret);
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
int pts = 0;
unsigned char buf[4096] = { 0 };
unsigned char *data_in = nullptr;
int size_in = 0;
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
// 定义并且分配内存,用于接收转换后的一帧图像
AVFrame *frame_rgb24 = av_frame_alloc();
frame_rgb24->width = 640; // encodec_ctx->width
frame_rgb24->height = 480; // encodec_ctx->height
frame_rgb24->format = AV_PIX_FMT_RGB24; // encodec_ctx->pix_fmt
av_frame_get_buffer(frame_rgb24, 32);
while (1) {
// 读取文件数据
ifs.read((char *)buf, sizeof(buf));
size_in = ifs.gcount();
// 读取失败或读取完毕
if (0 >= size_in) {
std::cout << "ifs.read end!" << std::endl;
break;
}
data_in = buf;
while (0 < size_in) {
// 解析音视频裸流数据,从中分离出一帧一帧的压缩编码数据
int re = av_parser_parse2(parser_ctx, decodec_ctx, &pkt->data, &pkt->size,
data_in, size_in, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
data_in += re; // 处理了多少,指针移位多少
size_in -= re; // 处理了多少,减去多少,小于等于0,则说明读取的字节已经处理完毕
if (pkt->size) {
// 发送解码
ret = avcodec_send_packet(decodec_ctx, pkt);
if (0 != ret) {
std::cout << "avcodec_send_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
while (0 <= ret) {
// 接收解码
ret = avcodec_receive_frame(decodec_ctx, frame);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
av_packet_unref(pkt);
break;
}
if (0 > ret) {
std::cout << "avcodec_receive_frame failed! ret = " << ret << std::endl;
PrintErr(ret);
av_packet_unref(pkt);
goto FAILED;
}
av_packet_unref(pkt);
// 像素格式转换
int height = sws_scale(sws_ctx, // 由 sws_getContext 创建的 SwsContext 指针
frame->data, // 源图像数据指针数组
frame->linesize, // 源图像每行的字节数
0, // 处理起始行,通常为 0,表示从第一行开始
frame->height, // 要处理的行数,通常为源图像的高度
frame_rgb24->data, // 目标图像数据指针数组
frame_rgb24->linesize // 目标图像每行的字节数
);
if (0 > height) {
std::cout << "sws_scale failed! ret = " << ret << std::endl;
goto FAILED;
}
frame_rgb24->pts = pts++;
std::cout << pts << " ";
// 渲染rgb24类型
SDL_UpdateTexture(texture, NULL, frame_rgb24->data[0], frame_rgb24->linesize[0]);
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);
// 睡眠 40 毫秒
//std::this_thread::sleep_for(std::chrono::milliseconds(40));
}
}
}
}
// 冲刷解码器
ret = avcodec_send_packet(decodec_ctx, NULL);
if (0 != ret) {
std::cout << "avcodec_send_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
while (0 <= ret) {
ret = avcodec_receive_frame(decodec_ctx, frame);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
break;
}
if (0 > ret) {
std::cout << "avcodec_receive_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
// 像素格式转换
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, frame_rgb24->data, frame_rgb24->linesize);
frame_rgb24->pts = pts++;
std::cout << pts << "- ";
// 渲染rgb24类型
SDL_UpdateTexture(texture, NULL, frame_rgb24->data[0], frame_rgb24->linesize[0]);
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);
}
FAILED:
// 内存回收
ifs.close();
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&decodec_ctx);
av_parser_close(parser_ctx);
av_frame_free(&frame_rgb24);
sws_free_context(&sws_ctx);
if (texture) {
SDL_DestroyTexture(texture);
}
if (render) {
SDL_DestroyRenderer(render);
}
if (sdl_win) {
SDL_DestroyWindow(sdl_win);
}
SDL_Quit();
return 0;
}
5. 视频效果截图

6. 进阶
////////////////////////////////////////////////////////////////////////////////////////////////////
/// 需求:
/// 1.读取800_600_25.h264视频文件;
/// 2.解码;
/// 3.将宽高修改为640×480,像素格式从 YUV420P 转换为 YUV444P;
/// 4.编码;
/// 5.写入640_480.h264文件;
////////////////////////////////////////////////////////////////////////////////////////////////////
根据 FFmpeg H264视频编码全流程解析 + FFmpeg H264视频解码全流程解析 再加上本篇学习的内容,可以完成!
6.1 实现源码
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
}
// 打印错误信息
static void PrintErr(int ret) {
char buf[1024] = { 0 };
av_strerror(ret, buf, sizeof(buf));
std::cout << buf << std::endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// 需求:
/// 1.读取800_600_25.h264视频文件;
/// 2.解码;
/// 3.将宽高修改为640×480,像素格式从 YUV420P 转换为 YUV444P;
/// 4.编码;
/// 5.写入640_480.h264文件;
////////////////////////////////////////////////////////////////////////////////////////////////////
int main(void)
{
std::cout << av_version_info() << std::endl;
// 打开视频文件
std::ifstream ifs;
ifs.open("800_600_25.h264", std::ios::binary);
if (!ifs.is_open()) {
std::cout << "ifs open file failed!\\n";
std::cout << "Error code: " << errno << std::endl;
std::cout << "Error message: " << strerror(errno) << std::endl;
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化解码器
// 查找解码器
const AVCodec *decodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!decodec) {
std::cout << "avcodec_find_decoder failed!" << std::endl;
return –1;
}
// 创建解码器上下文
AVCodecContext *decodec_ctx = avcodec_alloc_context3(decodec);
if (!decodec_ctx) {
std::cout << "avcodec_alloc_context3 failed!" << std::endl;
return –1;
}
// 打开解码器上下文
int ret = avcodec_open2(decodec_ctx, decodec, 0);
if (0 != ret) {
std::cout << "avcodec_open2 failed!" << std::endl;
PrintErr(ret);
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化音视频码流解析器
AVCodecParserContext *parser_ctx = av_parser_init(AV_CODEC_ID_H264);
if (!parser_ctx) {
std::cout << "av_parser_init failed!" << std::endl;
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// 打开输出文件
std::ofstream ofd;
ofd.open("640_480.h264", std::ios::binary); // 一定要以二进制方式打开
if (!ofd.is_open()) {
std::cout << "ofd open failed!" << std::endl;
std::cout << "Error code: " << errno << std::endl;
std::cout << "Error message: " << strerror(errno) << std::endl;
return –1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化编码器
// 查找编码器
const AVCodec *encodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!encodec) {
std::cout << "avcodec_find_encoder failed!" << std::endl;
return 0;
}
// 创建编码器上下文
AVCodecContext *encodec_ctx = avcodec_alloc_context3(encodec);
if (!encodec_ctx) {
std::cout << "avcodec_alloc_context3 failed!" << std::endl;
return 0;
}
// 设置基础信息
encodec_ctx->width = 640;
encodec_ctx->height = 480;
encodec_ctx->time_base = { 1, 25 };
encodec_ctx->pix_fmt = AV_PIX_FMT_YUV444P;
// 打开编码器上下文
int re = avcodec_open2(encodec_ctx, encodec, NULL);
if (0 != re) {
std::cout << "avcodec_open2 failed!" << std::endl;
PrintErr(re);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/// 初始化图像转换上下文
// yuv420转yuv444
SwsContext *sws_ctx = sws_getContext(800, 600, AV_PIX_FMT_YUV420P, // 源格式
640, 480, AV_PIX_FMT_YUV444P, // 目标格式
SWS_BILINEAR, NULL, NULL, NULL); // SWS_BILINEAR 双线性插值算法
/////////////////////////////////////////////////////////////////////////////////////////////////
int pts = 0;
unsigned char buf[4096] = { 0 };
unsigned char *data_in = nullptr;
int size_in = 0;
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
// 定义并且分配内存,用于接收转换后的一帧图像
AVFrame *frame_yuv444 = av_frame_alloc();
frame_yuv444->width = 640; // encodec_ctx->width
frame_yuv444->height = 480; // encodec_ctx->height
frame_yuv444->format = AV_PIX_FMT_YUV444P; // encodec_ctx->pix_fmt
av_frame_get_buffer(frame_yuv444, 32);
while (1) {
// 读取文件数据
ifs.read((char *)buf, sizeof(buf));
size_in = ifs.gcount();
// 读取失败或读取完毕
if (0 >= size_in) {
std::cout << "ifs.read end!" << std::endl;
break;
}
data_in = buf;
while (0 < size_in) {
// 解析音视频裸流数据,从中分离出一帧一帧的压缩编码数据
int re = av_parser_parse2(parser_ctx, decodec_ctx, &pkt->data, &pkt->size,
data_in, size_in, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
data_in += re; // 处理了多少,指针移位多少
size_in -= re; // 处理了多少,减去多少,小于等于0,则说明读取的字节已经处理完毕
if (pkt->size) {
// 发送解码
ret = avcodec_send_packet(decodec_ctx, pkt);
if (0 != ret) {
std::cout << "avcodec_send_packet failed! ret = " << ret << std::endl;
PrintErr(ret);
goto FAILED;
}
while (0 <= ret) {
// 接收解码
ret = avcodec_receive_frame(decodec_ctx, frame);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) {
av_packet_unref(pkt);
break;
}
if (0 > ret) {
std::cout << "avcodec_receive_frame failed! ret = " << ret << std::endl;
PrintErr(ret);
av_packet_unref(pkt);
goto FAILED;
}
av_packet_unref(pkt);
// 像素格式转换
int height = sws_scale(sws_ctx, // 由 sws_getContext 创建的 SwsContext 指针
frame->data, // 源图像数据指针数组
frame->linesize, // 源图像每行的字节数
0, // 处理起始行,通常为 0,表示从第一行开始
frame->height, // 要处理的行数,通常为源图像的高度
frame_yuv444->data, // 目标图像数据指针数组
frame_yuv444->linesize // 目标图像每行的字节数
);
if (0 > height) {
std::cout << "sws_scale failed! ret = " << ret << std::endl;
goto FAILED;
}
frame_yuv444->pts = pts++;
// 发送编码
int re = avcodec_send_frame(encodec_ctx, frame_yuv444);
if (re < 0) {
std::cout << "avcodec_send_frame failed!" << std::endl;
PrintErr(re);
break;
}
while (re >= 0) {
// 接收编码后的数据包
int re = avcodec_receive_packet(encodec_ctx, pkt);
if (re == AVERROR(EAGAIN) || re == AVERROR_EOF) {
// 目前还没有数据或已经到结尾
av_packet_unref(pkt);
break;
} else if (re < 0) {
std::cout << "avcodec_receive_packet failed!" << std::endl;
PrintErr(re);
av_packet_unref(pkt);
return –2;
}
std::cout << pts << " ";
ofd.write((char *)pkt->data, pkt->size);
// 释放
av_packet_unref(pkt);
}
// 睡眠 40 毫秒
// std::this_thread::sleep_for(std::chrono::milliseconds(40));
}
}
}
}
// 冲刷编码器
re = avcodec_send_frame(encodec_ctx, NULL);
if (re < 0) {
std::cout << "avcodec_send_frame failed!" << std::endl;
PrintErr(re);
return –3;
}
if (re >= 0 ) {
while (1) {
re = avcodec_receive_packet(encodec_ctx, pkt);
if (re == AVERROR(EAGAIN)) {
av_packet_unref(pkt);
continue;
} else if (re == AVERROR_EOF) {
av_packet_unref(pkt);
break;
} else if (re < 0) {
av_packet_unref(pkt);
break;
}
std::cout << pts << "- ";
ofd.write((char *)pkt->data, pkt->size);
av_packet_unref(pkt);
}
}
FAILED:
// 内存回收
ifs.close();
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&decodec_ctx);
av_parser_close(parser_ctx);
ofd.close();
av_frame_free(&frame_yuv444);
avcodec_free_context(&encodec_ctx);
sws_free_context(&sws_ctx);
return 0;
}
6.2 运行截图

网硕互联帮助中心




![打卡信奥刷题(3491)用C++实现信奥题 P10734 [NOISG 2019 Prelim] Experimental Charges-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/08/20260805014046-6a72949eaf30f-220x150.png)

评论前必须登录!
注册