Как я могу визуализировать кадры, декодированные FFmpeg, с использованием аппаратного декодирования с помощью D3D11?

Я завершил процесс декодирования видеокадра с помощью FFmpeg. Формат декодированного кадра — AV_PIX_FMT_NV12. Теперь я хочу отобразить этот кадр на экране с помощью D3D11. Мои вопросы:

  1. Какова эквивалентная концепция декодированного кадра в D3D11? Это текстура?
  2. Я видел много решений, конвертирующих данные NV12 в RGB, но похоже, что DX11 больше не требует этого преобразования.
  3. Я просто хочу отобразить этот кадр, и, поскольку мой кадр находится на графическом процессоре, есть ли более удобный способ рендеринга непосредственно на графическом процессоре без копирования?

Пожалуйста, простите мой не очень хороший английский. Может ли кто-нибудь предоставить справочный пример?

Я уже упоминал об этом проекте с открытым исходным кодом. текст https://github.com/balapradeepswork/D3D11NV12Rendering/tree/master/D3D11NV12Rendering Но я не очень хорошо это понимаю. Поскольку я не использую dx11 для создания игр, а только для показа видео, я жду более простого решения. Это беспокоило меня уже несколько недель, может ли кто-нибудь дать мне совет (даже хорошие учебники, некоторые из которых я нашел слишком старыми). Спасибо искренне!

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

Community 07.06.2024 13:18
stackoverflow.com/questions/78605610/… По этой ссылке представлено более подробное описание моего процесса декодирования.
mercuric taylor 11.06.2024 07:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
175
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Здесь невозможно дать полный ответ на этот вопрос, но исходный код проигрывателя VLC для Windows с открытым исходным кодом покажет, как это сделать.

Чтобы ответить на ваши вопросы на более высоком уровне:

Какова эквивалентная концепция декодированного кадра в D3D11? Это текстура?

Если использовать полное аппаратное декодирование, то всегда да. Если нет, то это зависит от того, хотите ли вы использовать графический процессор только для преобразования из NV12 в BGRA, что должно произойти на каком-то этапе процесса. Давайте рассмотрим различные сценарии:

Полное аппаратное ускорение

Сначала у вас будет одна текстура графического процессора для текущего выходного кадра в формате пикселей NV12. Каждый декодированный кадр копируется в эту единственную выходную текстуру. Вы накладываете эту выходную текстуру на простой прямоугольник, служащий вашим «экраном», с направленной прямо на него камерой, а также визуализируете и переворачиваете свою цепочку обмена после копирования каждого кадра.

Для преобразования NV12 в BGRA – что необходимо, поскольку AFAIK ваш конечный вывод в окно должен быть цепочкой обмена BGRA – вам затем необходимо применить аппаратный шейдер к выходной текстуре для преобразования из NV12 в BGRA, иначе цвета будут полностью изменены. неправильный. У VLC он есть, но он распространяется под лицензией GPL, поэтому у вас могут возникнуть проблемы, если вы используете его в своем собственном проекте. Однако фактический алгоритм таков, каков он есть, поэтому, если вы можете написать свой собственный шейдер с помощью HSL, вы сможете скопировать математические вычисления, не копируя код. (Изменить - похоже, что в проекте github есть шейдер от Microsoft, лицензированный MIT, поэтому его можно использовать).

Вдобавок ко всему, настройка всей системы декодирования с использованием libav, если вы еще этого не сделали, является кошмаром, и для разрешения менее 4K на современных ПК нет необходимости. Но если вы его используете, вы бы выделили массив текстур, используя CreateTexture2D с DXGI_FORMAT_NV12 форматом пикселей и без доступа к процессору. Выделите несколько десятков (зависит от того, сколько видеопамяти вы хотите сказать вашему плееру). Вы будете хранить эти текстуры и передавать их в декодер через AVD3D11VAContext::surface, и декодер будет декодировать непосредственно в эти текстуры. Затем у вас есть чистые текстуры VRAM в формате NV12, которые вы постоянно копируете в единую выходную текстуру посредством простого копирования текстур VRAM-VRAM, а затем немедленно помещаете обратно в очередь для повторного использования декодером. Обратите внимание, что это работает только с определенными кодеками libav.

Таким образом, при использовании этого метода все все время остается в VRAM, и это, безусловно, самый эффективный способ декодирования видео с помощью libav и D3D11. Но это чрезвычайно сложно и требует очень глубокого погружения в декодирование видео и, в частности, в libav. Любой, кто пытается дать вам здесь совет, кто не заходил в эту конкретную кроличью нору, скорее всего, не знает, о чем говорит.

Нет аппаратного ускорения

Вы говорите, что используете аппаратное декодирование, но я не уверен, что так оно и есть, поэтому я все же хочу потратить секунду на обсуждение альтернативы, поскольку она намного проще. Кадры, предоставленные вам libav, уже находятся в оперативной памяти процессора, хотя все еще в формате NV12, поэтому вам все равно придется в какой-то момент конвертировать их в BGRA. И здесь у вас есть два варианта:

  1. Вы можете продолжать использовать выходную текстуру D3D NV12, преобразованную в прямоугольник, как указано выше, используя аппаратный шейдер для преобразования из NV12. Затем для каждого кадра вам придется копировать текстуру кадра в выходную текстуру, что означает копирование из RAM во VRAM, что, как вы, вероятно, знаете, происходит очень медленно.

  2. Вы можете делать все, что связано с ЦП/ОЗУ, используя sws_getCachedContext и sws_scale для получения пикселей BGRA. Если вы сделаете это таким образом, вам вообще не понадобится использовать D3D; на этом этапе у вас будут просто необработанные данные BGRA, которые вы сможете скопировать в окно с помощью D2D или даже GDI+.

Поскольку копирование из RAM в VRAM происходит очень медленно, я не могу сказать вам, быстрее ли (1) или (2). Другими словами, преимущества использования графического процессора для преобразования NV12 в BGRA могут быть легко нивелированы медлительностью копирования данных NV12 во VRAM. Так что здесь вам просто придется поэкспериментировать.

Я видел много решений, конвертирующих данные NV12 в RGB, но похоже, что DX11 больше не требует этого преобразования.

Хм. Не уверен, что вы имеете в виду. Да, D3D11 может обрабатывать текстуру NV12 в VRAM или RAM, но конечная выходная поверхность или цепочка обмена всегда должна быть BGRA в Windows AFAIK, поэтому вам придется конвертировать в какой-то момент процесса. Самый эффективный способ сделать это — применить шейдер к выходной текстуре NV12.

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

Да, для этого потребуется пойти по пути ускорения. Если ваш кадр находится на графическом процессоре, значит, вы уже используете HA, и если да, то у вас уже должен быть доступ к текстурам DXGI, на которые выводит декодер.

Редактировать: Я просмотрел проект github, на который вы ссылались, и он не использует ffmpeg/libav для декодирования. Текстуры, которые он считывает с диска, уже являются кадрами NV12. Так что это не пример аппаратного декодирования.

Для настоящего аппаратного декодирования снова посмотрите VLC и то, как он использует libav (базовую библиотеку, лежащую в основе ffmpeg) для прямого декодирования в текстуры DXGI.

Проект Github ДЕЙСТВИТЕЛЬНО демонстрирует, как использовать шейдер на текстурах NV12 после их декодирования, включая настройку области просмотра, шейдера и т. д., и это все, что вам также придется сделать. Так что, конечно, это хороший ресурс, но это еще не полдела, когда дело доходит до создания полноценного игрока.

Большое спасибо за ваше терпение, так как я новичок, и мои описания проблем не очень ясны. Мое текущее решение использует AV_HWDEVICE_TYPE_D3D11VA и устанавливает формат AV_PIX_FMT_D3D11. Теперь у меня есть AVFrame. Согласно документации, «data[0] содержит указатель ID3D11Texture2D», я пытаюсь визуализировать эту текстуру D3D с графического процессора на экран. Как вы думаете, моя новая идея осуществима? И есть ли способ избежать копирования с графического процессора на графический процессор.

mercuric taylor 17.06.2024 12:19

Во-первых, вообще не беспокойтесь. Ваш вопрос достаточно ясен для тех, кому не повезло самому пройти через это. :) Честно предупреждаю, что моему плееру уже добрых 7 лет - и я никогда не хочу возвращаться к нему снова - поэтому я использую AV_PIX_FMT_D3D11VA_VLD, который, как говорят в документации ffmpeg, теперь является устаревшим, и они хотят, чтобы вы использовали AV_PIX_FMT_D3D11 вместо него. Похоже, вы на правильном пути, но мои детали реализации могут немного отличаться от ваших. Итак, я думаю, вы спрашиваете, можно ли применить декодированные текстуры кадра непосредственно к прямоугольному «экрану», верно?

Emperor Eto 17.06.2024 13:39

В моем плеере я создаю текстуры кадров (которые использует декодер) с помощью BindFlags = D3D11_BIND_DECODER, но выходная текстура, которая отображается на «экран», — это BindFlags = D3D11_BIND_SHADER_RESOURCE. Я считаю, что это не позволяет вам использовать текстуру кадра непосредственно для «экрана» и вынуждает вас копировать одну текстуру в другую. Однако копирование GPU->GPU происходит очень быстро, поэтому, честно говоря, я бы не слишком беспокоился об этом. Тем не менее, вы также можете попробовать и посмотреть. Пока вы держите D3D11 в режиме отладки, консоль сообщит вам, если что-то пойдет не так и почему.

Emperor Eto 17.06.2024 13:48

«Значение D3D11_BIND_DECODER: 0x200L Установите этот флаг, чтобы указать, что 2D-текстура используется для получения выходных данных от API декодера. Обычный способ создания ресурсов для выходных данных декодера — вызов метода ID3D11Device::CreateTexture2D для создания массива 2D-текстур. Однако вы не можете использовать массивы текстур, созданные с этим флагом, в вызовах ID3D11Device::CreateShaderResourceView." Я думаю, что это и есть ответ. Таким образом, копированием на выходную поверхность не обойтись. Но опять же, это будет быстро, и я бы не беспокоился об этом.

Emperor Eto 17.06.2024 14:15

И, к вашему сведению, я использую ID3D11DeviceContext::CopySubresourceRegion для копирования из декодированного кадра на выходную поверхность со всеми нулями и нулями, за исключением указателей поверхности src и dest и индекса массива текстур. Достаточно просто.

Emperor Eto 17.06.2024 14:19

Привет, я столкнулся с узким местом графического процессора. Подробнее о проблеме здесь. Есть ли у вас какие-либо идеи?

mercuric taylor 04.07.2024 09:43

@mercurictaylor Конечно, если вы любезно примете этот ответ, я постараюсь помочь с другим.

Emperor Eto 05.07.2024 00:04

Я уже принял ваш ответ, включающий использование CopySubresourceRegion, и принимаю вывод о том, что «вы не можете обойти копирование на поверхность вывода». Я думаю, что этот ответ должен стать окончательным выводом. Указанный вопрос больше не актуален. Еще раз спасибо за ваш терпеливый ответ. Ваш ответ мне, как новичку, который никогда не сталкивался с этим содержанием, очень полезен. Я не носитель английского языка. Я всегда хочу хорошо ответить на ваш ответ, но опаздываю. Извините за это, желаю вам хорошего дня.

mercuric taylor 05.07.2024 03:16

Совершенно никаких проблем. Вы «принимаете» ответ, щелкнув галочку слева и справа под оценкой ответа. Рад, что это было полезно. Чуть позже я рассмотрю другой вопрос.

Emperor Eto 05.07.2024 13:29

Другие вопросы по теме