Как конвертировать видео кадр ffmpeg в YUV444?

Я следил за руководство о том, как использовать FFmpeg и SDL, чтобы сделать простой видеоплеер без звука (пока). Просматривая учебник, я понял, что он устарел, и многие функции, которые он использовал как для ffmpeg, так и для SDL, устарели. Поэтому я искал современное решение и нашел вопрос о переполнении стека ответ это завершило то, что пропало в учебнике.

Тем не менее, он использует YUV420, который имеет низкое качество. Я хочу реализовать YUV444, и после того, как я немного изучу подвыборку цветности и, глядя на различные форматы YUV, я не совсем понимаю, как это реализовать. Из того, что я понимаю YUV420 это четверть качества YUV444. YUV444 означает, что каждый пиксель имеет свою собственную выборку цветности и, как таковой, является более подробным, в то время как YUV420 означает, что пиксели сгруппированы и имеют одинаковую выборку цветности, и, следовательно, менее детализированы.

И из того, что я понимаю, разные форматы YUV (420, 422, 444) отличаются тем, как они упорядочивают y, u и v. Все это немного ошеломляет, потому что я мало что сделал с кодеками, преобразованиями, и т.д. Любая помощь будет высоко ценится, и, если необходима дополнительная информация, пожалуйста, дайте мне знать, прежде чем голосовать.

Вот код из ответ Я упомянул о переходе на YUV420:

texture = SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING,
pCodecCtx->width,
pCodecCtx->height
);
if (!texture) {
fprintf(stderr, "SDL: could not create texture - exiting\n");
exit(1);
}

// initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_YUV420P,
SWS_BILINEAR,
NULL,
NULL,
NULL);

// set up YV12 pixel array (12 bits per pixel)
yPlaneSz = pCodecCtx->width * pCodecCtx->height;
uvPlaneSz = pCodecCtx->width * pCodecCtx->height / 4;
yPlane = (Uint8*)malloc(yPlaneSz);
uPlane = (Uint8*)malloc(uvPlaneSz);
vPlane = (Uint8*)malloc(uvPlaneSz);
if (!yPlane || !uPlane || !vPlane) {
fprintf(stderr, "Could not allocate pixel buffers - exiting\n");
exit(1);
}

uvPitch = pCodecCtx->width / 2;
while (av_read_frame(pFormatCtx, &packet) >= 0) {
// Is this a packet from the video stream?
if (packet.stream_index == videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

// Did we get a video frame?
if (frameFinished) {
AVPicture pict;
pict.data[0] = yPlane;
pict.data[1] = uPlane;
pict.data[2] = vPlane;
pict.linesize[0] = pCodecCtx->width;
pict.linesize[1] = uvPitch;
pict.linesize[2] = uvPitch;

// Convert the image into YUV format that SDL uses
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pict.data,
pict.linesize);

SDL_UpdateYUVTexture(
texture,
NULL,
yPlane,
pCodecCtx->width,
uPlane,
uvPitch,
vPlane,
uvPitch
);

SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);

}
}

// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(screen);
SDL_Quit();
exit(0);
break;
default:
break;
}

}

// Free the YUV frame
av_frame_free(&pFrame);
free(yPlane);
free(uPlane);
free(vPlane);

// Close the codec
avcodec_close(pCodecCtx);
avcodec_close(pCodecCtxOrig);

// Close the video file
avformat_close_input(&pFormatCtx);

После дополнительных исследований я узнал, что в YUV420 сначала сохраняются все Y, а затем комбинация байтов U и V один за другим, как показано на этом изображении:

Однако я также узнал, что YUV444 хранится в порядке U, Y, V и повторяется, как показано на следующем рисунке:

Я попытался изменить некоторые вещи в коде:

    // I changed SDL_PIXELFORMAT_YV12 to SDL_PIXELFORMAT_UYVY
// as to reflect the order of YUV444
texture = SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_UYVY,
SDL_TEXTUREACCESS_STREAMING,
pCodecCtx->width,
pCodecCtx->height
);
if (!texture) {
fprintf(stderr, "SDL: could not create texture - exiting\n");
exit(1);
}

// Changed AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUV444P
// for rather obvious reasons
sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_YUV444P,
SWS_BILINEAR,
NULL,
NULL,
NULL);

// There are as many Y, U and V bytes as pixels I just
// made yPlaneSz and uvPlaneSz equal to the number of pixels
yPlaneSz = pCodecCtx->width * pCodecCtx->height;
uvPlaneSz = pCodecCtx->width * pCodecCtx->height;
yPlane = (Uint8*)malloc(yPlaneSz);
uPlane = (Uint8*)malloc(uvPlaneSz);
vPlane = (Uint8*)malloc(uvPlaneSz);
if (!yPlane || !uPlane || !vPlane) {
fprintf(stderr, "Could not allocate pixel buffers - exiting\n");
exit(1);
}

uvPitch = pCodecCtx->width * 2;
while (av_read_frame(pFormatCtx, &packet) >= 0) {
// Is this a packet from the video stream?
if (packet.stream_index == videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

// Rearranged the order of the planes to reflect UYV order
// then set linesize to the number of Y, U and V bytes
// per row
if (frameFinished) {
AVPicture pict;
pict.data[0] = uPlane;
pict.data[1] = yPlane;
pict.data[2] = vPlane;
pict.linesize[0] = pCodecCtx->width;
pict.linesize[1] = pCodecCtx->width;
pict.linesize[2] = pCodecCtx->width;

// Convert the image into YUV format that SDL uses
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pict.data,
pict.linesize);

SDL_UpdateYUVTexture(
texture,
NULL,
yPlane,
1,
uPlane,
uvPitch,
vPlane,
uvPitch
);
//.................................................

Но теперь я получаю нарушение доступа при звонке в SDL_UpdateYUVTexture… Я честно не уверен, что не так. Я думаю, что это может иметь отношение к настройке AVPicture picчлен data а также linesize неправильно, но я не уверен.

1

Решение

После многих часов поисков возможных ответов я наткнулся на это сообщение в котором кто-то спрашивал о поддержке YUV444 для упакованного или планарного режима. Единственный текущий формат, который я нашел, это AYUV, который упакован.

Ответ, который они получили, был список всех поддерживаемых в настоящее время форматов, которые не включают AYUV. Поэтому SDL не поддерживает YUV444.

Единственное решение — использовать другую библиотеку, которая поддерживает AYUV / YUV444.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]