用libav(ffmpeg)将RGB转换为YUV,使图像三倍化

0 人关注

我正在建立一个小程序,在视频上捕捉屏幕(使用 X11 MIT-SHM扩展 )。如果我为捕获的帧创建单独的PNG文件,效果很好,但现在我试图整合libav(ffmpeg)来创建视频,我得到了...有趣的结果。

我所能达到的最远的结果是这样的。预期的结果(即直接从XImage文件的RGB数据创建的PNG)是这样的。

然而,我得到的结果是这样的。

正如你所看到的,颜色很怪异,图像出现了三次裁剪。我有一个捕捉屏幕的循环,首先我生成了单独的PNG文件(目前在下面的代码中有注释),然后我试图使用libswscale从RGB24转换为YUV420。

while (gRunning) {
        printf("Processing frame framecnt=%i \n", framecnt);
        if (!XShmGetImage(display, RootWindow(display, DefaultScreen(display)), img, 0, 0, AllPlanes)) {
            printf("\n Ooops.. Something is wrong.");
            break;
        // PNG generation
        // snprintf(imageName, sizeof(imageName), "salida_%i.png", framecnt);
        // writePngForImage(img, width, height, imageName);
        unsigned long red_mask = img->red_mask;
        unsigned long green_mask = img->green_mask;
        unsigned long blue_mask = img->blue_mask;
        // Write image data
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                unsigned long pixel = XGetPixel(img, x, y);
                unsigned char blue = pixel & blue_mask;
                unsigned char green = (pixel & green_mask) >> 8;
                unsigned char red = (pixel & red_mask) >> 16;
                pixel_rgb_data[y * width + x * 3] = red;
                pixel_rgb_data[y * width + x * 3 + 1] = green;
                pixel_rgb_data[y * width + x * 3 + 2] = blue;
        uint8_t* inData[1] = { pixel_rgb_data };
        int inLinesize[1] = { in_w };
        printf("Scaling frame... \n");
        int sliceHeight = sws_scale(sws_context, inData, inLinesize, 0, height, pFrame->data, pFrame->linesize);
        printf("Obtained slice height: %i \n", sliceHeight);
        pFrame->pts = framecnt * (pVideoStream->time_base.den) / ((pVideoStream->time_base.num) * 25);
        printf("Frame pts: %li \n", pFrame->pts);
        int got_picture = 0;
        printf("Encoding frame... \n");
        int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
//                int ret = avcodec_send_frame(pCodecCtx, pFrame);
        if (ret != 0) {
            printf("Failed to encode! Error: %i\n", ret);
            return -1;
        printf("Succeed to encode frame: %5d - size: %5d\n", framecnt, pkt.size);
        framecnt++;
        pkt.stream_index = pVideoStream->index;
        ret = av_write_frame(pFormatCtx, &pkt);
        if (ret != 0) {
            printf("Error writing frame! Error: %framecnt \n", ret);
            return -1;
        av_packet_unref(&pkt);

我已经把整个代码放在了这个gist上这里的这个问题看起来和我的很相似,但又不太一样,而且这个解决方案对我来说并不奏效,虽然我认为这和行距的计算方式有关。

c
video
ffmpeg
yuv
libav
José Tomás Tocino
José Tomás Tocino
发布于 2021-04-16
2 个回答
szatmary
szatmary
发布于 2021-04-17
0 人赞同

不要使用 av_image_alloc ,使用 av_frame_get_buffer

(与你的问题无关,但使用 avcodec_encode_video2 现在被认为是不好的做法,应该用 avcodec_send_frame avcodec_receive_packet 来代替 )

谢谢你的建议。我之所以使用老式的,是因为我有一个老版本的libav(我在CentOS 7上),但我打算尝试编译一个较新版本的libav来使用新式的。
José Tomás Tocino
José Tomás Tocino
发布于 2021-04-17
已采纳
0 人赞同

最后,错误不在于libav的使用,而在于将像素数据从 XImage 填充到RGB向量的代码上。我没有使用:

                pixel_rgb_data[y * width + x * 3    ] = red;
                pixel_rgb_data[y * width + x * 3 + 1] = green;
                pixel_rgb_data[y * width + x * 3 + 2] = blue;

我应该用这个。