【问题标题】:FFMPEG - AVFrame to per channel array conversionFFMPEG - AVFrame 到每个通道数组的转换
【发布时间】:2017-05-02 23:03:16
【问题描述】:

我希望将AVFrame 复制到一个数组中,其中像素以行优先顺序一次存储一个通道。

详情:

我正在使用 FFMPEG 的 api 从视频中读取帧。我使用avcodec_decode_video2 将每个帧作为AVFrame 获取,如下所示:

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]。问题是数组需要按行主要顺序存储帧,一次一个通道,即R11,R12,... R21,R22,... G11,G12,... G21,G22,.. . B11, B12, ... B21, B22, ... (我使用了符号[颜色通道][行索引][列索引],即G21是第2行第1列像素的绿色通道值) .我看过sws_scale,但我对它的理解还不够,无法弄清楚该函数是否能够进行这样的转换。有人可以帮忙吗!! :)

【问题讨论】:

    标签: ffmpeg libavcodec swscale


    【解决方案1】:

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

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

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

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

    // 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)
    

    【讨论】:

    • 谢谢@halfelf。我做了一些初步测试,与仅逐像素复制相比,使用 sws_scale 转换为 GBR 似乎相当慢。我会回来报告的!
    猜你喜欢
    • 2015-05-29
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多