Как использовать FFMPEG API для декодирования выделенной памяти клиенту - PullRequest
0 голосов
/ 25 марта 2020

Я пытаюсь использовать API FFMPEG для декодирования в буфер, определенный клиентской программой, следуя советам в этом вопросе , но используя новый шаблон для декодирования вместо устаревшего avcodec_decode_video2 function.

Если мой входной файл имеет формат I-frame, все работает отлично. Я протестировал файл .mov с кодировкой v210 (без сжатия).

Однако, если вход имеет формат long-GoP (я пытаюсь использовать высокий профиль H.264 4: 2: 2 в файл mp4) я получаю следующий приятно психоделический / импрессионистский c результат:

Crowd run. On acid!

Ясно что-то движение-вектор что происходит здесь!

И если я позволю FFMPEG управлять своими собственными буферами с вводом H.264 с помощью , а не , переопределяя AVCodecContext::get_buffer2, я могу сделать копию из полученного кадра в желаемый целевой буфер и получите хорошие результаты.

Вот мой метод декодера, _frame и _codecCtx являются объектными элементами типа AVFrame* и AVCodecContext* соответственно. Они получают allo c d и init'd в конструкторе.

        virtual const DecodeResult decode(const rv::sz_t toggle) override {
        _toggle = toggle & 1;
        using Flags_e = DecodeResultFlags_e;
        DecodeResult ans(Flags_e::kNoResult);

        AVPacket pkt;   // holds compressed data
        ::av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;
        int ret;

        // read the compressed frame to decode
        _err = av_read_frame(_fmtCtx, &pkt);
        if (_err < 0) {
            if (_err == AVERROR_EOF) {
                ans.set(Flags_e::kEndOfFile);
                _err = 0; // we can safely ignore EOF errors
                return ans;
            } else {
                baleOnFail(__PRETTY_FUNCTION__);
            }
        } 

        // send (compressed) packets to the decoder until it produces an uncompressed frame
        do {

            // sender
            _err = ::avcodec_send_packet(_codecCtx, &pkt);
            if (_err < 0) {
                if (_err == AVERROR_EOF) {
                    _err = 0; // EOFs are ok
                    ans.set(Flags_e::kEndOfFile);
                    break;
                } else {
                    baleOnFail(__PRETTY_FUNCTION__);
                }
            }

            // receiver
            ret = ::avcodec_receive_frame (_codecCtx, _frame);
            if (ret == AVERROR(EAGAIN)) {
                continue;
            } else if (ret == AVERROR_EOF) {
                ans.set(Flags_e::kEndOfFile);
                break;
            } else if (ret < 0) {
                _err = ret;
                baleOnFail(__PRETTY_FUNCTION__);
            } else {
                ans.set(Flags_e::kGotFrame);
            }

            av_packet_unref (&pkt);

        } while (!ans.test(Flags_e::kGotFrame));        

        //packFrame(); <-- used to copy to client image

        return ans;
    }

А вот мое переопределение для get_buffer2

        int getVideoBuffer(struct AVCodecContext* ctx, AVFrame* frm) {
        // ensure frame pointers are all null
        if (frm->data[0] || frm->data[1] || frm->data[2] || frm->data[3]){
            ::strncpy (_errMsg, "non-null frame data pointer detected.", AV_ERROR_MAX_STRING_SIZE);
            return -1;
        }

        // get format descriptor, ensure it's valid.
        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(frm->format));
        if (!desc) {
            ::strncpy (_errMsg, "Pixel format descriptor not available.", AV_ERROR_MAX_STRING_SIZE);
            return AVERROR(EINVAL);
        }

        // for Video, extended data must point to the same place as data.
        frm->extended_data = frm->data;

        // set the data pointers to point at the Image data.
        int chan = 0;
        IMG* img = _imgs[_toggle];
        // initialize active channels
        for (; chan < 3; ++chan) {
            frm->buf[chan] =  av_buffer_create (
                static_cast<uint8_t*>(img->begin(chan)),
                rv::unsigned_cast<int>(img->size(chan)),
                Player::freeBufferCallback, // callback does nothing
                reinterpret_cast<void*>(this),
                0 // i.e. AV_BUFFER_FLAG_READONLY is not set
            );
            frm->linesize[chan] = rv::unsigned_cast<int>(img->stride(chan));
            frm->data[chan] = frm->buf[chan]->data;
        }
        // zero out inactive channels
        for (; chan < AV_NUM_DATA_POINTERS; ++chan) {
            frm->data[chan] = NULL;
            frm->linesize[chan] = 0;
        }
        return 0;
    }

Я могу предположить, что код c Мне нужно хранить опорные кадры в памяти, и поэтому я не очень удивлен, что это не работает, но я не смог выяснить, как заставить его доставлять чистые декодированные кадры в память клиента. Я думал, что AVFrame::key_frame было бы подсказкой, но, наблюдая за его поведением в gdb, он не предоставляет полезного триггера для того, когда выделять AVFrame::buf s из пула буферов и когда их можно инициализировать так, чтобы они указывали на память клиента.

Благодарен за любую помощь!

...