AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

YUV2 unter Firemonkey

Ein Thema von Peter666 · begonnen am 1. Dez 2017 · letzter Beitrag vom 4. Dez 2017
Antwort Antwort
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.079 Beiträge
 
Delphi 10.4 Sydney
 
#1

AW: YUV2 unter Firemonkey

  Alt 3. Dez 2017, 20:53
Hm, irgendwie reden wir aneinander vorbei oder du liest die anderen Beiträge nicht zuende?
Wo genau hakt es denn?

Zeig uns doch mal bitte den Code in Verbindung mit dem OpenGL-Fenster.

Du hast ja im Prinzip schon alles.
Du musst nur nach dem Umwandeln per GPU von YUV in irgendein RGB-Format den Buffer per Map-Methode in das Bitmap kopieren und fertig. Und dann halt das Bitmap auf irgendwas anzeigen, aber das ist eine Fingerübung.
  Mit Zitat antworten Zitat
Peter666

Registriert seit: 11. Aug 2007
357 Beiträge
 
#2

AW: YUV2 unter Firemonkey

  Alt 4. Dez 2017, 12:02
Stimmt, ich denke wir reden etwas aneinander vorbei:

Delphi-Quellcode:
vertices[0] := vr.left;
    vertices[1] := vr.bottom;
    vertices[2] := vr.right;
    vertices[3] := vr.bottom;
    vertices[4] := vr.left;
    vertices[5] := vr.top;
    vertices[6] := vr.right;
    vertices[7] := vr.top;

    texcoords[0] := tr.left;
    texcoords[1] := tr.bottom;
    texcoords[2] := tr.right;
    texcoords[3] := tr.bottom;
    texcoords[4] := tr.left;
    texcoords[5] := tr.top;
    texcoords[6] := tr.right;
    texcoords[7] := tr.top;

    if FProgram = 0 then
      LoadShader;

    glUseProgram(FProgram);
    glUniformMatrix4fv(FuniformMatrix, 1, GLboolean(0), @FModelviewProj);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Texture.Handle);

    glUniform1i(FUniformSamplers[0], 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, Texture.UHandle);
    glUniform1i(FUniformSamplers[1], 1);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, Texture.VHandle);
    glUniform1i(FUniformSamplers[2], 2);

    glVertexAttribPointer(ATTRIBUTE_VERTEX, 2, GL_FLOAT,
{$IFDEF MSWINDOWS}false{$ELSE}0{$ENDIF}, 0, @vertices);
    glEnableVertexAttribArray(ATTRIBUTE_VERTEX);

    glVertexAttribPointer(ATTRIBUTE_TEXCOORD, 2, GL_FLOAT,
{$IFDEF MSWINDOWS}false{$ELSE}0{$ENDIF}, 0, @texcoords);
    glEnableVertexAttribArray(ATTRIBUTE_TEXCOORD);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glUseProgram(0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, 0);
...

function LoadShader: Boolean;
var
  vertShader, fragShader: GLUInt;
  status: GLint;
begin
  result := false;
  if not FInitialized then
    exit;

  if FProgram <> 0 then // Already Loaded
  begin
    result := true;
    exit;
  end;

  vertShader := 0;
  fragShader := 0;

  FProgram := glCreateProgram();
  try
    vertShader := compileShader(GL_VERTEX_SHADER, vertexShaderString);
    if vertShader = 0 then
      exit;

    fragShader := compileShader(GL_FRAGMENT_SHADER, fragmentShaderString);
    if fragShader = 0 then
      exit;

    glAttachShader(FProgram, vertShader);
    glAttachShader(FProgram, fragShader);
    glBindAttribLocation(FProgram, ATTRIBUTE_VERTEX, 'position');
    glBindAttribLocation(FProgram, ATTRIBUTE_TEXCOORD, 'texcoord');

    glLinkProgram(FProgram);

    glGetProgramiv(FProgram, GL_LINK_STATUS, @status);
    if status = 0 then
      exit;
    result := validateProgram(FProgram);
    FuniformMatrix := glGetUniformLocation(FProgram,
      'modelViewProjectionMatrix');

    FUniformSamplers[0] := glGetUniformLocation(FProgram, 's_texture_y');
    FUniformSamplers[1] := glGetUniformLocation(FProgram, 's_texture_u');
    FUniformSamplers[2] := glGetUniformLocation(FProgram, 's_texture_v');
  finally

    glDeleteShader(vertShader);
    glDeleteShader(fragShader);

    if not result then
    begin
      glDeleteProgram(FProgram);
      FProgram := 0;
    end;
  end;
end;
Im Prinzip machst du nichts anderes als das du 3 Texturen für Y, U und die V Werte hast und diese in der GPU mittels eines Shaders direkt im Grafikspeicher in RGB umwandelst. Du kannst das ganze auch so wie du vorschlagen willst machen:

Delphi-Quellcode:
const
  YUV_FIX2 = 6;
  YUV_MASK2 = (256 shl YUV_FIX2) - 1;

function MultHi(const v, coeff: integer): integer; inline;
begin
  result := (v * coeff) shr 8;
end;

function VP8Clip8(const v: integer): Byte; inline;
begin
  if (v and not YUV_MASK2) = 0 then
    result := v shr YUV_FIX2
  else if v < 0 then
    result := 0
  else
    result := 255;
end;

function VP8YUVToR(const y, v: integer): Byte; inline;
begin
  result := VP8Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234);
end;

function VP8YUVToG(const y, u, v: integer): Byte; inline;
begin
  result := VP8Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v,
    13320) + 8708);
end;

function VP8YUVToB(const y, u: integer): Byte; inline;
begin
  result := VP8Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
end;

procedure VP8YuvToRgb(y, u, v: integer; argb: PCardinal); inline;
begin
  argb^ := $FF000000 or VP8YUVToR(y, v) shl 16 or VP8YUVToG(y, u, v) shl 8 or
    VP8YUVToB(y, u);
end;

type
  TPacket = record
    srcY, srcU, srcV: PByte;
    LineSize: Array [0 .. 2] of integer;
  end;

procedure copyYuv(Packet: TPacket; Bitmap: TBitmap); overload;
var
  x, y: integer;
  ptr: PByte;
  dst: PCardinal;
  Data: TBitmapData;
begin
  Bitmap.Map(TMapAccess.Write, Data);
  try
    ptr := Data.Data;
    with Packet do
      for y := 0 to Bitmap.height - 1 do
      begin
        dst := PCardinal(ptr);
        for x := 0 to Bitmap.width - 1 do
        begin
          VP8YuvToRgb(srcY[x], srcU[x shr 1], srcV[x shr 1], dst);
          inc(dst);
        end;
        inc(srcY, LineSize[0]);
        inc(ptr, Data.Pitch);
        if ((y + 1) mod 2) = 0 then
        begin
          inc(srcU, LineSize[1]);
          inc(srcV, LineSize[2]);
        end;
      end;
  finally
    Bitmap.Unmap(Data);
  end;
end;
Wie ich aber schon im Eingangspost erwähnt habe ist das auf mobilen Geräten nicht gangbar. Die Umwandlung bei einem 1080i Bild ist mit 25fps nicht realisierbar.

Die einzige Alternative für mich wäre das mit einem, so wie ich das bei Firemonkey verstanden habe soll das ja gehen, GPU unterstützten TFilter zu machen. Das ganze würde dann zwar auch bedeuten, dass du was in der GPU warst wieder in eine Bitmap schiebst, aber es ist denke ich schneller, als die Umwandlung mittels CPU.
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.079 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: YUV2 unter Firemonkey

  Alt 4. Dez 2017, 13:38
Stimmt, ich denke wir reden etwas aneinander vorbei:

Im Prinzip machst du nichts anderes als das du 3 Texturen für Y, U und die V Werte hast und diese in der GPU mittels eines Shaders direkt im Grafikspeicher in RGB umwandelst. Du kannst das ganze auch so wie du vorschlagen willst machen:
Das ist der Punkt den du immer überliest oder falsch verstehst!
Mein Vorschlag ist NICHT es per CPU zu machen.
Du musst nur deine beiden Codeschnipsel kombinieren:
Delphi-Quellcode:
function ConvertYuvToRgbWithOpenGL(const Packet: TPacket): TBytes;
begin
  // make OpenGL magic here
end;

procedure CopyYuv(const Packet: TPacket; Bitmap: TBitmap); overload;
var
  BitmapData: TBitmapData;
  RGBABuffer: TBytes;
begin
  RGBABuffer := ConvertYUVToRGBWithOpenGL(Packet);

  Bitmap.Map(TMapAccess.Write, BitmapData);
  try
    System.Move(@RGBABuffer[0], BitmapData.Data, Length(RGBABuffer));
  finally
    Bitmap.Unmap(BitmapData);
  end;
end;
Ich bin ja nicht so firm mit OpenGL, aber anhand deiner Codezeilen kann man erkennen, dass du pro Videoframe ja eine Variable Texture (mit .Handle, .UHandle und .YHandle) hast, um die YUV-Daten aufzunehmen.
Du brauchst jetzt nur noch eine weitere Variable für eine 2D-Texture, die deine umgewandelten RGBA-Daten aufnimmt.
Den Inhalt deiner - ich nenne sie mal RGBATexture - musst du nur noch in den Speicher des FMX-Bitmaps umkopieren.
Die Farbraumkonvertierung erfolgt weiterhin mit OpenGL auf der GPU!!!

Beim obigen Pseudocodeschnipsel kann man sich ggf. den Zwischenschritt mit den TBytes-Array sparen, wenn du direkt den TBitmapData.Data Zeiger übergibst.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:20 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz