c - FFmpeg 将 RGB 转换为 YUV420 到 RGB 错误结果

标签 c ffmpeg rgb yuv

我正在尝试解决视频编码问题,并在此过程中确保我可以使用 FFmpeg 在像素格式之间进行转换。我在将虚拟 RGB24 位图转换为 YUV420 并再次转换回来时遇到问题。
下面是我的测试程序:

#include "Windows.h"

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavutil/time.h>

#define WIDTH 32
#define HEIGHT 32
#define ERROR_LEN 128
#define SWS_FLAGS SWS_BICUBIC

char _errorLog[ERROR_LEN];
void CreateBitmapFile(LPCWSTR fileName, long width, long height, WORD bitsPerPixel, BYTE* bitmapData, DWORD bitmapDataLength);

int main()
{
  printf("FFmpeg Pixel Conversion Test\n");

  av_log_set_level(AV_LOG_DEBUG);

  int w = WIDTH;
  int h = HEIGHT;

  struct SwsContext* rgbToI420Context;
  struct SwsContext* i420ToRgbContext;

  rgbToI420Context = sws_getContext(w, h, AV_PIX_FMT_RGB24, w, h, AV_PIX_FMT_YUV420P, SWS_FLAGS, NULL, NULL, NULL);
  if (rgbToI420Context == NULL) {
    fprintf(stderr, "Failed to allocate RGB to I420 conversion context.\n");
  }

  i420ToRgbContext = sws_getContext(w, h, AV_PIX_FMT_YUV420P, w, h, AV_PIX_FMT_RGB24, SWS_FLAGS, NULL, NULL, NULL);
  if (i420ToRgbContext == NULL) {
    fprintf(stderr, "Failed to allocate I420 to RGB conversion context.\n");
  }

  // Create dummy bitmap.
  uint8_t rgbRaw[WIDTH * HEIGHT * 3];
  for (int row = 0; row < 32; row++)
  {
    for (int col = 0; col < 32; col++)
    {
      int index = row * WIDTH * 3 + col * 3;

      int red = (row < 16 && col < 16) ? 255 : 0;
      int green = (row < 16 && col > 16) ? 255 : 0;
      int blue = (row > 16 && col < 16) ? 255 : 0;

      rgbRaw[index] = (byte)red;
      rgbRaw[index + 1] = (byte)green;
      rgbRaw[index + 2] = (byte)blue;
    }
  }

  CreateBitmapFile(L"test-reference.bmp", WIDTH, HEIGHT, 24, rgbRaw, WIDTH * HEIGHT * 3);

  printf("Converting RGB to I420.\n");

  uint8_t* rgb[3];
  uint8_t* i420[3];
  int rgbStride[3], i420Stride[3];

  rgbStride[0] = w * 3;
  i420Stride[0] = w * h;
  i420Stride[1] = w * h / 4;
  i420Stride[2] = w * h / 4;

  rgb[0] = rgbRaw;
  i420[0] = (uint8_t*)malloc((size_t)i420Stride[0] * h);
  i420[1] = (uint8_t*)malloc((size_t)i420Stride[1] * h);
  i420[2] = (uint8_t*)malloc((size_t)i420Stride[2] * h);

  int toI420Res = sws_scale(rgbToI420Context, rgb, rgbStride, 0, h, i420, i420Stride);
  if (toI420Res < 0) {
    fprintf(stderr, "Conversion from RGB to I420 failed, %s.\n", av_make_error_string(_errorLog, ERROR_LEN, toI420Res));
  }

  printf("Converting I420 to RGB.\n");

  uint8_t* rgbOut[3];
  int rgbOutStride[3];

  rgbOutStride[0] = w * 3;
  rgbOut[0] = (uint8_t*)malloc((size_t)rgbOutStride[0] * h);

  int toRgbRes = sws_scale(i420ToRgbContext, i420, i420Stride, 0, h, rgbOut, rgbOutStride);
  if (toRgbRes < 0) {
    fprintf(stderr, "Conversion from RGB to I420 failed, %s.\n", av_make_error_string(_errorLog, ERROR_LEN, toRgbRes));
  }

  CreateBitmapFile(L"test-output.bmp", WIDTH, HEIGHT, 24, rgbOut, WIDTH * HEIGHT * 3);

  free(rgbOut[0]);

  for (int i = 0; i < 3; i++) {
    free(i420[i]);
  }

  sws_freeContext(rgbToI420Context);
  sws_freeContext(i420ToRgbContext);

  return 0;
}

/**
* Creates a bitmap file and writes to disk.
* @param[in] fileName: the path to save the file at.
* @param[in] width: the width of the bitmap.
* @param[in] height: the height of the bitmap.
* @param[in] bitsPerPixel: colour depth of the bitmap pixels (typically 24 or 32).
* @param[in] bitmapData: a pointer to the bytes containing the bitmap data.
* @param[in] bitmapDataLength: the number of pixels in the bitmap.
*/
void CreateBitmapFile(LPCWSTR fileName, long width, long height, WORD bitsPerPixel, BYTE* bitmapData, DWORD bitmapDataLength)
{
  HANDLE file;
  BITMAPFILEHEADER fileHeader;
  BITMAPINFOHEADER fileInfo;
  DWORD writePosn = 0;

  file = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  //Sets up the new bmp to be written to

  fileHeader.bfType = 19778;                                                                    //Sets our type to BM or bmp
  fileHeader.bfSize = sizeof(fileHeader.bfOffBits) + sizeof(RGBTRIPLE);                         //Sets the size equal to the size of the header struct
  fileHeader.bfReserved1 = 0;                                                                   //sets the reserves to 0
  fileHeader.bfReserved2 = 0;
  fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);                                           //Sets offbits equal to the size of file and info header
  fileInfo.biSize = sizeof(BITMAPINFOHEADER);
  fileInfo.biWidth = width;
  fileInfo.biHeight = height;
  fileInfo.biPlanes = 1;
  fileInfo.biBitCount = bitsPerPixel;
  fileInfo.biCompression = BI_RGB;
  fileInfo.biSizeImage = width * height * (bitsPerPixel / 8);
  fileInfo.biXPelsPerMeter = 2400;
  fileInfo.biYPelsPerMeter = 2400;
  fileInfo.biClrImportant = 0;
  fileInfo.biClrUsed = 0;

  WriteFile(file, &fileHeader, sizeof(fileHeader), &writePosn, NULL);

  WriteFile(file, &fileInfo, sizeof(fileInfo), &writePosn, NULL);

  WriteFile(file, bitmapData, bitmapDataLength, &writePosn, NULL);

  CloseHandle(file);
}
源位图是:
Reference bitmap
两次转换后的输出bmp为:
Output bitmap

最佳答案

愚蠢的错误。

CreateBitmapFile(L"test-output.bmp", WIDTH, HEIGHT, 24, rgbOut, WIDTH * HEIGHT * 3);
应该:
CreateBitmapFile(L"test-output.bmp", WIDTH, HEIGHT, 24, rgbOut[0], WIDTH * HEIGHT * 3);

关于c - FFmpeg 将 RGB 转换为 YUV420 到 RGB 错误结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63645517/

相关文章:

arrays - 将列表作为参数传递给c中的函数

ffmpeg - ffmpeg中来自一个来源的多个音轨

ffmpeg - 视频 - 在流 #1 中找不到编解码器 wavpack 的标签,容器中当前不支持编解码器无法使用 FFmpeg 将文件 MKV 写入 MP4

FFmpeg avcodec_find_encoder(AV_CODEC_ID_OPUS) 和 avcodec_find_decoder(AV_CODEC_ID_OPUS) 都返回 NULL

jquery css 颜色值返回 RGB?

c - 设置二维数组值时读取字符串字符时出错

c - 获取指向窗口像素的指针

c - C strcmp 中的字符串比较

image - RGB 到标准 rgb 转换。矢量化

java - 将位图 RGB 像素加载到 BufferedImage 中