FFMPEG - AVFrame 到每个 channel 数组的转换

标签 ffmpeg libavcodec swscale

我想复制 AVFrame到一个数组中,其中像素以行优先顺序一次存储一个 channel 。

细节:

我正在使用 FFMPEG 的 api 从视频中读取帧。我用过avcodec_decode_video2AVFrame 的形式获取每一帧如下:

AVFormatContext* fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, filepath, NULL, NULL);
...
int video_stream_idx;  // stores the stream index for the video
...
AVFrame* vid_frame = NULL;
vid_frame = av_frame_alloc();
AVPacket vid_pckt;
int frame_finish;
...
while (av_read_frame(fmt_ctx, &vid_pckt) >= 0) {
    if (b_vid_pckt.stream_index == video_stream_idx) {
        avcodec_decode_video2(cdc_ctx, vid_frame, &frame_finish, &vid_pckt);
        if (frame_finish) {
            /* perform conversion */
        }
    }
}

目标数组如下所示:
unsigned char* frame_arr = new unsigned char [cdc_ctx->width * cdc_ctx->height * 3];

我需要复制所有 vid_frame进入 frame_arr ,其中像素值的范围应为 [0, 255]。问题是数组需要按行主要顺序存储帧,一次一个 channel ,即 R11、R12、... R21、R22、... G11、G12、... G21、G22、.. . B11, B12, ... B21, B22, ...(我使用了符号 [color channel][row index][column index],即 G21 是第 2 行第 1 列像素的绿色 channel 值) .我看过sws_scale ,但我对它的理解还不够,无法弄清楚该函数是否能够进行这样的转换。有人可以帮忙吗!! :)

最佳答案

您称为“一次一个 channel ”的格式有一个名为 planar 的术语。 . (顺便说一句,相反的格式被命名为 packed )几乎每个像素格式都是行顺序的。

这里的问题是输入格式可能会有所不同,所有这些都应该转换为一种格式。就是这样 sws_scale()做。

但是,没有这样的planar RGB在 ffmpeg 库中格式化。您必须将自己的像素格式描述写入ffmpeg源代码libavutil/pixdesc.c并重新构建库。

或者您可以将框架转换为 AV_PIX_FMT_GBRP格式,这是与您想要的最相似的格式。 AV_PIX_FMT_GBRP是平面格式,而绿色 channel 首先是红色,最后是红色(蓝色中间)。然后重新排列这些 channel 。

// Create a SwsContext first:
SwsContext* sws_ctx = sws_getContext(cdc_ctx->width, cdc_ctx->height, cdc_ctx->pix_fmt, cdc_ctx->width, cdc_ctx->height, AV_PIX_FMT_GBRP, 0, 0, 0, 0);
// alloc some new space for storing converted frame
AVFrame* gbr_frame = av_frame_alloc();
picture->format = AV_PIX_FMT_GBRP;
picture->width  = cdc_ctx->width;
picture->height = cdc_ctx->height;
av_frame_get_buffer(picture, 32);
....

while (av_read_frame(fmt_ctx, &vid_pckt) >=0) {
    ret = avcodec_send_packet(cdc_ctx, &vid_pckt);
    // In particular, we don't expect AVERROR(EAGAIN), because we read all
    // decoded frames with avcodec_receive_frame() until done.
    if (ret < 0)
        break;

    ret = avcodec_receive_frame(cdc_ctx, vid_frame);
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
        break;
    if (ret >= 0) {
        // convert image from native format to planar GBR
        sws_scale(sws_ctx, vid_frame->data, 
                  vid_frame->linesize, 0, vid_frame->height, 
                  gbr_frame->data, gbr_frame->linesize);

        // rearrange gbr channels in gbr_frame as you like
        // g channel is gbr_frame->data[0]
        // b channel is gbr_frame->data[1]
        // r channel is gbr_frame->data[2]
        // ......
    }
}

av_frame_free(gbr_frame);
av_frame_free(vid_frame);
sws_freeContext(sws_ctx);
avformat_free_context(fmt_ctx)

关于FFMPEG - AVFrame 到每个 channel 数组的转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41196429/

相关文章:

tcp - ffmpeg 可以播放视频但不能播放包含相同数据的流

ffmpeg - 3gp 文件中的比特率不好,使用 ffmpeg 转换为 mp3

ffmpeg : audio + image to video : reduce time and space it takes

c++ - 与 libavcodec 链接,仍然看到 undefined reference

go - fatal error : 'libavcodec/avcodec.h' file not found caused by zergon321/reisen

更改区域上的 FFmpeg sws_scale

c++ - 如何使用带有ffmpeg的DXVA2解码和获取帧

c++ - AVFrame 的线宽为负

c# - 如何在 C# 中从带有过渡的图像创建视频?

encoding - 如何将 AVFrame 写为 JPEG 图像