libyuv API MJPGToI420() 的用法示例

Example usage of a libyuv API MJPGToI420()

我正在尝试使用 libyuv API,更具体地说 MJPGToI420()

我想先把一张jpeg图片作为输入MJPGToI420(),其签名如下:

int MJPGToI420(const uint8_t* sample,
               size_t sample_size,
               uint8_t* dst_y,
               int dst_stride_y,
               uint8_t* dst_u,
               int dst_stride_u,
               uint8_t* dst_v,
               int dst_stride_v,
               int src_width,
               int src_height,
               int dst_width,
               int dst_height);

然后,我想为dst_ydst_udst_v指针分配space。但是,我不知道要分配给他们多少space。我也对步幅应该是什么感到困惑,即参数 dst_stride_ydst_stride_udst_stride_v 应该是什么。

非常感谢任何正确方向的指示。

编辑: 这是来自 libyuv 来源 unit tests 的一段使用此函数的代码。但是,测试 returns 1 这是预期行为的功能失败。该测试还只对数据使用零,而不是实际的 MJPG 文件。

TEST_F(LibYUVConvertTest, MJPGToI420) {
  const int kOff = 10;
  const int kMinJpeg = 64;
  const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg
                             ? benchmark_width_ * benchmark_height_
                             : kMinJpeg;
  const int kSize = kImageSize + kOff;
  align_buffer_page_end(orig_pixels, kSize);
  align_buffer_page_end(dst_y_opt, benchmark_width_ * benchmark_height_);
  align_buffer_page_end(dst_u_opt, SUBSAMPLE(benchmark_width_, 2) *
                                       SUBSAMPLE(benchmark_height_, 2));
  align_buffer_page_end(dst_v_opt, SUBSAMPLE(benchmark_width_, 2) *
                                       SUBSAMPLE(benchmark_height_, 2));

  // EOI, SOI to make MJPG appear valid.
  memset(orig_pixels, 0, kSize);
  orig_pixels[0] = 0xff;
  orig_pixels[1] = 0xd8;  // SOI.
  orig_pixels[kSize - kOff + 0] = 0xff;
  orig_pixels[kSize - kOff + 1] = 0xd9;  // EOI.

  for (int times = 0; times < benchmark_iterations_; ++times) {
    int ret =
        MJPGToI420(orig_pixels, kSize, dst_y_opt, benchmark_width_, dst_u_opt,
                   SUBSAMPLE(benchmark_width_, 2), dst_v_opt,
                   SUBSAMPLE(benchmark_width_, 2), benchmark_width_,
                   benchmark_height_, benchmark_width_, benchmark_height_);
    // Expect failure because image is not really valid.
    EXPECT_EQ(1, ret);
  }

  free_aligned_buffer_page_end(dst_y_opt);
  free_aligned_buffer_page_end(dst_u_opt);
  free_aligned_buffer_page_end(dst_v_opt);
  free_aligned_buffer_page_end(orig_pixels);
}

编辑 2: 此外,这是我尝试过的方法,但是,最终的 yuv 文件甚至在 yuv 查看器中都看不到(使用缓冲区创建 dst_u_optdst_y_opt),这让我相信我可能搞砸了函数:

int convertMJPGToI420() {

    auto fileSize = filesize(IMG_NAME);

    // load image into memory
    uint8_t* my_img = (uint8_t*) calloc(fileSize, 1);
    std::ifstream fin(IMG_NAME, ios::in | ios::binary);
    fin.read(reinterpret_cast<char*>(my_img), fileSize);

    // exif data offset 
    // This is the size of the exif data
    const int kOff = 4096;

    // 4k image is being sent in
    int benchmark_width_ = 3840;
    int benchmark_height_ = 2160;

    const int kSize = fileSize;

    // align_buffer_page_end is a macro (look at link posted for unit tests above)
    // I'm not sure if the size allocation for these is correct
    // I have tried to model it based off the example
    align_buffer_page_end(orig_pixels, kSize);
    align_buffer_page_end(dst_y_opt, benchmark_width_ * benchmark_height_);
    align_buffer_page_end(dst_u_opt, SUBSAMPLE(benchmark_width_, 2) * 
                                        SUBSAMPLE(benchmark_height_, 2));
    align_buffer_page_end(dst_v_opt, SUBSAMPLE(benchmark_width_, 2) * 
                                        SUBSAMPLE(benchmark_height_, 2));

    // EOI, SOI to make MJPG appear valid
    memset(orig_pixels, 0, kSize);
    orig_pixels[0] = 0xff;
    orig_pixels[1] = 0xd8; // SOI

    memcpy(orig_pixels + 2, my_img, kSize - kOff - 3);

    orig_pixels[kSize - kOff + 0] = 0xff;
    orig_pixels[kSize - kOff + 1] = 0xd9; // EOI

    // using async as this function might be ansynchronous
    std::future<int> ret = std::async(libyuv::MJPGToI420, orig_pixels, kSize, dst_y_opt, benchmark_width_,
                        dst_u_opt, SUBSAMPLE(benchmark_width_, 2),
                        dst_v_opt, SUBSAMPLE(benchmark_width_, 2), 
                        benchmark_width_, benchmark_height_, 
                        benchmark_width_, benchmark_height_);

    ret.wait();

    // ret is always one, which means there was a failure
    if(ret.get() == 0) {
        cout << "return value was zero" << endl;
    } else {
        cout << "return value was one" << endl;
    }

    FILE* file = fopen("/data/dst_u_opt", "wb");
    fwrite(dst_y_opt, 1, SUBSAMPLE(benchmark_width_, 2) * SUBSAMPLE(benchmark_height_, 2) , file);
    fclose(file);

    file = fopen("/data/dst_v_opt", "wb");
    fwrite(dst_y_opt, 1, SUBSAMPLE(benchmark_width_, 2) *  SUBSAMPLE(benchmark_height_, 2), file);
    fclose(file);

    free_aligned_buffer_page_end(dst_y_opt);
    free_aligned_buffer_page_end(dst_u_opt);
    free_aligned_buffer_page_end(dst_v_opt);
    free_aligned_buffer_page_end(orig_pixels);

    return 0;
}

您需要知道 jpeg 的宽度和高度。

I420 是一个 420 次采样的 YUV。 Y 平面是宽度 * 高度(以字节为单位)。 dst_stride_y 值为宽度 例如

char* dst_y = malloc(width * height);

U、V平面为半宽半高。要处理奇怪的尺寸,您应该四舍五入。

dst_stride_u = (width + 1) / 2;
dst_stride_v = (width + 1) / 2;

u和v平面是((width + 1) / 2) * ((height + 1) / 2)字节。

char* dst_u = malloc(((width + 1) / 2) * ((height + 1) / 2));
char* dst_y = malloc(((width + 1) / 2) * ((height + 1) / 2));

如果您想提交问题,包括更好的文档,post 在这里: https://bugs.chromium.org/p/libyuv/issues/list