FFMPEG - преобразование AVFrame в массив каналов
Я ищу, чтобы скопировать AVFrame
в массив, где пиксели хранятся по одному каналу за раз в главном порядке строк.
Подробности:
Я использую API FFMPEG для чтения кадров из видео. я использовал 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
, но я не понимаю этого достаточно, чтобы выяснить, способна ли эта функция выполнять такое преобразование. Может кто-нибудь помочь!!:)
1 ответ
Формат, который вы назвали "один канал за раз", имеет термин planar
, (кстати, противоположный формат называется packed
) И почти каждый формат пикселей имеет порядок строк.
Проблема здесь в том, что формат ввода может отличаться, и все они должны быть преобразованы в один формат. Это то что sws_scale()
делает.
Однако такого нет planar RGB
Формат в библиотеках ffmpeg пока отсутствует. Вы должны написать собственное описание формата пикселя в исходный код 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)