Einzelnen Beitrag anzeigen

Peter666

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

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