GameDev.ru — Разработка игр
GameDev.ru / Статьи / Самый простой способ воспроизведения видео со звуком OpenGL + VLC в текстуру

Самый простой способ воспроизведения видео со звуком OpenGL + VLC в текстуру

Внимание! Этот документ ещё не опубликован.

Автор:

Всем привет!

Я только изучаю С++ и не могу сказать что в моем примере все идеально верно сделано, я старался написать по минимуму и только саму суть:
Как воспроизводить видео в OpenGL текстуру.

OpenGL_VLC | Самый простой способ воспроизведения видео со звуком OpenGL + VLC в текстуру

Первая попытка у меня была с DirectShow, я до сих пор с содроганием вспоминаю его АПИ. Оно работало но очень тормозило на больших видео файлах (FullHD)
Вторая - захват через OpenCV, работает быстро и поддерживает много форматов, но увы без звука, и нужно самому синхронизировать скорость.
И вот мне подсказали про VLC плеер.

http://www.videolan.org/vlc/

И как оказалось нормальных примеров с пояснениями даже на английском языке не так много, тем более под OpenGL.
Есть туториал с SDL связкой, но я не использую SDL в своем проекте, по этому пришлось хорошенько покопаться прежде чем все заработало.
Плюс ко всему половина из примеров на старых версиях, и в новых эти комманды не поддерживаются.

Ну довольно слов, вот код с пояснениями, + проект на MSVC++ Express 2010.

#include "stdafx.h"

// Подключаем openGL
#define GLEW_STATIC 1

#include <GL\glew.h>
#include <GL\gl.h>
#include <GL\glu.h>
#include <GL\glaux.h>
#include <GL\glfw.h>

// и его библиотеки
#pragma comment( lib, "glu32.lib" )
#pragma comment( lib, "opengl32.lib" )
#pragma comment( lib, "glew32s.lib" )
#pragma comment( lib, "GLAUX.lib" )
#pragma comment ( lib , "GLFW.lib")

// Закомментируем чтобы не ругался на конфликт ДЛЛок
#pragma comment(linker, "/nodefaultlib:libcmt.lib")

// Подключаем VLC плеер
#include <vlc\vlc.h>

// и его библиотеку
#pragma comment (lib,"libvlc.lib")

// Разрешение окошка
int graphicsWidth = 800;
int graphicsHeight = 600;

// Размер текстуры куда будем рендерить видео
int textureWidth = 512;
int textureHeight = 512;

// В этот массив будем получать кадры из видео, а потом перекидывать в 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 )
{
  (void) data;
}

// Вызывается плеером после display
void unlock( void *data, void *id, void *const *ipixels )
{
  
}


int _tmain( int argc, _TCHAR* argv[] )
{
  // Создаем GLFW окно для рендера OpenGL
  glfwInit();
  glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE );
  glfwOpenWindow( graphicsWidth, graphicsHeight, 0, 0, 0, 0, 24, 0, GLFW_WINDOW );
  
  // и поставим его по центру экрана
  GLFWvidmode desktopMode;
  glfwGetDesktopMode( &desktopMode );
  glfwSetWindowPos( desktopMode.Width/2 - graphicsWidth/2, desktopMode.Height/2 - graphicsHeight/2 );

  // Подключаем все последние фишки OpenGL, в том числе работу с текстурами.
  glewInit();

  // Включаем использование текстур
  glEnable( GL_TEXTURE_2D );

  // Создаем пустую текстуру
  GLuint glTexture;
  glGenTextures( 1, &glTexture );
  
  // ставим ее активной
  glActiveTexture( GL_TEXTURE0 );
  glBindTexture( GL_TEXTURE_2D, glTexture );

  // установим параметры рендера текстуры: фильтрация, генерация mip-map
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
  glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
  glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f );

  // Выделяем память для нашей текстуры
  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );

  // Выделяем память куда плеер будет скидывать кадр, *4 т.к. каждый пиксель плеер отдает в BGRA
  pixels=new unsigned char[ textureWidth * textureHeight * 4 ];

  // Переменные для плеера
  libvlc_instance_t *libvlc;  // сама библиотека
  libvlc_media_t *m;      // киношка которую будем крутить
  libvlc_media_player_t *mp;  // плеер, который будет крутить киношку (по идее можно несколько плееров создать и одновременно крутить)

  // параметры плеера, передаются в него коммандной строкой
  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);
  
  // Инициализация VLC библиотеки
  libvlc = libvlc_new( vlc_argc, vlc_argv );
  
  // Создание плеера
  mp = libvlc_media_player_new( libvlc );

  // Укажем плееру какие CALLBACKи вызывать при рендере кадра
  // а уже в калбеках (см выше) говорим в какой буферп положить кадр
  libvlc_video_set_callbacks( mp, lock, unlock, display, NULL );
    
  // Укажем формат пикселей (RV32 это RGBA8 OpenGLя), размер кадра (по размеру текстуры) и размер одной строки кадра (textureWidth*4, плееру зачем-то это нужно знать)
  libvlc_video_set_format( mp, "RV32", textureWidth, textureHeight, textureWidth*4 );

  // Укажем какой файл воспроизвести
  m  = libvlc_media_new_path( libvlc, "Video/time_machine.avi" );
  libvlc_media_player_set_media( mp, m );
  libvlc_media_release( m );
  
  // Мотор! После этой комманды плеер начнет воспроизведение со звуком, и будет сбрасывать каждый кадр в наш массив pixels
  libvlc_media_player_play( mp );

  // Инициализация матриц OpenGL
  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  gluPerspective( 50.0f, (float)graphicsWidth/(float)graphicsHeight, 0.1f, 1000.0f );
  gluLookAt( 0.0f, -1.0f, -3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f );

  glMatrixMode( GL_MODELVIEW );
  glLoadIdentity();
  glRotatef( -40.0f, 0.0f, 1.0f, 0.0f );  // повернем немного модель, чтобы видно было что все по честному в 3Д

  // основной цикл
  while(glfwGetWindowParam(GLFW_OPENED))
  {
    if( glfwGetKey(GLFW_KEY_ESC)==GLFW_PRESS ) break;
    
    // т.к. плеер сам, в отдельном потоке сбрасывает кадры в pixels
    // выходит в любой момент нам доступен очередной кадр
    // скопируем его из pixels в текстуру OpenGL
    // а так как текстура у нас одна и она всегда активна, то при рендере она будет использоваться с уже новым кадром
    // GL_BGRA т.к. RGB перевернуто в буфере плеера
    glTexSubImage2D ( GL_TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels );

    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );

    // Медленно крутим модель
    glRotatef( 0.01f, 0.0f, 1.0f, 0.0f );

    // Рисуем модель
    glBegin( GL_QUADS );

      glTexCoord2f( 1.0f, 0.0f );
      glVertex3f( -1.0f, 1.0f, 0.0f );
    
      glTexCoord2f( 0.0f, 0.0f );
      glVertex3f( 1.0f, 1.0f, 0.0f );
    
      glTexCoord2f( 0.0f, 1.0f );
      glVertex3f( 1.0f, -1.0f, 0.0f );
    
      glTexCoord2f( 1.0f, 1.0f );
      glVertex3f( -1.0f, -1.0f, 0.0f );

    glEnd();

    // Вывод на экран
    glfwSwapBuffers();
  }

  // Остановим воспроизведение
  libvlc_media_player_stop( mp );
  
  // Подождем пока плеер остановится
  while ( libvlc_media_player_is_playing( mp ) )
  {
    glfwPollEvents();
  }

  // Почистим все перед выходом
  glDeleteTextures( 1, &glTexture );
  delete pixels;
  libvlc_media_player_release( mp );
  libvlc_release( libvlc );

  return 0;
}

Важно!

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

Все файлы в проекте VLC от 2.0.5 версии, просто копируйте в свой проект - подключайте как у меня и все заработает.

Если вы все же захотите самостоятельно подключить VLC в свой проект используя СДК с официального сайта - могут возникнуть сложности. Дело в том, что lib файлы из СДК содержат функции которых нет в ДЛЛ, пришлось делать lib из DLL по этому примеру
http://wiki.videolan.org/GenerateLibFromDll

Архив с проектом
https://goo.gl/kE7gbU

20 июня 2013

#GLFW, #OpenGL, #VLC, #видео


Обновление: 9 июля 2016

2001—2018 © GameDev.ru — Разработка игр