Как создать видео в градациях серого с помощью avconv?

Некоторое время я искал быстрый и надежный способ создания видео в градациях серого с библиотекой avconv из кадров, снятых/созданных с помощью OpenCV, в приложении C++.

Я знаю, что у OpenCV есть внутренний способ создания видео, однако у него есть некоторые ограничения производительности и параметров кодирования.

Итак, таким образом, я хотел бы знать, какие есть варианты выполнения этой задачи?


person Fernando    schedule 20.06.2016    source источник


Ответы (1)


Для выполнения этой задачи одним из найденных мной вариантов, который соответствует моим потребностям, является следующий класс:

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
}
class AVConvVideoMaker
{
    AVCodec* codec;
    AVCodecContext* context;
    AVFrame* picture;
    int imgWidth, imgHeight, imgBufferSize;
    FILE* outputFile;
    int outputBufferSize;
    uint8_t* outputBuffer;
    int pictureBufferSize;
    uint8_t* pictureBuffer;
    int outputSize;

public:
    AVConvVideoMaker(std::string outputFilePath, int imgWidth, int imgHeight)
        : codec(NULL)
        , context(NULL)
        , picture(NULL)
        , imgWidth(imgWidth)
        , imgHeight(imgHeight)
        , imgBufferSize(imgWidth*imgHeight)
        , outputFile(fopen(outputFilePath.c_str(), "wb"))
        , outputBufferSize(100000)
        , outputBuffer(new uint8_t[outputBufferSize])
        , pictureBufferSize((imgBufferSize*3)/2)
        , pictureBuffer(new uint8_t[pictureBufferSize])
        , outputSize(0)
    {
        avcodec_register_all();

        this->codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO);
        if (!this->codec)
        {
            throw std::runtime_error("Codec not found");
        }
        this->context = avcodec_alloc_context3(codec);
        this->picture = avcodec_alloc_frame();

        this->context->bit_rate = 400000;
        this->context->width = this->imgWidth;
        this->context->height = this->imgHeight;
        this->context->time_base = (AVRational){1, 25};
        this->context->gop_size = 10;
        this->context->max_b_frames = 1;
        this->context->pix_fmt = PIX_FMT_YUV420P;
        if(avcodec_open2(this->context, this->codec, NULL) < 0)
        {
            throw std::runtime_error("Could not open codec");
        }
        if(!this->outputFile)
        {
            throw std::runtime_error("Could not open video output file");
        }
        this->picture->data[0] = this->pictureBuffer;
        this->picture->data[1] = this->picture->data[0] + imgBufferSize;
        this->picture->data[2] = this->picture->data[1] + imgBufferSize / 4;
        this->picture->linesize[0] = this->imgWidth;
        this->picture->linesize[1] = this->imgWidth / 2;
        this->picture->linesize[2] = this->imgWidth / 2;
    }

    void insertFrame(cv::Mat1b& img)
    {
        fflush(stdout);

        /* Y */
        for(int y=0; y < this->context->height; y++)
        {
            for(int x=0; x < this->context->width; x++)
            {
                this->picture->data[0][y * picture->linesize[0] + x] = img.at<uchar>(y,x);
            }
        }

        /* Cb and Cr */
        for(int y=0; y < this->context->height/2; y++)
        {
            for(int x=0; x < this->context->width/2; x++)
            {
                this->picture->data[1][y * this->picture->linesize[1] + x] = 128;
                this->picture->data[2][y * this->picture->linesize[2] + x] = 128;
            }
        }
        this->outputSize = avcodec_encode_video(this->context, this->outputBuffer, this->outputBufferSize, this->picture);
        fwrite(this->outputBuffer, 1, outputSize, this->outputFile);
    }

    ~AVConvVideoMaker()
    {
        this->outputBuffer[0] = 0x00;
        this->outputBuffer[1] = 0x00;
        this->outputBuffer[2] = 0x01;
        this->outputBuffer[3] = 0xb7;
        fwrite(this->outputBuffer, 1, 4, this->outputFile);
        fclose(this->outputFile);

        avcodec_close(this->context);
        av_free(this->context);
        av_free(this->picture);

        delete outputBuffer;
        delete pictureBuffer;
    }

};

Для компиляции этого в Ubuntu 16.04 вам нужно связать:

g++ --std=c++11 main.cpp -lopencv_core -lopencv_highgui -lavutil -lavcodec
person Fernando    schedule 20.06.2016