Подтвердить что ты не робот

Как использовать аппаратное ускорение с помощью ffmpeg

Мне нужно, чтобы ffmpeg декодировал мое видео (например, h264) с помощью аппаратного ускорения. Я использую обычный способ декодирования кадров: read packet → decode frame. И я бы хотел, чтобы ffmpeg ускорил декодирование. Поэтому я построил его с помощью --enable-vaapi и --enable-hwaccel=h264. Но я не знаю, что мне делать дальше. Я попытался использовать avcodec_find_decoder_by_name("h264_vaapi"), но он возвращает nullptr. В любом случае, я могу использовать другие API, а не только API VA. Как предполагается ускорение декодирования ffmpeg?

P.S. Я не нашел примеров в Интернете, который использует ffmpeg с hwaccel.

4b9b3361

Ответ 1

После некоторого исследования я смог реализовать необходимое ускоренное декодирование HW на OS X (VDA) и Linux (VDPAU). Я обновлю ответ, когда я получу руки от реализации Windows. Поэтому начнем с самых простых:

Mac OS X

Чтобы ускорить работу HW на Mac OS, вы должны просто использовать следующее: avcodec_find_decoder_by_name("h264_vda"); Обратите внимание, однако, что вы можете ускорить видео h264 только на Mac OS с помощью FFmpeg.

Linux VDPAU

В Linux все гораздо сложнее (кто удивлен?). FFmpeg имеет 2 ускорителя HW в Linux: VDPAU (Nvidia) и VAAPI (Intel) и только один HW-декодер: для VDPAU. И может показаться вполне разумным использовать декодер vdpau, как в примере с Mac OS выше: avcodec_find_decoder_by_name("h264_vdpau");

Вы можете быть удивлены, узнав, что это ничего не меняет, и у вас нет ускорения вообще. Это потому, что это только начало, вам нужно написать гораздо больше кода, чтобы ускорить работу. К счастью, вам не нужно придумывать решение самостоятельно: есть как минимум 2 хороших примера того, как достичь этого: libavg и сам FFmpeg, libavg имеет класс VDPAUDecoder, который совершенно ясен и на котором я основывал свою реализацию. Вы также можете обратиться к ffmpeg_vdpau.c, чтобы сравнить другую реализацию. По-моему, реализация libavg легче понять.

Единственное, что недостает перечисленным выше примерам, - это надлежащее копирование декодированного кадра в основную память. В обоих примерах используется VdpVideoSurfaceGetBitsYCbCr, который убил всю производительность, которую я получил на своей машине. Вот почему вы можете использовать следующую процедуру для извлечения данных с графического процессора:

bool VdpauDecoder::fillFrameWithData(AVCodecContext* context,
    AVFrame* frame)
{
    VdpauDecoder* vdpauDecoder = static_cast<VdpauDecoder*>(context->opaque);
    VdpOutputSurface surface;
    vdp_output_surface_create(m_VdpDevice, VDP_RGBA_FORMAT_B8G8R8A8, frame->width, frame->height, &surface);
    auto renderState = reinterpret_cast<vdpau_render_state*>(frame->data[0]);
    VdpVideoSurface videoSurface = renderState->surface;

    auto status = vdp_video_mixer_render(vdpauDecoder->m_VdpMixer,
        VDP_INVALID_HANDLE,
        nullptr,
        VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME,
        0, nullptr,
        videoSurface,
        0, nullptr,
        nullptr,
        surface,
        nullptr, nullptr, 0, nullptr);
    if(status == VDP_STATUS_OK)
    {
        auto tmframe = av_frame_alloc();
        tmframe->format = AV_PIX_FMT_BGRA;
        tmframe->width = frame->width;
        tmframe->height = frame->height;
        if(av_frame_get_buffer(tmframe, 32) >= 0)
        {
            VdpStatus status = vdp_output_surface_get_bits_native(surface, nullptr,
                reinterpret_cast<void * const *>(tmframe->data),
                reinterpret_cast<const uint32_t *>(tmframe->linesize));
            if(status == VDP_STATUS_OK && av_frame_copy_props(tmframe, frame) == 0)
            {
                av_frame_unref(frame);
                av_frame_move_ref(frame, tmframe);
                return;
            }
        }
        av_frame_unref(tmframe);
    }
    vdp_output_surface_destroy(surface);
    return 0;
}

В то время как у него есть некоторые "внешние" объекты, используемые внутри, вы должны понимать это, как только вы внедрили часть "get buffer" (к которой упомянутые выше примеры имеют большую помощь). Также я использовал формат BGRA, который был более подходящим для моих нужд, возможно, вы выберете другой.

Проблема со всем этим заключается в том, что вы не можете просто заставить его работать с FFmpeg, вам нужно понять, по крайней мере, основы API VDPAU. И я надеюсь, что мой ответ поможет кому-то в реализации ускорения HW в Linux. Я потратил много времени на это сам, прежде чем понял, что нет простого, однолинейного способа внедрения ускоренного декодирования HW в Linux.

Linux VA-API

Поскольку мой первоначальный вопрос касался VA-API, я не могу оставить его без ответа. Прежде всего, нет никакого декодера для VA-API в FFmpeg, поэтому avcodec_find_decoder_by_name("h264_vaapi") не имеет никакого смысла: это nullptr. Я не знаю, насколько сложнее (или, может быть, проще?) Реализовать декодирование через VA-API, поскольку все примеры, которые я видел, были довольно запугивающими. Поэтому я решил не использовать VA-API вообще, и мне пришлось реализовать ускорение для карты Intel. К счастью для меня, есть библиотека VDPAU (драйвер?), Которая работает над VA-API. Таким образом, вы можете использовать VDPAU на картах Intel!

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

Кроме того, вы можете проверить комментарии к исходному вопросу, где @Timothy_G также упомянул некоторые ссылки, касающиеся VA-API.