SDL2常用函数&结构分析:SDL_Texture&SDL_CreateTexture&SDL_UpdateTexture

SDL_Texture

SDL_Texture是计算机图形图像中,如何在屏幕上显示图像的一个数据的抽象。中文也已翻译为纹理。这种概念在OpenGL中也有出现。对于SDL_Texture本身来说,它包含了显示驱动用于显示的特定数据结构。

SDL_Texture的创建一半通过SDL_CreateTexture创建,创建一次后,可以重复使用,通过SDL_UpdateTexture函数更新数据。

SDL_CreateTexture

函数原型:

SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer,
                               Uint32        format,
                               int           access,
                               int           w,
                               int           h)

函数功能:使用此函数可为渲染器上下文创建纹理。

参数列表

参数释义
renderer渲染器上下文
format渲染的像素格式,支持的格式,在 SDL_PixelFormatEnum中,详情见渲染器支持的像素格式
access纹理访问模式,定义在 SDL_TextureAccess中; 详见纹理访问模式
w纹理的宽度(以像素为单位)
h纹理的高度(以像素为单位)

返回值:返回渲染器上下文的纹理。

渲染器支持的像素格式:

SDL_PIXELFORMAT_UNKNOWN
SDL_PIXELFORMAT_RGB332
SDL_PIXELFORMAT_RGB444
SDL_PIXELFORMAT_RGB555
SDL_PIXELFORMAT_ARGB1555
SDL_PIXELFORMAT_RGBA5551
SDL_PIXELFORMAT_ABGR1555
SDL_PIXELFORMAT_BGRA5551
SDL_PIXELFORMAT_RGB565
SDL_PIXELFORMAT_RGBA32当前平台的RGBA字节数组颜色的别名(>= SDL 2.0.5)
SDL_PIXELFORMAT_ARGB32当前平台的ARGB字节数组颜色数据的别名(>= SDL 2.0.5)
SDL_PIXELFORMAT_YV12平面模式: Y + V + U (3 planes)
SDL_PIXELFORMAT_IYUV平面模式: Y + U + V (3 planes)
SDL_PIXELFORMAT_YUY2打包模式: Y0+U0+Y1+V0 (1 plane)
SDL_PIXELFORMAT_UYVY打包模式: U0+Y0+V0+Y1 (1 plane)
SDL_PIXELFORMAT_YVYU打包模式: Y0+V0+Y1+U0 (1 plane)
SDL_PIXELFORMAT_NV12打包模式: Y + U/V 交错 (2 planes) (>= SDL 2.0.4)
SDL_PIXELFORMAT_NV21打包模式: Y + V/U 交错 (2 planes) (>= SDL 2.0.4)

纹理访问模式

访问模式含义
SDL_TEXTUREACCESS_STATIC变化很少,不可锁定
SDL_TEXTUREACCESS_STREAMING经常变化,可锁定
SDL_TEXTUREACCESS_TARGET可以用作渲染目标

SDL_UpdateTexture

函数原型:

int SDL_UpdateTexture(SDL_Texture*    texture,
                      const SDL_Rect* rect,
                      const void*     pixels,
                      int             pitch)

函数功能:使用新的像素数据更新给定的纹理矩形。也就是说,可以固定刷新纹理的某一分部区域。

参数列表

参数含义
texturethe texture to update
rect用SDL_Rect结构表达纹理中需要更新数据的区域, 如果为NULL则更新整个纹理
pixels纹理格式的原始像素数据,格式通常在创建纹理的函数中指定
pitch一行像素数据中的字节数,包括行之间的填充。字节数通常由纹理的格式决定

这里需要解释一下pitch的计算方式,以一帧尺寸为960x540,格式为SDL_PIXELFORMAT_IYUV为例,计算

pitch :

SDL_PIXELFORMAT_IYUV,也就是IYUV类型,也称为YUV420p、I420,它在内存中的存储格式为三平面存储。

即:第一个平面960个字节的位置存Y分量,第二个平面480个字节存U分量,第三个平面480个字节存V分量。而这个函数中的pitch所说的一行数像素数据中的字节数,其实指的是第一个平面的长度(字节为单位),也就是960字节,虽然和宽度在数学上相等,但它们所表达的意思是不一样的,

另外,这是一个相当慢的函数,旨在用于不经常更改的静态纹理。

如果要经常更新纹理,则最好将纹理创建为流式传输并使用下面SDL_LockTexture锁定函数。 虽然此功能适用于流式纹理,但出于优化原因,如果之后锁定纹理,则可能无法获得像素。

SDL_LockTexture:锁定纹理

SDL_UnlockTexture:解锁纹理

返回值

成功时返回0或失败时返回负错误代码

#include <iostream>

#include <SDL.h>
#include <SDL2/SDL_image.h>

using namespace  std;

const int WIDTH = 960, HEIGHT = 540;
int main() {
    if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        cout << "SDL could not initialized with error: " << SDL_GetError() << endl;
        return -1;
    }
    SDL_Window *window = SDL_CreateWindow("Hello SDL world!", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          WIDTH, HEIGHT, SDL_WINDOW_ALLOW_HIGHDPI);
    if (NULL == window) {
        cout << "SDL could not create window with error: " << SDL_GetError() << endl;
        return -1;
    }

    if (!(IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG)) {
        cout << "SDL_image could not init with error: " << IMG_GetError() << endl;
        return -1;
    }

    FILE* pFile = fopen("little_prince_i420_960x540.yuv", "rb");
    if (pFile == NULL) {
        cerr << "little_prince_i420_960x540.yuv open failed" << endl;
    }
    unsigned char *m_yuv_data;
    int frameSize = HEIGHT * WIDTH * 12 / 8;
    m_yuv_data = (unsigned char*)malloc(frameSize * sizeof(unsigned char));
    fread(m_yuv_data, 1, frameSize, pFile);
    fclose(pFile);

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); // 创建渲染器
    // 创建纹理
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV,  SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
    if (texture != NULL) {
        SDL_Event windowEvent;
        while (true) {
            if (SDL_PollEvent(&windowEvent)) {
                if (SDL_QUIT == windowEvent.type) {
                    cout << "SDL quit!!" << endl;
                    break;
                }
            }
            SDL_UpdateTexture(texture, NULL, m_yuv_data, WIDTH); // 更新纹理
            SDL_RenderClear(renderer); // 清除渲染器
            SDL_RenderCopy(renderer, texture, NULL, NULL); // 拷贝纹理到渲染器
            SDL_RenderPresent(renderer); // 渲染
        }
        SDL_DestroyTexture(texture);
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 0;
    }
}
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要捕获SDL2窗口画面并录制视频,你可以结合SDL2和FFmpeg库来实现。首先,你需要使用SDL2来创建窗口和渲染器,并在每一帧绘制完窗口内容后,将渲染器的像素数据复制到FFmpeg的AVFrame中。然后,你可以使用FFmpeg来编码并写入视频帧。 以下是一个简单的示例代码,展示了如何使用SDL2和FFmpeg来捕获窗口画面并录制视频: ```cpp extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> } #include <SDL2/SDL.h> int main() { av_register_all(); // 初始化SDL2 SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow("Capture Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 640, 480); // 创建FFmpeg相关的结构体 AVFormatContext* formatContext; avformat_alloc_output_context2(&formatContext, nullptr, nullptr, "output.mp4"); AVOutputFormat* outputFormat = formatContext->oformat; AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); AVStream* stream = avformat_new_stream(formatContext, codec); AVCodecContext* codecContext = stream->codec; codecContext->codec_id = AV_CODEC_ID_H264; codecContext->codec_type = AVMEDIA_TYPE_VIDEO; codecContext->pix_fmt = AV_PIX_FMT_YUV420P; codecContext->width = 640; codecContext->height = 480; codecContext->time_base = AVRational{1, 25}; codecContext->bit_rate = 400000; avio_open(&formatContext->pb, "output.mp4", AVIO_FLAG_WRITE); avformat_write_header(formatContext, nullptr); AVFrame* frame = av_frame_alloc(); frame->format = codecContext->pix_fmt; frame->width = codecContext->width; frame->height = codecContext->height; av_frame_get_buffer(frame, 32); // 开始录制视频,逐帧写入 for (int i = 0; i < 250; ++i) { av_frame_make_writable(frame); // 渲染窗口内容到纹理 SDL_RenderClear(renderer); // 这里是你绘制窗口内容的代码 // ... SDL_UpdateTexture(texture, nullptr, nullptr, 0); SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderPresent(renderer); // 将纹理像素数据复制到AVFrame中 uint8_t* pixels = nullptr; int pitch = 0; SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels), &pitch); for (int y = 0; y < codecContext->height; ++y) { memcpy(frame->data[0] + y * frame->linesize[0], pixels + y * pitch, codecContext->width * 4); } SDL_UnlockTexture(texture); // 编码并写入视频帧 AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; avcodec_send_frame(codecContext, frame); avcodec_receive_packet(codecContext, &packet); av_interleaved_write_frame(formatContext, &packet); av_packet_unref(&packet); } av_write_trailer(formatContext); av_frame_free(&frame); avio_close(formatContext->pb); avformat_free_context(formatContext); // 释放SDL2资源 SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 在上述示例代码中,我们使用SDL2创建了一个窗口和渲染器,并使用一个纹理来绘制窗口内容。然后,我们将纹理的像素数据复制到AVFrame中,并使用FFmpeg进行编码并写入视频帧。 请注意,上述代码只是一个简单的示例,你可能需要根据你的实际需求进行适当修改。另外,为了成功编译运行此代码,你需要安装SDL2和FFmpeg库,并将它们链接到你的项目中。 希望这个示例能对你有所帮助!如果你有任何疑问,请随时提出。祝你成功开发SDL2录制窗口画面的功能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值