代码之家  ›  专栏  ›  技术社区  ›  José Tomás Tocino

用libav(ffmpeg)实现RGB到YUV的转换

  •  0
  • José Tomás Tocino  · 技术社区  · 4 年前

    我正在构建一个小程序来捕获屏幕(使用 X11 MIT-SHM extension )在视频上。如果我创建捕获帧的单个PNG文件,效果会很好,但是现在我正在尝试集成libav(ffmpeg)来创建视频,我得到了。。。有趣的结果。

    Expected result

    Obtained result

    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);
        }
    

    at this gist . This question right here 看起来很像我的,但不完全一样,解决方案对我不起作用,尽管我认为这与计算线跨距的方式有关。

    0 回复  |  直到 4 年前
        1
  •  2
  •   szatmary    4 年前

    不要使用 av_image_alloc av_frame_get_buffer .

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

        2
  •  0
  •   José Tomás Tocino    4 年前

    最后,错误不在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;
    

    我应该用这个:

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

    不知何故,我只是乘以矩阵中的水平位移,而不是垂直位移。我一改,效果就很好。