FFMPEG - AVFrame 到每个通道数组的转换
FFMPEG - AVFrame to per channel array conversion
我想将一个 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
,但我对它的理解还不够,无法确定该函数是否能够进行此类转换。有人可以帮忙吗!! :)
您称为 "one channel at a time" 的格式有一个名为 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)
我想将一个 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
,但我对它的理解还不够,无法确定该函数是否能够进行此类转换。有人可以帮忙吗!! :)
您称为 "one channel at a time" 的格式有一个名为 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)