如何在 C\C++ 中将位图作为帧写入 Ogg Theora?

发布于 2024-08-11 15:15:23 字数 63 浏览 4 评论 0原文

如何在 C\C++ 中将位图作为帧写入 Ogg Theora?

一些带有来源的示例将非常有用!)

How to write bitmaps as frames to Ogg Theora in C\C++?

Some Examples with source would be grate!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

做个少女永远怀春 2024-08-18 15:15:23

整个解决方案作为代码示例发布在此处有点冗长,但如果您从 Xiph.org 下载 libtheora,则会有一个示例 png2theora。我要提到的所有库函数都可以在 Xiph.org 上的 theora 和 ogg 文档中找到。

  1. 调用 th_info_init() 来初始化 th_info 结构,然后通过在其中分配适当的成员来设置输出参数。
  2. 在对 th_encode_alloc() 的调用中使用该结构来获取编码器上下文
  3. 使用 ogg_stream_init() 初始化 ogg 流
  4. 使用 th_comment_init 初始化空白 th_comment 结构

迭代执行以下操作:

  1. 使用编码器上下文、空白注释结构和ogg_packet。
  2. 使用 ogg_stream_packetin() 将生成的数据包发送到 ogg 流

直到 th_encode_flushheader 返回 0(或错误代码)

现在,重复调用 ogg_stream_pageout(),每次将 page.header 然后 page.body 写入输出文件,直到它返回0. 现在调用 ogg_stream_flush 并将结果页写入文件。

您现在可以将帧写入编码器。我是这样做的:

int theora_write_frame(int outputFd, unsigned long w, unsigned long h, unsigned char *yuv_y, unsigned char *yuv_u, unsigned char *yuv_v, int last)
{
  th_ycbcr_buffer ycbcr;
  ogg_packet op;
  ogg_page og;

  unsigned long yuv_w;
  unsigned long yuv_h;

  /* Must hold: yuv_w >= w */
  yuv_w = (w + 15) & ~15;
  /* Must hold: yuv_h >= h */
  yuv_h = (h + 15) & ~15;

  //Fill out the ycbcr buffer
  ycbcr[0].width = yuv_w;
  ycbcr[0].height = yuv_h;
  ycbcr[0].stride = yuv_w;
  ycbcr[1].width = yuv_w;
  ycbcr[1].stride = ycbcr[1].width;
  ycbcr[1].height = yuv_h;
  ycbcr[2].width = ycbcr[1].width;
  ycbcr[2].stride = ycbcr[1].stride;
  ycbcr[2].height = ycbcr[1].height;

  if(encoderInfo->pixel_fmt == TH_PF_420)
  {
    //Chroma is decimated by 2 in both directions
    ycbcr[1].width = yuv_w >> 1;
    ycbcr[2].width = yuv_w >> 1;
    ycbcr[1].height = yuv_h >> 1;
    ycbcr[2].height = yuv_h >> 1;
  }else if(encoderInfo->pixel_fmt == TH_PF_422)
  {
    ycbcr[1].width = yuv_w >> 1;
    ycbcr[2].width = yuv_w >> 1;
  }else if(encoderInfo->pixel_fmt != TH_PF_422)
  {
    //Then we have an unknown pixel format
    //We don't know how long the arrays are!
    fprintf(stderr, "[theora_write_frame] Unknown pixel format in writeFrame!\n");
    return -1;
  }

  ycbcr[0].data = yuv_y;
  ycbcr[1].data = yuv_u;
  ycbcr[2].data = yuv_v;

  /* Theora is a one-frame-in,one-frame-out system; submit a frame
     for compression and pull out the packet */
  if(th_encode_ycbcr_in(encoderContext, ycbcr)) {
    fprintf(stderr, "[theora_write_frame] Error: could not encode frame\n");
    return -1;
  }

  if(!th_encode_packetout(encoderContext, last, &op)) {
    fprintf(stderr, "[theora_write_frame] Error: could not read packets\n");
    return -1;
  }

  ogg_stream_packetin(&theoraStreamState, &op);
  ssize_t bytesWritten = 0;
  int pagesOut = 0;
  while(ogg_stream_pageout(&theoraStreamState, &og)) {
    pagesOut ++;
    bytesWritten = write(outputFd, og.header, og.header_len);
    if(bytesWritten != og.header_len)
    {
      fprintf(stderr, "[theora_write_frame] Error: Could not write to file\n");
      return -1;
    }
    bytesWritten = write(outputFd, og.body, og.body_len);
    if(bytesWritten != og.body_len)
    {
      bytesWritten = fprintf(stderr, "[theora_write_frame] Error: Could not write to file\n");
      return -1;
    }
  }
  return pagesOut;
}

其中encoderInfo是用于初始化编码器的th_info结构(对我来说数据部分是静态的)。

在最后一帧上,在 th_encode_packetout() 上设置最后一帧将确保流正确终止。

完成后,请确保进行清理(主要是关闭 fds)。 th_info_clear() 将清除 th_info 结构,并且 th_encode_free() 将释放编码器上下文。

显然,您需要将位图转换为 YUV 平面,然后才能将它们传递给 theora_write_frame()。

希望这对您有所帮助。祝你好运!

The entire solution is a little lengthy to post on here as a code sample, but if you download libtheora from Xiph.org, there is an example png2theora. All of the library functions I am about to mention can be found in the documentation on Xiph.org for theora and ogg.

  1. Call th_info_init() to initialise a th_info structure, then set up you output parameters by assigning the appropriate members in that.
  2. Use that structure in a call to th_encode_alloc() to get an encoder context
  3. Initialise an ogg stream, with ogg_stream_init()
  4. Initialise a blank th_comment structure using th_comment_init

Iterate through the following:

  1. Call th_encode_flushheader with the the encoder context, the blank comment structure and an ogg_packet.
  2. Send the resulting packet to the ogg stream with ogg_stream_packetin()

Until th_encode_flushheader returns 0 (or an error code)

Now, repeatedly call ogg_stream_pageout(), every time writing the page.header and then page.body to an output file, until it returns 0. Now call ogg_stream_flush and write the resulting page to the file.

You can now write frames to the encoder. Here is how I did it:

int theora_write_frame(int outputFd, unsigned long w, unsigned long h, unsigned char *yuv_y, unsigned char *yuv_u, unsigned char *yuv_v, int last)
{
  th_ycbcr_buffer ycbcr;
  ogg_packet op;
  ogg_page og;

  unsigned long yuv_w;
  unsigned long yuv_h;

  /* Must hold: yuv_w >= w */
  yuv_w = (w + 15) & ~15;
  /* Must hold: yuv_h >= h */
  yuv_h = (h + 15) & ~15;

  //Fill out the ycbcr buffer
  ycbcr[0].width = yuv_w;
  ycbcr[0].height = yuv_h;
  ycbcr[0].stride = yuv_w;
  ycbcr[1].width = yuv_w;
  ycbcr[1].stride = ycbcr[1].width;
  ycbcr[1].height = yuv_h;
  ycbcr[2].width = ycbcr[1].width;
  ycbcr[2].stride = ycbcr[1].stride;
  ycbcr[2].height = ycbcr[1].height;

  if(encoderInfo->pixel_fmt == TH_PF_420)
  {
    //Chroma is decimated by 2 in both directions
    ycbcr[1].width = yuv_w >> 1;
    ycbcr[2].width = yuv_w >> 1;
    ycbcr[1].height = yuv_h >> 1;
    ycbcr[2].height = yuv_h >> 1;
  }else if(encoderInfo->pixel_fmt == TH_PF_422)
  {
    ycbcr[1].width = yuv_w >> 1;
    ycbcr[2].width = yuv_w >> 1;
  }else if(encoderInfo->pixel_fmt != TH_PF_422)
  {
    //Then we have an unknown pixel format
    //We don't know how long the arrays are!
    fprintf(stderr, "[theora_write_frame] Unknown pixel format in writeFrame!\n");
    return -1;
  }

  ycbcr[0].data = yuv_y;
  ycbcr[1].data = yuv_u;
  ycbcr[2].data = yuv_v;

  /* Theora is a one-frame-in,one-frame-out system; submit a frame
     for compression and pull out the packet */
  if(th_encode_ycbcr_in(encoderContext, ycbcr)) {
    fprintf(stderr, "[theora_write_frame] Error: could not encode frame\n");
    return -1;
  }

  if(!th_encode_packetout(encoderContext, last, &op)) {
    fprintf(stderr, "[theora_write_frame] Error: could not read packets\n");
    return -1;
  }

  ogg_stream_packetin(&theoraStreamState, &op);
  ssize_t bytesWritten = 0;
  int pagesOut = 0;
  while(ogg_stream_pageout(&theoraStreamState, &og)) {
    pagesOut ++;
    bytesWritten = write(outputFd, og.header, og.header_len);
    if(bytesWritten != og.header_len)
    {
      fprintf(stderr, "[theora_write_frame] Error: Could not write to file\n");
      return -1;
    }
    bytesWritten = write(outputFd, og.body, og.body_len);
    if(bytesWritten != og.body_len)
    {
      bytesWritten = fprintf(stderr, "[theora_write_frame] Error: Could not write to file\n");
      return -1;
    }
  }
  return pagesOut;
}

Where encoderInfo is the th_info structure used to initialise the encoder (static in the data section for me).

On your last frame, setting the last frame on th_encode_packetout() will make sure the stream terminates properly.

Once your done, just make sure to clean up (closing fds mainly). th_info_clear() will clear the th_info structure, and th_encode_free() will free your encoder context.

Obviously, you'll need to convert your bitmap into YUV planes before you can pass them to theora_write_frame().

Hope this is of some help. Good luck!

深巷少女 2024-08-18 15:15:23

这是 libtheora API示例代码

这是一个 微型操作指南,展示了如何使用 theora 二进制文件。当编码器读取视频的原始未压缩“yuv4mpeg”数据时,您也可以通过将视频帧传输到编码器来从应用程序中使用该数据。

Here's the libtheora API and example code.

Here's a micro howto that shows how to use the theora binaries. As the encoder reads raw, uncompressed 'yuv4mpeg' data for video you could use that from your app, too by piping the video frames to the encoder.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文