トップへ(mam-mam.net/)

AndroidのMediaProjectionを使ってスクリーンショットを撮る ~Delphiでお手軽プログラミング

検索:

AndroidのMediaProjectionを使ってスクリーンショットを撮る ~Delphiでお手軽プログラミング

MediaProjectionのAndroid用ソースコード

Button1を押すと画面のスクリーンショットをImage1に表示します。
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.StdCtrls, System.Messaging, FMX.Controls.Presentation,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Media,
  Androidapi.JNIBridge, FMX.Objects, Androidapi.JNI.Hardware;

type
  TOnImageAvailable = procedure (reader: JImageReader) of object;

  TImageAvailableListener=class(TJavaLocal,JImageReader_OnImageAvailableListener)
    private
      FOnImageAvailable: TOnImageAvailable;
    public
      procedure OnImageAvailable(reader:JImageReader);cdecl;
      property OnImgAvailable: TOnImageAvailable
        read FOnImageAvailable write FOnImageAvailable;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
  private
    { private 宣言 }
    MPManager: JMediaProjectionManager;
    FMessageSubscriptionID:integer;
    FVd:JVirtualDisplay;
    ImageAvailableListener:TImageAvailableListener;
    procedure HandleActivityMessage(const Sender: TObject; const M: TMessage);
    function OnActivityResult(RequestCode, ResultCode: Integer;
      Itt: JIntent ): Boolean;
    procedure OnImageAvailable(reader: JImageReader);
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;
const
  REQUEST_CODE_SCREEN_CAPTURE:integer=100;//任意の整数

implementation

{$R *.fmx}
Uses
  Androidapi.Jni.JavaTypes, Androidapi.Helpers, Androidapi.JNI.os,
  Androidapi.JNI.App, FMX.Helpers.Android, FMX.Surfaces, Androidapi.JNI.util ;

procedure TForm1.Button1Click(Sender: TObject);
var MPObj:JObject;
begin
  FMessageSubscriptionID :=
    TMessageManager.DefaultManager.SubscribeToMessage(
      TMessageResultNotification, HandleActivityMessage
    );
  MPObj:=TAndroidHelper.Activity.getSystemService(
    TJContext.JavaClass.MEDIA_PROJECTION_SERVICE
  );
  MPManager:=TJMediaProjectionManager.Wrap(
    (MPObj as ILocalObject).GetObjectID
  );
  TAndroidHelper.Activity.startActivityForResult(
    MPManager.createScreenCaptureIntent(),
    REQUEST_CODE_SCREEN_CAPTURE
  );
end;

procedure TForm1.HandleActivityMessage(const Sender: TObject;
  const M: TMessage);
begin
  if M is TMessageResultNotification then
    OnActivityResult(
      TMessageResultNotification(M).RequestCode,
      TMessageResultNotification(M).ResultCode,
      TMessageResultNotification(M).Value
    );
end;

function TForm1.OnActivityResult( RequestCode, ResultCode: Integer; Itt: JIntent ): Boolean;
var MProjection: JMediaProjection;
    ImgReader: JImageReader;
    handler: JHandler;
    DisplayMetrics: JDisplayMetrics;
    Dencity: integer;
begin
  result := false;
  if RequestCode=REQUEST_CODE_SCREEN_CAPTURE then
  begin
    if ResultCode = TJActivity.JavaClass.RESULT_OK  then
    begin
      MProjection:=MpManager.getMediaProjection(ResultCode,Itt);
      if MProjection <> nil then
      begin
        //RGBA_8888を使用する必要がある
        ImgReader:=TJImageReader.JavaClass.newInstance(
          screen.Width, screen.Height,
          TJPixelFormat.JavaClass.RGBA_8888 ,2);

        handler:=TJHandler.JavaClass.init();

        DisplayMetrics := TAndroidHelper.DisplayMetrics();
        TAndroidHelper.Display.getMetrics(DisplayMetrics);
        Dencity:=DisplayMetrics.densityDpi;

        FVd:=MProjection.createVirtualDisplay(
          StringToJString('ScreenCaptureTest'),
          screen.Width, screen.Height, Dencity,
          TJDisplayManager.JavaClass.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
          or
          TJDisplayManager.JavaClass.VIRTUAL_DISPLAY_FLAG_PUBLIC,
          ImgReader.getSurface(), nil, handler
        );
        ImageAvailableListener:=TImageAvailableListener.Create;
        ImageAvailableListener.OnImgAvailable:=OnImageAvailable;
        ImgReader.setOnImageAvailableListener(
          ImageAvailableListener, handler
        );
      end;
      result := true;
    end
    else
    begin
      showmessage('キャンセルされた');
    end;
  end;
end;

procedure TForm1.OnImageAvailable(reader: JImageReader);
var Img: JImage;
    JBuf: JByteBuffer;
    JBmp: JBitmap;
    JBmpSfc: TBitmapSurface;
    JImgPlane:JImage_Plane;
    PixelStride,RowStride,RowPadding:Integer;
begin
  Img := reader.acquireLatestImage;
  if Img <> nil then
  begin
    JImgPlane:=Img.getPlanes[0];
    JBuf:=JImgPlane.getBuffer();
    if JBuf <> nil then
    begin
      PixelStride:=JImgPlane.getPixelStride;
      RowStride:=JImgPlane.getRowStride;
      RowPadding:=RowStride-PixelStride*Screen.Width;

      JBmp:=TJBitmap.JavaClass.createBitmap(
        Screen.Width+RowPadding div PixelStride,
        Screen.Height, TJBitmap_Config.JavaClass.ARGB_8888
      );
      JBmp.copyPixelsFromBuffer(JBuf);
      if JBmp <> nil then
      begin
        JBmpSfc := TBitmapSurface.Create;
        JBitmapToSurface(JBmp, JBmpSfc);
        Image1.Bitmap.Assign(JBmpSfc);
        JBmpSfc.Free;
      end;
    end;
    Img.close;
  end;
end;


{ TImageAvailableListener }

procedure TImageAvailableListener.OnImageAvailable(reader: JImageReader);
begin
  if Assigned(FonImageAvailable) then
  begin
    FOnImageAvailable(reader);
  end;
end;

end.