пятница, 24 июля 2015 г.

Наложение видеопотока WEB-камеры текстурой в OpenGL


Ничто не мешает на каждую грань выводить свою камеру (любой медиапоток), а сам 3D-объект может быть какой угодно конфигурации. Сабж для тестов в рамках консультации по созданию системы кругового/дополнительного обзора снаружи автомобиля.

Подготовим поверхность и создадим куб:
procedure Init;
begin
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glNewList(CUBE, GL_COMPILE);
  glEnable (GL_TEXTURE_2D);
  glBegin(GL_QUADS);
    glNormal3f(-1.0,  0.0,  0.0);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f(-0.5, -0.5,-0.5);
    glTexCoord2f (1.0, 1.0);
    glVertex3f(-0.5, -0.5, 0.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f(-0.5,  0.5, 0.5);
    glTexCoord2f (0.0, 0.0);
    glVertex3f(-0.5,  0.5,-0.5);
  glEnd;
  glBegin(GL_QUADS);
    glNormal3f( 0.0,  1.0,  0.0);
    glTexCoord2f (0.0, 0.0);
    glVertex3f(-0.5,  0.5,-0.5);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f(-0.5,  0.5, 0.5);
    glTexCoord2f (1.0, 1.0);
    glVertex3f( 0.5,  0.5, 0.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f( 0.5,  0.5,-0.5);
  glEnd;
  glBegin(GL_QUADS);
    glNormal3f( 1.0,  0.0,  0.0);
    glTexCoord2f (0.0, 1.0);
    glVertex3f( 0.5,  0.5,-0.5);
    glTexCoord2f (0.0, 0.0);
    glVertex3f( 0.5,  0.5, 0.5);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0.5);
    glTexCoord2f (1.0, 1.0);
    glVertex3f( 0.5, -0.5,-0.5);
  glEnd;
  glBegin(GL_QUADS);
    glNormal3f( 0.0, -1.0,  0.0);
    glTexCoord2f (0.0, 0.0);
    glVertex3f( 0.5, -0.5,-0.5);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f( 0.5, -0.5, 0.5);
    glTexCoord2f (1.0, 1.0);
    glVertex3f(-0.5, -0.5, 0.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f(-0.5, -0.5,-0.5);
  glEnd;
  glBegin(GL_QUADS);
    glNormal3f( 0.0,  0.0,  1.0);
    glTexCoord2f (1.0, 1.0);
    glVertex3f( 0.5, -0.5, 0.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f( 0.5,  0.5, 0.5);
    glTexCoord2f (0.0, 0.0);
    glVertex3f(-0.5,  0.5, 0.5);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f(-0.5, -0.5, 0.5);
  glEnd;
  glBegin(GL_QUADS);
    glNormal3f( 0.0,  0.0, -1.0);
    glTexCoord2f (0.0, 0.0);
    glVertex3f( 0.5,  0.5,-0.5);
    glTexCoord2f ( 1.0, 0.0);
    glVertex3f( 0.5, -0.5,-0.5);
    glTexCoord2f (1.0, 1.0);
    glVertex3f(-0.5, -0.5,-0.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f(-0.5,  0.5,-0.5);
  glEnd;
  glDisable (GL_TEXTURE_2D);
  glEndList;
  glEnable (GL_LIGHTING);
  glEnable (GL_LIGHT0);
  glEnable (GL_DEPTH_TEST);
  glEnable (GL_CULL_FACE);
end;
procedure SetDCPixelFormat;
var
  nPixelFormat: Integer;
  pfd: TPixelFormatDescriptor;
begin
  FillChar(pfd, SizeOf(pfd), 0);
  pfd.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
  nPixelFormat := ChoosePixelFormat(DC, @pfd);
  SetPixelFormat(DC, nPixelFormat, @pfd);
end;
procedure drawGradientBackground;
begin
    glPushMatrix;
    glLoadIdentity;
    glMatrixMode(GL_PROJECTION);
    glPushMatrix;
    glLoadIdentity;
    gluOrtho2D(0.1, 1.0, 0.0, 1.0);
    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);
    glBegin(GL_QUADS);
    glColor3f(1.0, 0.5, 0.5);
    glVertex2f(0.0, 0.0);
    glColor3f(0.6, 0.4, 0.6);
    glVertex2f(1.0, 0.0);
    glColor3f(0.4, 0.4, 1.0);
    glVertex2f(1.0, 1.0);
    glColor3f(0.6, 0.4, 0.6);
    glVertex2f(0.0, 1.0);
    glEnd;
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glPopMatrix;
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix;
end;

Граббим кадр из потока камеры:
procedure GetBitmap(Bitmap: TBitmap);
var vh: TVideoInfoHeader;
    tmt: TAMMediaType;
    BitmapInfo: TBitmapInfo;
    Buffer: Pointer;
    tmp: array of byte;
    hr: HRESULT;
    sz: integer;
begin
 if pGrabber = nil then EXIT;
 hr:= pGrabber.GetCurrentBuffer(sz, NIL);
 if (sz <= 0) or FAILED(hr) then EXIT;
 try
  ZeroMemory(@tmt, sizeof(TAMMediaType));
  // Получаем тип медиа потока на входе у фильтра перехвата
  hr:= pGrabber.GetConnectedMediaType(tmt);
  if hr<>0 then EXIT;
  vh:= TVideoInfoHeader(tmt.pbFormat^);
  ZeroMemory(@BitmapInfo, sizeof(TBitmapInfo));
  CopyMemory(@BitmapInfo.bmiHeader, @vh.bmiHeader, sizeof(TBITMAPINFOHEADER));
  Buffer:= nil;
  Bitmap.Handle:= CreateDIBSection(0, BitmapInfo, DIB_RGB_COLORS, Buffer, 0, 0);
  SetLength(tmp, sz);
  pGrabber.GetCurrentBuffer(sz, @tmp[0]);
  CopyMemory(Buffer, @tmp[0], tmt.lSampleSize);
 finally SetLength(tmp, 0) end
end;

DirectShow. Построение графа фильтров:
var  _tpf: double;
pConfigMux: IConfigAviMux;  //интерфейс управления мультиплексором
begin
  CoInitialize(nil);
  //получаем интерфейс IGraphBuilder
  CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,
                   IID_IGraphBuilder, GraphBuilder);
  if failed(CoCreateInstance(TGUID(CLSID_FilterGraph),
                            NIL, CLSCTX_INPROC_SERVER,
                            TGUID(IID_IGraphBuilder),
                            GraphBuilder)) then exit;
  //создаем объект для построения графа захвата FCaptureGraphBuilder
  CoCreateInstance(CLSID_CaptureGraphBuilder2,
                   NIL,
                   CLSCTX_INPROC_SERVER,
                   IID_ICaptureGraphBuilder2,
                   FCaptureGraphBuilder);
  //FCaptureGraphBuilder.SetFiltergraph(GraphBuilder); //Задаем граф фильтров
  MArray[combobox1.ItemIndex].BindToObject(NIL, NIL, IID_IBaseFilter, VideoCaptureFilter);
  GraphBuilder.AddFilter(VideoCaptureFilter, 'VideoCaptureFilter'); //Получаем фильтр графа захвата


  //создаем объект для построения фильтра источника FASFReader
 // CoCreateInstance(CLSID_ASFReader, nil, CLSCTX_INPROC, IID_IBaseFilter, FASFReader);
  //Добавляем его в граф
 // GraphBuilder.AddFilter(FASFReader, 'SourceUrl');
  //загружаем в него mms поток из Url
//  (FASFReader as IFileSourceFilter).Load(_wfn{StringToOleStr(Url)}, nil);
  //создаем объект для построения фильтра разделителя audio потока
  CoCreateInstance(CLSID_InfTee, nil, CLSCTX_INPROC, IID_IBaseFilter, FTee_Audio);
  // Добавляем его в граф
  GraphBuilder.AddFilter(FTee_Audio, 'Infinite Pin Tee Filter Audio');
  //создаем объект для построения фильтра разделителя video потока
  CoCreateInstance(CLSID_InfTee, nil, CLSCTX_INPROC, IID_IBaseFilter, FTee_Video);
  // Добавляем его в граф
  GraphBuilder.AddFilter(FTee_Video, 'Infinite Pin Tee Filter Video');
  //создаем объект для построения фильтра вывода звука FAudioRender
  CoCreateInstance(CLSID_AudioRender, nil, CLSCTX_INPROC, IID_IBaseFilter, FAudioRender);
  // Добавляем его в граф
  GraphBuilder.AddFilter(FAudioRender, 'DirectSound Audio Renderer');
  //создаем объект для построения фильтра вывода видео FVideoRender
  CoCreateInstance(CLSID_VideoMixingRenderer, nil, CLSCTX_INPROC, IID_IBaseFilter, FVideoRender);
  // Добавляем его в граф
  GraphBuilder.AddFilter(FVideoRender, 'Video Renderer');

  //--- GRAB
  CoCreateInstance(CLSID_SampleGrabber,
                   NIL,
                   CLSCTX_INPROC_SERVER,
                   IID_IBaseFilter,
                   pGrabberF);
  // Добавляем фильтр в граф
  GraphBuilder.AddFilter(pGrabberF, 'GRABBER');
  pGrabberF.QueryInterface(IID_ISampleGrabber, pGrabber);
  if pGrabber <> NIL then begin
    ZeroMemory(@mt, sizeof(AM_MEDIA_TYPE));
    with mt do
    begin
      majortype  := MEDIATYPE_Video;
      subtype    := MEDIASUBTYPE_RGB24;
      formattype := FORMAT_VideoInfo;
    end;
    pGrabber.SetMediaType(mt);
    pGrabber.SetBufferSamples(TRUE);
    pGrabber.SetOneShot(FALSE);
  end;
  //---
  //--- VMR
  if failed(FVideoRender.QueryInterface(IID_IVMRFilterConfig,
            pConfig)) then exit;
  pConfig.SetNumberOfStreams(1);
  if failed(FVideoRender.QueryInterface(IID_IVMRMixerBitmap,
            pBitmap)) then exit;
  FCaptureGraphBuilder.SetFiltergraph(GraphBuilder);

  //начинаем строить граф
  with FCaptureGraphBuilder do begin
      RenderStream(@PIN_CATEGORY_PREVIEW, @MEDIATYPE_Video,
                     VideoCaptureFilter, NIL, FVideoRender);
      //RenderStream(@PIN_CATEGORY_PREVIEW, @MEDIATYPE_Video,
       //              VideoCaptureFilter, NIL, pGrabber);
   //строим участок графа от фильтра FASFReader до фильтра FTee_Video
   //RenderStream(nil,  @MEDIATYPE_Video, FASFReader, nil, FTee_Video);
   // строим участок графа от фильтра FASFReader до фильтра FTee_ Audio
   //RenderStream(nil, @MEDIATYPE_Audio, FASFReader, nil, FTee_Audio);
   // строим участок графа от фильтра FTee_ Audio  до фильтра FAudioRender
   RenderStream(nil, @MEDIATYPE_Audio, FTee_Audio, nil, FAudioRender);
   // строим участок графа от фильтра FTee_Video  до фильтра F VideoRender
   RenderStream(nil, @MEDIATYPE_Video, FTee_Video, nil, FVideoRender);

  end;
  // запись
  //проверяем флаг записи
  if en_wdm then begin
   //Создаем файл для записи данных из графа в виде даты со временем
   DateTimeToString(FileName,'hh.mm.ss_dd.mm.yyyy',now);
   FileName:= FileName+'.avi';
   //задаем выходной файл для записи и добавляем в граф фильтр мультиплексора
   FCaptureGraphBuilder.SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(StringToOleStr(AnsiString(FileName))), FMux, FSink);
   //строим граф фильтров для захвата изображения от фильтра FTee_ Video до фильтра FVideoRender
   FCaptureGraphBuilder.RenderStream(nil, @MEDIATYPE_Video,  FTee_Video, Nil, FMux);
   //строим граф фильтров для захвата звука от фильтра FTee_ Audio  до фильтра FAudioRender
   FCaptureGraphBuilder.RenderStream(nil, @MEDIATYPE_Audio, FTee_Audio, Nil, FMux);

   pConfigMux:= NIL;
   //получаем интерфейс управления мультиплексором
   if FAILED(FMux.QueryInterface(IID_IConfigAviMux, pConfigMux)) then begin
    // При захвате видео со звуком устанавливаем звуковой поток в
    // качестве основного для синхронизации с другими потоками в файле
    pConfigMux.SetMasterStream(1);
    pConfigMux := NIL;
   end;
  end;
  //получаем интерфейс ImediaControl
  if failed(GraphBuilder.QueryInterface(IID_IMediaControl,
            MediaControl))then exit;
  //Get the IMediaEventEx Interface
  if failed(GraphBuilder.QueryInterface(IID_IMediaEventEx,
            MediaEventEx)) then exit;
  //Get Audio and Video Interfaces
  if failed(GraphBuilder.QueryInterface(IID_IBasicAudio,
            BasicAudio))then exit;
  if failed(GraphBuilder.QueryInterface(IID_IBasicVideo,
            BasicVideo))then exit;
  if failed(GraphBuilder.QueryInterface(IID_IVideoWindow,
            VideoWindow))then exit;
  //Get file info
  VideoAvail := Assigned(BasicVideo) and Assigned(VideoWindow) and
                (not failed(VideoWindow.get_Visible(_vis)));
  //Get video info
  if VideoAvail then begin
   BasicVideo.GetVideoSize(VideoWidth, VideoHeight);
   BasicVideo.get_BitRate(VideoBitRate);
   BasicVideo.get_AvgTimePerFrame(_tpf);
   if _tpf <> 0 then VideoFPS:= 1 / _tpf else VideoFPS:= 0;
  end;
  //располагаем окошко с видео на панель
  VideoWindow.put_WindowStyle(WS_CHILD or WS_CLIPSIBLINGS);
  VideoWindow.put_Owner(Panel1.Handle);
  VideoWindow.put_MessageDrain(Panel1.Handle);
  VideoWindow.SetWindowPosition(0,
                                0,
                                panel1.ClientRect.Right,
                                panel1.ClientRect.Bottom);
  VideoWindow.put_Visible(TRUE);
  MediaControl.Run(); //Запускаем отображение предпросмотра с вебкамер
  _setvol(gl_vol);
end;
Отрисовываем на текстуре сграбленное:
var i, j: integer;
    ps : TPaintStruct;
begin
 if pGrabber = nil then EXIT;
  GetBitmap(b);
  tmpb.Canvas.StretchDraw(Rect(0, 0, tmpb.Width, tmpb.Height), b);
  for i:= 0 to tmpb.Width - 1 do
         for j := 0 to tmpb.Height - 1 do begin
             ArrayImage [i, j, 0] := GetRValue(tmpb.Canvas.Pixels[i,j]);
             ArrayImage [i, j, 1] := GetGValue(tmpb.Canvas.Pixels[i,j]);
             ArrayImage [i, j, 2] := GetBValue(tmpb.Canvas.Pixels[i,j]);
         end;
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
                  TexWidth, TexHeight,
                  0, GL_RGB, GL_UNSIGNED_BYTE, @ArrayImage);
 Angle := Angle + 2.5;
  if Angle >= 360.0 then Angle := 0.0;
  BeginPaint(form1.panel3.Handle, ps);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT );
  drawGradientBackground;
  glPushMatrix;
    glRotatef(Angle, 0.0, 1.0, 0.0);
    glCallList(CUBE);
  glPopMatrix;
  glFlush;
  SwapBuffers(DC);
  EndPaint(form1.panel3.Handle, ps);
end;
p.s.: под Android юзать OpenGL ES2 разумеется.

Комментариев нет:

Отправить комментарий

В комментариях уважайте собеседника, внимательно читайте посты и не додумывайте. Выход новых версий ПО, внешняя ссылка, переставшая работать с течением времени и т.п. не является основанием для претензий. Желающие спокойно подискутировать и высказаться — Welcome. Желающие спонсировать блог — Donate. Нарушение этих простых правил ведет к бану и удалению комментариев без предупреждения.