2

FastJpeg ライブラリ (jpegdec.pas) を使用して、JPEG フレームを Graphics.TBitmap オブジェクトにデコードしています。デコードは正常に機能し、TBitmap.SaveToFile() メソッドを使用して目視検査用にビットマップをファイルに出力すると、見栄えがよくなります。次に、TBitmap ハンドルを使用して GetObject() を呼び出し、TDibSection オブジェクトを取得します。返された TDibSection オブジェクトは、トップ レベル フィールド (bmWidth、bmHeight など) の正しい値を示しますが、bmBit は NIL であり、SaveToFile() 呼び出しが画像をディスクに正しく書き出すので驚くべきことがわかりました。私が抱えている問題は、TBitmapHeaderInfo フィールド (dsBmih) がすべてゼロであることです。また、問題があれば、dsBitFields、dshSection、および dsOffset フィールドもすべてゼロになります。プライマリ フィールドが埋められ、それ以降のすべてが除外されたかのようです。ここ'

dsBm: (0, 320, 240, 1280, 1, 32, nil)
dsBmih: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
dsBitfields: (0, 0, 0)
dshSection: 0
dsOffset: 0

以下のコードは、基本的に私が何をしているかを示しています。空白の TBitmapHeaderInfo フィールドが返されるのはなぜですか? これが AVI dll の呼び出しに問題を引き起こしているので、これを修正する必要があります。

これがコードスニペットです

var
    theBitmap: Graphics.TBitmap;
    aryBytes: TDynamicByteArray;
    dibs: TDibSection;
begin
    aryBytes := nil;

    // The following function loads JPEG frame #0 from a collection of JPEG frames.
    aryBytes := LoadJpegFrame(0);

    // Decode the first JPEG frame so we can pass it to the compressor
    //  selector call.
    theBitmap := JpegDecode(@aryBytes[0], Length(aryBytes));

    if GetObject(theBitmap.Handle, sizeof(dibs), @dibs) = 0 then
        raise Exception.Create('Get Object failed getting the TDibSection information for the bitmap.');

    // ... The TBitmapHeaderInfo field in dibs is empty as described in the post.
end;

更新: TLama のコメントに応えて、以下に示すようにコードを更新しました。それは今動作します。いくつか質問があります:

1) コードを合理化できますか? これは明らかに上記の元のコードよりもはるかに複雑で、実行する手順が多すぎる可能性があります。

2) すべてのメモリを解放し、必要なすべての GDI ハンドルとオブジェクトを解放していますか? 私はメモリリークを望んでいません。

更新されたコードは次のとおりです。

var
    hr: HRESULT;
    bmi: TBitmapInfo;
    pImg: PJpegDecode;
    jpegDecodeErr: TJpegDecodeError;
    hbm: HBITMAP;
    pBits: Pointer;
begin
    hr := 0; pImg := nil; hbm := 0; pBits := nil;

    try
        jpegDecodeErr := JpegDecode(@aryBytes[0], Length(aryBytes), pImg);

        if jpegDecodeErr <> JPEG_SUCCESS then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The bitmap failed to decode with error code: ' + IntToStr(Ord(jpegDecodeErr)));

        if not Assigned(pImg) then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The bitmap decoded from the first JPEG frame in the video file is unassigned: ' + fullVideoInFilename);

        pImg^.ToBMI(bmi);

        theBitmap := pImg.ToBitmap;

        // Now create a DIB section.
        hbm := CreateDIBSection(theBitmap.Handle, bmi, DIB_RGB_COLORS, pBits, 0, 0);

        if hbm = ERROR_INVALID_PARAMETER then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) One of the parameters passed to CreateDIBSection is invalid.');

        if hbm = 0 then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The call to CreateDIBSection failed.');

        // Select the compressor.  This call USED to fail before TLama's
        //  suggestion.  Now it works.
        hr := aviMaker.compression(hbm, nil, true, Self.Handle);

        if hr <> S_OK then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) Error during compressor selector call: ' + FormatAviMessage(hr));
    finally
        if Assigned(pImg) then
        begin
            pImg^.Free;
            pImg := nil;
        end;

        if Assigned(theBitmap) then
            FreeAndNil(theBitmap);

        if hbm > 0 then
            DeleteObject(hbm);
    end; // try (2)
end;
4

1 に答える 1

3

CreateDIBSectionJPEG ファイルからデコードされた最初のフレームの DIB セクションへのハンドルのみが必要なので、 を使用して、関数呼び出しによって割り当てられたメモリのブロックにコピーするだけで十分だと思いますTJpegDecode.pRGB。 DIB セクションの値はすでにあります。

procedure ProbeGetObject(ACanvas: TCanvas; AGDIObject: HGDIOBJ);
var
  Width, Height: Integer;
  DIBSection: TDIBSection;
  BitmapInfo: TBitmapInfo;
begin
  if GetObject(AGDIObject, SizeOf(DIBSection), @DIBSection) <> 0 then
  begin
    FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
    BitmapInfo.bmiHeader := DIBSection.dsBmih;
    // if you comment the following line, the image will be rendered flipped
    BitmapInfo.bmiHeader.biHeight := - BitmapInfo.bmiHeader.biHeight;
    Width := DIBSection.dsBm.bmWidth;
    Height := Abs(DIBSection.dsBm.bmHeight);
    StretchDIBits(ACanvas.Handle, 0, 0, Width, Height, 0, 0, Width, Height,
      DIBSection.dsBm.bmBits, BitmapInfo, DIB_RGB_COLORS, SRCCOPY);
  end;
end;

procedure InitializeCompressor(Buffer: Pointer; BufferLen: Integer);
var
  ScreenDC: HDC;
  DIBHandle: HBITMAP;
  DIBValues: Pointer;
  BufferSize: DWORD;
  BufferHandle: THandle;
  BufferPointer: Pointer;
  JPEGImage: PJpegDecode;
  BitmapInfo: TBitmapInfo;
begin
  if JpegDecode(Buffer, BufferLen, JPEGImage) = JPEG_SUCCESS then
  try
    JPEGImage^.ToBMI(BitmapInfo);
    BufferSize := Abs(BitmapInfo.bmiHeader.biWidth *
      BitmapInfo.bmiHeader.biHeight * 4);
    BufferHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
      PAGE_READWRITE, 0, BufferSize, nil);
    if BufferHandle <> 0 then
    try
      BufferPointer := MapViewOfFile(BufferHandle, FILE_MAP_WRITE, 0, 0, 0);
      if Assigned(BufferPointer) then
      begin
        CopyMemory(BufferPointer, JPEGImage^.pRGB, BufferSize);
        ScreenDC := GetDC(0);
        if ScreenDC <> 0 then
        try
          DIBHandle := CreateDIBSection(ScreenDC, BitmapInfo, DIB_RGB_COLORS,
            DIBValues, BufferHandle, 0);
          if (DIBHandle <> 0) and Assigned(DIBValues) then
          try
            ProbeGetObject(Form1.Canvas, DIBHandle);
            // here try to initialize the compressor, the DIB section should
            // contain values obtained from the JPEG decoder; in the DIBHandle
            // variable is the handle to the DIB section
          finally
            DeleteObject(DIBHandle);
          end;
        finally
          ReleaseDC(0, ScreenDC);
        end;
      end;
    finally
      CloseHandle(BufferHandle);
    end;
  finally
    JPEGImage^.Free;
  end;
end;
于 2012-05-01T21:49:47.037 に答える