使用 SDL2 渲染屏幕的主要流程:
示例:
extern "C"
{
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
#include "libswscale\swscale.h"
#include "libavutil\imgutils.h"
}
#include "SDL.h"
#include "SDL_thread.h"
#include <iostream>
#include <fstream>
#include <memory>
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// 注册所有的格式和解码器
av_register_all();
// 初始化 SDL 库
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
std::cerr << "Could not initialize SDL - " << SDL_GetError() << std::endl;
return -1;
}
AVFormatContext *pFmtCtx = NULL;
// 打开视频文件,读取文件头信息到 AVFormatContext 结构体中
if (avformat_open_input(&pFmtCtx, lpCmdLine, NULL, NULL) != 0)
{
return -1;
}
// 程序结束时关闭 AVFormatContext
std::shared_ptr<AVFormatContext*> fmtCtxCloser(&pFmtCtx, avformat_close_input);
// 读取流信息到 AVFormatContext->streams 中
// AVFormatContext->streams 是一个数组,数组大小是 AVFormatContext->nb_streams
if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
{
return -1;
}
// 找到第一个视频流
int videoStream = -1;
for (decltype(pFmtCtx->nb_streams) i = 0; i < pFmtCtx->nb_streams; ++i)
{
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if (videoStream == -1)
{
return -1;
}
// 获取解码器上下文
AVCodecParameters *pCodecParams = pFmtCtx->streams[videoStream]->codecpar;
// 获取解码器
AVCodec *pCodec = avcodec_find_decoder(pCodecParams->codec_id);
if (pCodec == NULL)
{
std::cerr << "Unsupported codec!" << std::endl;
return -1;
}
// 解码器上下文
AVCodecContext *pCodecCtx = avcodec_alloc_context3(NULL); // allocate
if (avcodec_parameters_to_context(pCodecCtx, pCodecParams) < 0) // initialize
{
return -1;
}
// 程序结束时关闭解码器
std::shared_ptr<AVCodecContext> codecCtxCloser(pCodecCtx, avcodec_close);
// 打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
return -1;
}
// 创建帧
AVFrame *pFrame = av_frame_alloc();
std::shared_ptr<AVFrame*> frameDeleter(&pFrame, av_frame_free);
// 创建转换后的帧
AVFrame *pFrameYUV = av_frame_alloc();
std::shared_ptr<AVFrame*> frameBGRDeleter(&pFrameYUV, av_frame_free);
const AVPixelFormat destPixFormat = AV_PIX_FMT_BGR24; // AV_PIX_FMT_YUV420P
// 开辟数据存储区
int numBytes = av_image_get_buffer_size(destPixFormat, pCodecCtx->width, pCodecCtx->height, 1);
uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
// 程序结束时释放
std::shared_ptr<uint8_t> bufferDeleter(buffer, av_free);
// 填充 pFrameBGR 中的若干字段(data、linsize等)
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, buffer, destPixFormat,
pCodecCtx->width, pCodecCtx->height, 1);
// 获取图像处理上下文
SwsContext *pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, destPixFormat,
SWS_BICUBIC, NULL, NULL, NULL);
// 程序结束时释放
std::shared_ptr<SwsContext> swsCtxDeleter(pSwsCtx, sws_freeContext);
// 创建 SDL screen
SDL_Window *screen = SDL_CreateWindow("Demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
if (screen == NULL)
{
std::cerr << "Could not create window - " << SDL_GetError() << std::endl;
return -1;
}
std::shared_ptr<SDL_Window> screenDestoryer(screen, SDL_DestroyWindow);
// 创建 Renderer
SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_TARGETTEXTURE);
std::shared_ptr<SDL_Renderer> rendererDestroyer(renderer, SDL_DestroyRenderer);
// 创建 texture
Uint32 sdlPixFormat = SDL_PIXELFORMAT_BGR24; // SDL_PIXELFORMAT_YV12
SDL_Texture *texture = SDL_CreateTexture(renderer, sdlPixFormat,
SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
std::shared_ptr<SDL_Texture> textureDestroyer(texture, SDL_DestroyTexture);
SDL_Event event;
AVPacket packet;
int frameCount = 0;
const int saveFrameIndex = 1;
while (av_read_frame(pFmtCtx, &packet) >= 0) // 将 frame 读取到 packet
{
// 迭代结束后释放 av_read_frame 分配的 packet 内存
std::shared_ptr<AVPacket> packetDeleter(&packet, av_packet_unref);
if (packet.stream_index == videoStream) // 如果读到的是视频流
{
// 使用解码器 pCodecCtx 将 packet 解码
avcodec_send_packet(pCodecCtx, &packet);
// 返回 pCodecCtx 解码后的数据,注意只有在解码完整个 frame 时该函数才返回 0
if (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
{
// 图像转换
sws_scale(pSwsCtx, pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
// 渲染图片
SDL_Rect rc = { 0, 0, pCodecCtx->width, pCodecCtx->height };
SDL_UpdateTexture(texture, &rc, pFrameYUV->data[0], pFrameYUV->linesize[0]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, &rc, &rc);
SDL_RenderPresent(renderer);
SDL_Delay(100); // 延迟100毫秒
}
}
// 处理消息
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
SDL_Quit();
return 0;
}
}
SDL_Quit();
return 0;
}