Проблема преобразования формата пикселей [FFMPEG]
Я написал небольшую программу, используя библиотеки ffmpeg. что делает следующее-
1) расшифровать кадр. 2) конвертировать кадр в rgb24 . 3) конвертировать кадр rgb24 обратно в yuv420p. 4) закодировать кадр yuv420p и упаковать его в видеофайл.
Но конец видео не совпадает с входным видео. в финальном видео есть некоторые артефакты (горизонтальные линии). Я также получаю предупреждение при вызове метода rgbToYuv - Предупреждение: данные не выровнены! Это может привести к потере скорости
Я подозреваю, что что-то не так с моими методами преобразования формата, потому что, когда я комментирую шаги покрытия из моей программы, выходное видео идентично входному видео.
следующие мои методы -
int VideoFileInstance::convertToRGBFrame(AVFrame **yuvframe,AVFrame **rgbPictInfo) {
int ret;
int width = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->width;
int height = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->height;
int m_bufferSize = avpicture_get_size(PIX_FMT_RGB24,width, height);
uint8_t *buffer = (uint8_t *)av_malloc(m_bufferSize);
//init context if not done already.
if (imgConvertCtxYUVToRGB == NULL) {
//init once
imgConvertCtxYUVToRGB = sws_getContext(width, height, PIX_FMT_YUV420P, width, height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
if(imgConvertCtxYUVToRGB == NULL) {
av_log(NULL,AV_LOG_ERROR,"error creating img context");
return -1;
}
}
avpicture_fill((AVPicture*)(*rgbPictInfo), buffer,
PIX_FMT_RGB24,
width, height);
uint8_t *inDate[3] = {
(*yuvframe)->data[0] ,
(*yuvframe)->data[1] ,
(*yuvframe)->data[2]
};
int destLineSize[1] = {3*width};
ret = sws_scale(imgConvertCtxYUVToRGB, inDate, (*yuvframe)->linesize, 0, height,
(*rgbPictInfo)->data, destLineSize);
av_free(buffer);
return ret;
}
int VideoFileInstance::convertToYuvFrame (AVFrame **rgbFrame , AVFrame ** yuvFrame) {
int ret = 0;
int width = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->width;
int height = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->height;
int m_bufferSize = avpicture_get_size(PIX_FMT_YUV420P, width, height);
uint8_t *buffer = (uint8_t *)av_malloc(m_bufferSize);
avpicture_fill((AVPicture*)(*yuvFrame), buffer, PIX_FMT_YUV420P,
width, height);
if(imgConvertCtxRGBToYUV == NULL) {
imgConvertCtxRGBToYUV = sws_getContext(width, height, PIX_FMT_RGB24, width, height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
if(imgConvertCtxRGBToYUV == NULL){
av_log(NULL,AV_LOG_ERROR,"error creating img context");
return -1;
}
}
avpicture_fill((AVPicture*)(*yuvFrame), buffer,
PIX_FMT_YUV420P,
width, height);
sws_scale(imgConvertCtxRGBToYUV,(*rgbFrame)->data , (*rgbFrame)->linesize, 0, height,
(*yuvFrame)->data , (*yuvFrame)->linesize);
av_free(buffer);
return ret;
}
Размер входного видео 424 X 200. Что-то не так с моими функциями преобразования.
2 ответа
См. /questions/1490111/kak-vyi-izmenyaete-razmer-avframe/1490120#1490120 Вторая точка маркера, avpicture_ и связанные с ней функции не гарантируют выравнивание, необходимо использовать аналоги av_image_ с align=16 или align=32.
Использование Рональдсом предложения об использовании методов av_image* решило проблему для меня. Ниже приведен фиксированный код для одного из методов.
int VideoFileInstance::convertToRGBFrame(AVFrame **yuvframe,AVFrame **rgbPictInfo) {
int ret;
int width = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->width;
int height = ifmt_ctx->streams[VIDEO_STREAM_INDEX]->codec->height;
//init context if not done already.
if (imgConvertCtxYUVToRGB == NULL) {
//init once
imgConvertCtxYUVToRGB = sws_getContext(width, height, PIX_FMT_YUV420P, width, height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, 0, 0, 0);
if(imgConvertCtxYUVToRGB == NULL) {
av_log(NULL,AV_LOG_ERROR,"error creating img context");
return -1;
}
}
// call av_freep(rgbPictInfo->data) to free memory
av_image_alloc( (*rgbPictInfo)->data, //data to be filled
(*rgbPictInfo)->linesize,//line sizes to be filled
width, height,
PIX_FMT_RGB24, //pixel format
32 //aling
);
ret = sws_scale(imgConvertCtxYUVToRGB, (*yuvframe)->data, (*yuvframe)->linesize, 0, height,
(*rgbPictInfo)->data, (*rgbPictInfo)->linesize);
return ret;
}