Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.65/17: Рейтинг темы: голосов - 17, средняя оценка - 4.65
Eliador
16 / 16 / 4
Регистрация: 18.05.2011
Сообщений: 83
1

Рендеринг видео потока используя OpenTK и LibVLC

01.04.2014, 08:18. Просмотров 3330. Ответов 5
Метки нет (Все метки)

Здравствуйте!
Требуется отрендерить на полигон видео. Решил использовать LibVLC, нашел пример для OpenGL:
http://www.gamedev.ru/articles/OpenGL_VLC_GLFW
Разница лишь в том, что надо это сделать на OpenTK, тобишь на C#. Есть врапер LibVLC.NET:
http://libvlcnet.codeplex.com/
Воспроизвести видео у меня получилось, это не составило проблем, но вот как перенаправить поток не на отдельное окно или контрол, а в память(как это сделано в примере с OpenGL) не получается, крашится при выходе из первого же колбэка(делегата), причем не с исключением, а с какой-то системной ошибкой, которую винда сама определить не может, все ищет решение. Было мнение, что первый колбэк должен вернуть именно null, но во врапере сигнатура делегата для него имеет IntPtr тип возвращаемого значения, который не может быть null, а IntPtr.Zero - это все-таки конкретное значение(0). Пробовал это решить, использую PInvoke и сам вручную подключал плюсовую dll, а метод libvlc_video_set_callbacks и колбэк Lock делал как unsafe и возвращал void*, но не помогло.

Сейчас у меня 3 версии того, в чем может быть проблема:
1. В примере по первой ссылке не правельно указаны параметры передаваемые в плеер. Т.е. перенаправление потока осуществляется не vmem командой, а вот какой я понять не могу, не могу найти хоть какуюто информацию об атрибутах запуска vlc. В свою очередь, команда --vout точно работает, т.к. при ее наличие дефолтное окно с видео потоком пропадает и остается только звук.
2. В Lock колбэке надо возврощать что-то конкретное(опять же в примере по первой ссылке возможна ошибка). Но что это должно быть конкретно я не пойму.
3. Что-то не то в последней сборке vlc библиотеки(а использую я именно ее).

Если 3 я еще попробую проверить то с первыми двумя пунктами я в тупике, да и не факт что проблема в этом, это только мои догадки.

В общем нужна помощь по этому вопросу, заранее благодарю.

Добавлено через 23 часа 34 минуты
Извините, что сразу же не приложил код.
Загрузка dll:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    static class LibVlc
    {
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_new(int argc, string[] argv);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_new_path(IntPtr p_instance, IntPtr psz_path);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_player_new_from_media(IntPtr p_md);
        
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_release(IntPtr p_md);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int libvlc_media_player_play(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_player_stop(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_player_release(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_release(IntPtr p_instance);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_video_set_callbacks(IntPtr mp, libvlc_video_lock_cb _lock, libvlc_video_unlock_cb unlock, libvlc_video_display_cb display, IntPtr opaque);
 
        public delegate IntPtr libvlc_video_lock_cb(IntPtr opaque, ref IntPtr planes);
        public delegate void libvlc_video_unlock_cb(IntPtr opaque, IntPtr picture, ref IntPtr planes);
        public delegate void libvlc_video_display_cb(IntPtr opaque, IntPtr picture);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public unsafe static extern void libvlc_video_set_format(IntPtr mp, string chroma, uint width, uint height, uint pitch);
    }
Собственно, использование этой библиотеки:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
argv = new string[9]{
                "--no-xlib",
                "-q",
                "--no-video-title-show",
                "--quiet",
                "--ignore-config",
                "--vout", "vmem",  // это самый основной ключ - рендер в память, а не в окошко плеера
                "-I", "dumy"
            };
            inst = LibVlc.libvlc_new(9, argv);
            m = LibVlc.m_libvlc_media_new_path(inst, "test2.avi");
            mp = LibVlc.libvlc_media_player_new_from_media(m);
            LibVlc.libvlc_media_release(m);
            LibVlc.libvlc_video_set_callbacks(mp, cbLock, cbUnlock, cbDisplay, IntPtr.Zero);
            LibVlc.libvlc_video_set_format(mp, "RV32", 1280, 720, 4 * 1280);
            LibVlc.libvlc_media_player_play(mp);
И конечно делегаты:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
IntPtr cbLock(IntPtr opaque, ref IntPtr planes)
        {
            Pixels = planes; //Pixels определена как глобальная переменная IntPtr
            
            return IntPtr.Zero;
        } //падает при выходе из этого колбэка
        
        void cbUnlock(IntPtr opaque, IntPtr picture, ref IntPtr planes)
        { 
        }
 
        void cbDisplay(IntPtr opaque, IntPtr picture)
        { 
        }
Есть еще вариант(наиболее вероятный), почему оно падает там. Возможно (вероятнее всего) я не правильно определяю формат буфера памяти, куда будут читаться кадры из потока. Это делается с помощью метода libvlc_video_set_format, но вроде бы я там все правильно определил:
RV32 - аналог RGBA8, то-есть каждый пиксель описывается 4-я байтами, а при ширине изображения 1280, размер одного кадра будет составлять 1280 * 4. Разрешение видео несколько раз пересматривал, 1280x720.
В чем проблема не пойму, может я все-таки не правильно формат буфера определяю?
1
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
01.04.2014, 08:18
Ответы с готовыми решениями:

Воспроизведение OGG через OpenTK (OpenTK.OpenAL) из ресурсов
Добрый вечер всем! Собственно, мой первый пост :) Имеется проблема с воспроизведением звуков OGG...

Проверка наличия rtsp-потока с IP-камеры libvlc
Здравствуйте. Пишу программу с возможностью показа видео с IP-камер. Использую libvlc. Процедуры...

Обработка и рендеринг видео. До 70.000р
Здравствуйте! Не могли бы вы, пожалуйста, посоветовать сборку компьютера для обработки и рендеринга...

Цель апгрейда - рендеринг аудио и видео, игры, стримминг
Привет, форумчане. На сегодняшний день передо мной стоит вопрос апгрейда железа компьютера. Что...

Acer5742g при нагрузках(рендеринг видео, игры) перестаёт заряжаться
Добрый день, столкнулся с такой проблемой: при сильных нагрузках на ноутбук Acer5742g: рендеринг,...

5
Eliador
16 / 16 / 4
Регистрация: 18.05.2011
Сообщений: 83
03.04.2014, 08:29  [ТС] 2
Разобрался еще кое в чем, как оказалось я не совсем правельно понял суть Lock колбэка. Lock вызывается не после того как очередной кадр был вычитан из потока, а перед этим, тобиш в параметре planes не содержится вычитанного кадра, а необходимо в него положить адрес буфера, куда будет вычитан очередной кадр.

Делал двумя способами:

1. В лоб, использовал маршализацию
перед воспроизведение выделяем память в неуправляемой куче
C#
1
pMem = Marshal.AllocHGlobal( Width*Height*4 );
pMem - глобыльный IntPtr
а в Lock ссылаюсь на этот буфер
C#
1
planes  = pMem;
при нажатии на стоп или при закрытии приложения память освобождаем
C#
1
Marshal.FreeHGlobal( pMem );
2. Используя структуру GCHandle для получения доступа к неуправляемой памяти. Этот способ я позаимствовал в библиотеке LibVLC.NET
Создаю объект VideoBuffer:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    public class VideoBuffer
    {
 
       public VideoBuffer(uint width, uint height)
        {
            Width = width;
            Height = height;
            PixelFormat = pixelFormat;
            Stride = Width * 4;
            Lines = Height;
            FrameBuffer = new byte[Stride * Lines];
        }
 
        private GCHandle m_GCHandle = default(GCHandle);
 
        internal IntPtr Lock()
        {
            return (m_GCHandle = GCHandle.Alloc(FrameBuffer, GCHandleType.Pinned)).AddrOfPinnedObject();
        }
 
        internal void Unlock()
        {
            m_GCHandle.Free();
        }
 
        public readonly uint Width;
        public readonly uint Height;
        public readonly uint Stride;
        public readonly uint Lines;
        public readonly byte[] FrameBuffer;
    }
перед воспроизведение создаю этот буфер
C#
1
Pixels = new LibVLC.VideoBuffer(1280, 720);
Pixels - глобальный VideoBuffer
а в Lock ссылаюсь на этот буфер
C#
1
planes = Pixels.Lock();
Но всеравно крашится с той же ошибкой, но теперь у меня хоть есть какая-то уверенность, что крашится он из-за кривого обращения к памяти.
Я не очень силен в неуправляемом коде в C# что я делю не так? Или я опять не правельно понял логику перенаправления видео потока в память с помощью libVLC?
1
Eliador
16 / 16 / 4
Регистрация: 18.05.2011
Сообщений: 83
07.04.2014, 09:03  [ТС] 3
Чтобы убедиться, что я правильно понял всю суть перенаправления потока в память у libvlc, я пересобрал проект на cpp.
Вот весь исходник:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
#include <vlc\vlc.h>
 
// В этот массив будем получать кадры из видео, а потом перекидывать в OpenGL их
unsigned char * pixels;
 
// Callback вызывается VLC плеером перед рендером кадра
void *lock( void *data, void **p_pixels )
{
  *p_pixels = pixels; // просто указываем плееру куда положить текущий кадр
    return NULL;
}
 
// Вызывается плеером после *lock
void display( void *data, void *id )
{
  
}
 
// Вызывается плеером после display
void unlock( void *data, void *id, void *const *ipixels )
{
  
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    // параметры плеера, передаются в него коммандной строкой
      char const *vlc_argv[] =
      {
            //"--no-audio", // раскомментировать чтобы не проигрывался звук
            "--no-xlib",
        "-q",
        //"-vvv",    // раскомментировать чтобы плеер выдавал сообщения в консоль
        "--no-video-title-show",
        "--quiet",
            "--ignore-config",
            "--vout", "vmem",  // это самый основной ключ - рендер в память, а не в окошко плеера
        "-I", "dumy"
      };
    int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
    pixels=new unsigned char[ 1280 * 720 * 4 ];
 
    libvlc_instance_t * inst;
    libvlc_media_player_t *mp;
    libvlc_media_t *m;
 
    inst = libvlc_new(vlc_argc, vlc_argv);
    m = libvlc_media_new_path(inst, "Wildlife.wmv");
    mp = libvlc_media_player_new_from_media(m);
    libvlc_media_release(m);
 
    libvlc_video_set_callbacks( mp, lock, unlock, display, NULL );
    libvlc_video_set_format( mp, "RV32", 1280, 720, 1280*4 );
 
    libvlc_media_player_play(mp);
 
    Sleep(10000);
 
    libvlc_media_player_stop(mp);
    libvlc_media_player_release(mp);
    libvlc_release(inst);
 
    return 0;
}
Все работает замечательно, по указателю pixels у меня всегда находится свежий фрейм из потока.
Следовательно ошибка локализована однозначно, все дело в передаче адреса буфера под очередной кадр из потока в колбэке Lock.
Следовательно, вопрос остается тот-же, как на C# представить аналог такого?:
C#
1
2
3
4
unsigned char * pixels;
........
void **p_pixels;
*p_pixels = pixels;
Мне кажется имеет смысл развить этот вопрос в новой теме, касающейся PInvoke, не привязываясь к libvlc, но не нарушу я ли этим правил каких?
1
tezaurismosis
Администратор
Эксперт .NET
8635 / 3922 / 731
Регистрация: 17.04.2012
Сообщений: 8,700
Записей в блоге: 14
10.04.2014, 19:29 4
Eliador, продолжайте тут, вопрос непростой и тему рассматриваете общую с начальной.
0
Eliador
16 / 16 / 4
Регистрация: 18.05.2011
Сообщений: 83
14.04.2014, 08:29  [ТС] 5
Проблема решена!
Все дело было в том, что делегат тоже надо "маршалировать", т.е. назначить ему атрибут UnmanagedFunctionPointer
C#
1
2
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr libvlc_video_lock_cb(IntPtr opaque, ref IntPtr planes);
Во всяком случае теперь не падает при выходе из этого колбэка, теперь надо убедиться в том, что в буфер записывается очередной кадр из потока.

Добавлено через 36 минут
Да, все работает, видео рендерится на полигон!
3
medwedby
1 / 1 / 0
Регистрация: 01.02.2015
Сообщений: 1
01.02.2015, 14:22 6
Eliador,

Пытаюсь повторить, вроде бы работает, но через определенное количество секунд вылазит ошибка "CallbackOnCollectedDelegate".

Как я понял, MSDN говорит что сборщик мусора убивает делегат до того как происходит callback, но я не понял как решить эту проблему.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
 public static class LibVlc
    {
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_new(int argc, string[] argv);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_new_path(IntPtr p_instance, string psz_path);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_player_new_from_media(IntPtr p_md);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_release(IntPtr p_md);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int libvlc_media_player_play(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_player_stop(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_media_player_release(IntPtr p_mi);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_release(IntPtr p_instance);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_video_set_callbacks(IntPtr mp, libvlc_video_lock_cb _lock, libvlc_video_unlock_cb unlock, libvlc_video_display_cb display, IntPtr opaque);
 
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate IntPtr libvlc_video_lock_cb(IntPtr opaque, ref IntPtr planes);
 
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void libvlc_video_unlock_cb(IntPtr opaque, IntPtr picture, ref IntPtr planes);
 
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void libvlc_video_display_cb(IntPtr opaque, IntPtr picture);
 
        [DllImport("VLC\\libvlc.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void libvlc_video_set_format(IntPtr mp, string chroma, uint width, uint height, uint pitch);
 
        private static string[] argv;
        static IntPtr inst;
        static IntPtr m;
        static IntPtr mp;
 
        public static void Init(int SizeX, int SizeY)
        {
 
            argv = new string[9]{
                "--no-xlib",
                "-q",
                "--no-video-title-show",
                "--quiet",
                "--ignore-config",
                "--vout", "vmem",  // это самый основной ключ - рендер в память, а не в окошко плеера
                "-I", "dumy"
            };
 
            inst = LibVlc.libvlc_new(9, argv);
            m = LibVlc.libvlc_media_new_path(inst, "video.avi");
            mp = LibVlc.libvlc_media_player_new_from_media(m);
 
            libvlc_media_release(m);
            libvlc_video_set_callbacks(mp, cbLock, cbUnlock, cbDisplay, IntPtr.Zero);
            libvlc_video_set_format(mp, "RV32", (uint)SizeX, (uint)SizeY, 4 * (uint)SizeX);
            Form1.videoBuffer = new VideoBuffer((uint)SizeX, (uint)SizeY);
        }
        public static IntPtr cbLock(IntPtr opaque, ref IntPtr planes)
        {
            planes = Form1.videoBuffer.Lock();
 
            return IntPtr.Zero;
        } //падает при выходе из этого колбэка
 
        public static void cbUnlock(IntPtr opaque, IntPtr picture, ref IntPtr planes)
        {
        }
 
        public static void cbDisplay(IntPtr opaque, IntPtr picture)
        {
        }
 
        public static void Dispose()
        {
            libvlc_media_player_release(mp);
        }        
 
        public static void Play()
        {            
            libvlc_media_player_play(mp);
        }
        public static void Pause()
        {
        }
        public static void Stop()
        {
            libvlc_media_player_stop(mp);
        }
    }
 
    public class VideoBuffer
    {
        public VideoBuffer(uint width, uint height)
        {
            Width = width;
            Height = height;
            Stride = Width * 4;
            Lines = Height;
            FrameBuffer = new byte[Stride * Lines];
        }
 
        private GCHandle m_GCHandle = default(GCHandle);
 
        internal IntPtr Lock()
        {
            return (m_GCHandle = GCHandle.Alloc(FrameBuffer, GCHandleType.Pinned)).AddrOfPinnedObject();
        }
 
        internal void Unlock()
        {
            m_GCHandle.Free();
        }
 
        public readonly uint Width;
        public readonly uint Height;
        public readonly uint Stride;
        public readonly uint Lines;
        public readonly byte[] FrameBuffer;
    }
1
01.02.2015, 14:22
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
01.02.2015, 14:22

3д моделирование и рендеринг одно и тоже?Что такое рендеринг?
3д моделирование и рендеринг одно и тоже?Что такое рендеринг?

В сервисе видео-наблюдения на малине с передачей видео-потока на веб страничку добавить картинку
Всем доброго дня! Может не совсем по теме, но у меня похоже такая же проблема..... Строю сервис...

LibVLC
Кто нибудь работал с последней версией этой библиотеки? Набросайте пожалуйста примитивный...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru