首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VP8 C/C++源代码,如何将ARGB格式的帧编码为帧,而不是从文件中编码。

VP8 C/C++源代码,如何将ARGB格式的帧编码为帧,而不是从文件中编码。
EN

Stack Overflow用户
提问于 2020-10-22 08:36:51
回答 1查看 370关注 0票数 2

我正在尝试开始使用VP8 library,我不是在构建in the standard way they tell you to,我只是将所有的主文件和"encoder“文件夹加载到一个新的Visual Studio项目中,并将C文件包含在一个外部的"C”DLL导出函数中,到目前为止构建都还不错。我只是不知道从哪里开始从ARGB开始编码,比如说,将3帧C++数据编码成一个非常基本的视频,只是为了开始

我能找到的唯一示例是在名为ARGB的examples文件夹中,尽管它们的前提是它们已经加载到另一个文件中并解析它的帧,然后转换它,所以看起来有点复杂,我只想传入一个包含几个ARGB帧的字节数组,并让它输出一个非常简单的VP8视频。

我见过How to encode series of images into VP8 using WebM VP8 Encoder API? (C/C++),但公认的答案只是链接到the build instructions和引用the general specification of the vp8 format,我能找到的最接近的是the example encoding parameters,但我只是想从C++做所有的事情,除了默认的simple_encoder.c之外,我似乎找不到任何其他的例子

只是引用一些相关的部分,我想我理解了,但仍然需要更多的帮助

代码语言:javascript
复制
//in int main...
...
vpx_image_t raw;
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
                     info.frame_height, 1)) {
    //"Failed to allocate image." error
}

所以我想我大体上理解了这一部分,VPX_IMG_FMT_I420是唯一不是在这个文件本身中生成的部分,但它是在vpx_image.h中生成的,首先是

代码语言:javascript
复制
#define VPX_IMG_FMT_PLANAR 
//then after...
typedef enum vpx_img_fmt {
    VPX_IMG_FMT_NONE,
    VPX_IMG_FMT_RGB24,   /**< 24 bit per pixel packed RGB */
    ///some other formats....
    VPX_IMG_FMT_ARGB,     /**< 32 bit packed ARGB, alpha=255 */

    VPX_IMG_FMT_YV12    = VPX_IMG_FMT_PLANAR | VPX_IMG_FMT_UV_FLIP | 1, /**< planar YVU */
    VPX_IMG_FMT_I420    = VPX_IMG_FMT_PLANAR | 2,
   
  } vpx_img_fmt_t; /**< alias for enum vpx_img_fmt */

因此,我想我的问题的一部分已经通过编写这篇文章得到了回答,其中一种格式是VPX_IMG_FMT_ARGB,尽管我不知道它在哪里定义,但我猜在上面的代码中我会将其替换为

代码语言:javascript
复制
const VpxInterface *encoder = get_vpx_encoder_by_name("v8");

vpx_image_t raw;
VpxVideoInfo info = { 0, 0, 0, { 0, 0 } };

info.frame_width = 1920;
info.frame_height = 1080;
info.codec_fourcc = encoder->fourcc;
info.time_base.numerator = 1;
info.time_base.denominator = 24;

bool didIt = vpx_img_alloc(&raw, VPX_IMG_FMT_ARGB, 
          info.frame_width, info.frame_height/*example width and height*/, 1)
//check didIt..

vpx_codec_enc_cfg_t cfg;
vpx_codec_ctx_t codec;
vpx_codec_err_t res;

res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
//check if !res for error

cfg.g_w = info.frame_width;
cfg.g_h = info.frame_height;
cfg.g_timebase.num = info.time_base.numerator;
cfg.g_timebase.den = info.time_base.denominator;
cfg.rc_target_bitrate = 200;

VpxVideoWriter *writer = NULL;

writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
//check if !writer for error

bool startIt = vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0);
//not even sure where codec was set actually..


//check !startIt for error starting

//now the next part in the original is where it reads from the input file, but instead
//I need to pass in an array of some ARGB byte arrays..
//thing is, in the next step they use a while loop for 
//vpx_img_read(&raw, fopen("path/to/YV12formatVideo", "rb"))
//to set the contents of the raw vpx image allocated earlier, then
//they call another program that writes it to the writer object,
//but I don't know how to read the actual ARGB data directly into the raw image
//without using fopen, so that's one question (review at end)

//so I'll just put a placeholder here for the **question**

//assuming I have an array of byte arrays stored individually
//for simplicity sake
int size = 1920 * 1080 * 4;

uint8_t imgOne[size] = {/*some big byte array*/};
uint8_t imgTwo[size] = {/*some big byte array*/};
uint8_t imgThree[size] = {/*some big byte array*/};

uint8_t *images[] = {imgOne, imgTwo, imgThree};

int framesDone = 0;
int maxFrames = 3;

//so now I can replace the while loop with a filler function 
//until I find out how to set the raw image with ARGB data
while(framesDone < maxFrames) {
    magicalFunctionToSetARGBOfRawImage(&raw, images[framesDone]);
    
    encode_frame(&codec, &raw, framesDone, 0, writer);
    
    framesDone++;
}

//now apparently it needs to be flushed after

while(encode_frame(&codec, 0, -1, 0, writer)){}
vpx_img_free(&raw);
bool isDestroyed = vpx_codec_destroy(&codec);
//check if !isDestroyed for error

//now we gotta define the encode_Frames function, but simpler 
//(and make it above other function for reference purposes 
//or in header

static int encode_frame(
     vpx_codex_ctx_t *coydek, 
     vpx_image_t pic,
     int currentFrame, 
     int flags,
     VpxVideoWriter *koysayv/*writer*/
) {
    //now to substitute their encodeFrame function for
    //the actual raw calls to simplify things
    const DidIt = vpx_codec_encode(
        coydek,
        pic,
        currentFrame,
        1,//duration I think
        flags,//whatever that is
        VPX_DL_REALTIME//different than simlpe_encoder
    );
    
    if(!DidIt) return;//error here
    
    vpx_codec_iter_t iter = 0;
    const vpx_codec_cx_pkt_t *pkt = 0;
    int gotThings = 0;
    
    while(
        (pkt = vpx_codec_get_cx_data(
            coydek,
            &iter
        )) != 0
    ) {
        gotThings = 1;
        
        if(
            pkt->kind 
            == VPX_CODEC_CX_FRAME_PKT //don't exactly
            //understand this part
        ) {
            const 
            int 
            keyframe = (
                pkt
                    ->
                    data
                    .frame
                    .flags 
                    & 
                    VPX_FRAME_IS_KEY
            ) != 0; //don'texactly understand the
            //& operator here or how it gets the keyframe
            
            bool wroteFrame = vpx_video_writer_write_frame(
                koysayv,
                pkt->data.frame.buf
                //I'm guessing this is the encoded 
                //frame data
                ,
                pkt->data.frame.sz,
                pkt->data.frame.pts
            );
            
            if(!wroteFrame) return; //error
        }
    }
    
    return gotThings;
}

问题是,我不知道如何将ARGB数据读取到原始图像缓冲区本身,如上所述,在原始示例中,它们使用vpx_img_read(&raw, fopen("path/to/file", "rb")),但是如果我从字节数组本身开始,那么我应该使用什么函数来代替文件呢?

我有一种感觉,它可以通过vpx_img_read found in tools_common.c函数的源代码来解决:

代码语言:javascript
复制
int vpx_img_read(vpx_image_t *img, FILE *file) {
  int plane;

  for (plane = 0; plane < 3; ++plane) {
    unsigned char *buf = img->planes[plane];
    const int stride = img->stride[plane];
    const int w = vpx_img_plane_width(img, plane) *
                  ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    const int h = vpx_img_plane_height(img, plane);
    int y;

    for (y = 0; y < h; ++y) {
      if (fread(buf, 1, w, file) != (size_t)w) return 0;
      buf += stride;
    }
  }

  return 1;
}

尽管我个人没有足够的经验知道如何获得单帧ARGB数据,但我认为关键部分是fread(buf, 1, w, file),它似乎将file的各个部分读取到代表img->planes[plane];buf中,然后通过读取buf自动读取到img->planes[plane];中,但我不确定是否是这种情况,也不确定如何替换文件中的fread,只接受一个也被加载到内存中的bye数组……

EN

回答 1

Stack Overflow用户

发布于 2021-01-26 22:43:46

没有定义VPX_IMG_FMT_ARGB,因为libvpx不支持它(据我所见)。要使用此库压缩图像,必须首先将其转换为支持的格式之一,如I420 (VPX_IMG_FMT_I420)。这里的代码(不是我的):https://gist.github.com/racerxdl/8164330对RGB格式做得很好。如果您不想使用libswscale进行从RGB到I420的转换,您可以这样做(此代码将字节的RGBA数组转换为libvpx可以使用的I420 vpx_image ):

代码语言:javascript
复制
    unsigned int   tx       = <width of your image>
    unsigned int   ty       = <height of your image>
    unsigned char *image    = <array of bytes : RGBARGBA... of size ty*tx*4>
    vpx_image_t   *imageVpx = <result that must have been properly initialized by libvpx>

    imageVpx->stride[VPX_PLANE_U    ] = tx/2;
    imageVpx->stride[VPX_PLANE_V    ] = tx/2;
    imageVpx->stride[VPX_PLANE_Y    ] = tx;
    imageVpx->stride[VPX_PLANE_ALPHA] = tx;
    imageVpx->planes[VPX_PLANE_U    ] = new unsigned char[ty*tx/4];
    imageVpx->planes[VPX_PLANE_V    ] = new unsigned char[ty*tx/4];
    imageVpx->planes[VPX_PLANE_Y    ] = new unsigned char[ty*tx  ];
    imageVpx->planes[VPX_PLANE_ALPHA] = new unsigned char[ty*tx  ];

    unsigned char *planeY  = imageVpx->planes[VPX_PLANE_Y    ];
    unsigned char *planeU  = imageVpx->planes[VPX_PLANE_U    ];
    unsigned char *planeV  = imageVpx->planes[VPX_PLANE_V    ];
    unsigned char *planeA  = imageVpx->planes[VPX_PLANE_ALPHA];

    for (unsigned int y=0; y<ty; y++)
    {
        if (!(y % 2))
        {
            for (unsigned int x=0; x<tx; x+=2)
            {
                int r = *image++;
                int g = *image++;
                int b = *image++;
                int a = *image++;

                *planeY++ = max(0, min(255, (( 66*r + 129*g +  25*b) >> 8) + 16));
                *planeU++ = max(0, min(255, ((-38*r + -74*g + 112*b) >> 8) + 128));
                *planeV++ = max(0, min(255, ((112*r + -94*g + -18*b) >> 8) + 128));
                *planeA++ = a;

                r = *image++;
                g = *image++;
                b = *image++;
                a = *image++;

                *planeA++ = a;
                *planeY++ = max(0, min(255, ((66*r + 129*g + 25*b) >> 8) + 16));
            }
        }
        else
        {
            for (unsigned int x=0; x<tx; x++)
            {
                int const r = *image++;
                int const g = *image++;
                int const b = *image++;
                int const a = *image++;

                *planeA++ = a;
                *planeY++ = max(0, min(255, ((66*r + 129*g + 25*b) >> 8) + 16));
            }
        }
    }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64473578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档