Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

DelphiでAndroidのカメラ映像をアプリケーションテザリングでWindowsアプリに表示する



0.作成するもの

同じLANに接続されている(同じWifiアクセスポイントに接続している)android端末とwindows端末で、 android端末のカメラで撮影した映像を1秒に1回windows端末にjpg形式で画像を転送して、 windows端末に表示します。


1.プロジェクトの作成とファイルの保存

ファイル⇒新規作成⇒マルチデバイス アプリケーション -Delphi
をクリックします。
「空のアプリケーション」を選択してOKを押します。
「すべて保存」ボタンを押して、
ドキュメント\Embarcadero\Studio\Projects\AppTether\windows
フォルダにプロジェクト(Project1.dproj)とユニット(Unit1.pas)をデフォルトのまま保存します。

右ペイン(Project1.dproj - プロジェクト)の「ProjectGroup1」を右クリックし、 「新規プロジェクトを追加」を左クリックします。


「マルチデバイス アプリケーション」を選択してOKを左クリックします。


「空のアプリケーション」を選択してOKを左クリックします。


「すべて保存」ボタンを押して、
ドキュメント\Embarcadero\Studio\Projects\AppTether\android
フォルダにプロジェクト(Project2.dproj)とユニット(Unit2.pas)をデフォルトのまま保存します。
プロジェクトグループ「ProjectGroup1.groupproj」ファイルは
ドキュメント\Embarcadero\Studio\Projects\AppTether
フォルダに保存します。

右上ペインのProject2のターゲットプラットフォームを展開して「Android」をダブルクリックして切り替えます。


フォルダ構成
・・・\Projects\AppTether
    ProjectGroup1.groupproj
・・・\Projects\AppTether\windows
    Project1.dproj
    Unit1.pas
・・・\Projects\AppTether\android
    Project2.dproj
    Unit2.pas

2.画面の作成

Unit1(Windowsアプリの画面)に
TButton、TEdit、TImage、TTetheringManager、TTetheringAppProfile、TTimerをドラッグ&ドロップします。



Unit2(Androidアプリの画面)に
TButton、TEdit、TTetheringManager、TTetheringAppProfile、TTimer、TCameraComponentをドラッグ&ドロップします。



3.プロパティの設定

Unit1(Windowsアプリの画面)のTetheringAppProfile1.ManagerプロパティをTetheringManager1に設定します。
Unit1(Windowsアプリの画面)のTetheringAppProfile1.Groupプロパティを MyGroup に設定します。
Unit1(Windowsアプリの画面)のTetheringAppProfile1.Resourcesプロパティの[...]ボタンをクリックします。
新規追加ボタンをクリックすると「リソース0」が追加され、プロパティを以下に設定します。
Kind:Mirror  Name:img  ResType:Stream



Unit2(Androidアプリの画面)のTetheringAppProfile1.ManagerプロパティをTetheringManager1に設定します。
Unit2(Androidアプリの画面)のTetheringAppProfile1.Groupプロパティを MyGroup に設定します。
Unit2(Androidアプリの画面)のTetheringAppProfile1.Resourcesプロパティの[...]ボタンをクリックします。
新規追加ボタンをクリックすると「リソース0」が追加され、プロパティを以下に設定します。
Kind:Shared  Name:img  ResType:Stream



4.プロジェクトオプションの設定

右上ペインの「libProject2.so」をダブルクリックしてプロジェクトを切り替えます。
プロジェクト⇒オプション をクリックします。
左ペインの アプリケーション⇒使用する権限 をクリックします。
ターゲットを、「すべての構成 - Androidプラットフォーム」に切り替えます。
カメラの使用にチェックをいれて「保存」をクリックします。


5.Unit1(Windowsアプリ)ソースコードの記述

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IPPeerClient,
  IPPeerServer, FMX.StdCtrls, FMX.Controls.Presentation, FMX.Edit, FMX.Objects,
  System.Tether.Manager, System.Tether.AppProfile;

type
  TForm1 = class(TForm)
    TetheringManager1: TTetheringManager;
    TetheringAppProfile1: TTetheringAppProfile;
    Image1: TImage;
    Timer1: TTimer;
    Edit1: TEdit;
    Button1: TButton;
    procedure TetheringAppProfile1Resources0ResourceReceived(
      const Sender: TObject; const AResource: TRemoteResource);
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure TetheringAppProfile1AcceptResource(const Sender: TObject;
      const AProfileId: string; const AResource: TCustomRemoteItem;
      var AcceptResource: Boolean);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  TetheringManager1.AutoConnect();
end;

procedure TForm1.TetheringAppProfile1AcceptResource(const Sender: TObject;
  const AProfileId: string; const AResource: TCustomRemoteItem;
  var AcceptResource: Boolean);
begin
    AcceptResource:=True;
end;

procedure TForm1.TetheringAppProfile1Resources0ResourceReceived(
  const Sender: TObject; const AResource: TRemoteResource);
begin
  TThread.Synchronize(nil,procedure
  begin
    Image1.Bitmap.LoadFromStream(AResource.Value.AsStream);
  end);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if TetheringManager1.RemoteProfiles.Count>0 then
    Edit1.Text:='接続中'
  else
    Edit1.Text:='未接続';
end;

end.

6.Unit2(Androidアプリ)ソースコードの記述

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IPPeerClient,
  IPPeerServer, FMX.Edit, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Media,
  System.Tether.Manager, System.Tether.AppProfile,
  FMX.platform, FMX.surfaces, System.Permissions ;

type
  TForm2 = class(TForm)
    TetheringManager1: TTetheringManager;
    TetheringAppProfile1: TTetheringAppProfile;
    Timer1: TTimer;
    CameraComponent1: TCameraComponent;
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CameraComponent1SampleBufferReady(Sender: TObject;
      const ATime: TMediaTime);
  private
    { private 宣言 }
    img_flag:boolean;
    Fstrm:TMemoryStream;

    function AppEvent(iAppEvent:TApplicationEvent;iContext:TObject):Boolean;
    procedure PermissionRequestResult(
      Sender: TObject; const APermissions: TArray;
      const AGrantResults: TArray);
    procedure RequestPermissions();
  public
    { public 宣言 }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

uses
  Androidapi.Helpers, Androidapi.JNI.Os,
  //Androidapi.JNI.JavaTypes,
  Androidapi.Jni.Support, FMX.DialogService ;

function TForm2.AppEvent(iAppEvent: TApplicationEvent;
  iContext: TObject): Boolean;
begin
  Result:=False;
  case iAppEvent of
    TApplicationEvent.BecameActive:
    begin
      //focus取得時
      CameraComponent1.Active:=true;
      Sleep(100);
      CameraComponent1.Active:=False;
      Sleep(400);
      //オートフォーカスモードに設定する
      CameraComponent1.FocusMode:=TFocusMode.ContinuousAutoFocus;
      CameraComponent1.Active:=True;
    end;
    TApplicationEvent.WillBecomeInactive:
    begin
      //focus喪失時
      CameraComponent1.Active:=False;
    end;
  end;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TetheringManager1.AutoConnect();
end;

procedure TForm2.CameraComponent1SampleBufferReady(Sender: TObject;
  const ATime: TMediaTime);
var bmp  :TBitmap;
    bmps :TBitmapSurface;
    pm   :TBitmapCodecSaveParams;
begin
  if img_flag then
    TThread.Synchronize(
      TThread.CurrentThread,
      procedure
      begin
        bmp:=TBitmap.Create;
        bmps := TBitmapSurface.Create;

        CameraComponent1.SampleBufferToBitmap(bmp,true);
        bmps.Assign(bmp);

        if Fstrm<>nil then fstrm.DisposeOf;

        Fstrm:=TMemoryStream.Create;

        //品質80で保存(0-100)
        pm.Quality:=80;
        TBitmapCodecManager.SaveToStream(fstrm,bmps,'.jpg',@pm);

        fstrm.Position:=0;
        TetheringAppProfile1.Resources.FindByName('img').Value:=fstrm;

        img_flag:=false;
        bmp.DisposeOf;
        bmps.DisposeOf;
      end
    );
end;

procedure TForm2.FormCreate(Sender: TObject);
var APPEventService:IFMXApplicationEventService;
begin
  img_flag:=false;
  CameraComponent1.Kind:=TCameraKind.BackCamera;
  if TPlatformServices.Current.SupportsPlatformService(
       IFMXApplicationEventService) then
  begin
      APPEventService:=IFMXApplicationEventService(
        TPlatformServices.Current.GetPlatformService(
          IFMXApplicationEventService)
      );
  end;
  if (APPEventService<>nil) then
    APPEventService.SetApplicationEventHandler(AppEvent);
  RequestPermissions();
end;

procedure TForm2.PermissionRequestResult(Sender: TObject;
  const APermissions: TArray;
  const AGrantResults: TArray);
begin
  //カメラとストレージの権限があるか
  if (AGrantResults[0]<>TPermissionStatus.Granted) then
  begin
    //権限がない場合
    //「□今後は表示しない」チェックボックスにチェックが入っているか
    if (TJActivityCompat.JavaClass.shouldShowRequestPermissionRationale(
         TAndroidHelper.Activity,TJManifest_permission.JavaClass.CAMERA)) then
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っていない場合
      // 非同期でダイアログを表示して説明と許可を要求
      TDialogService.MessageDialog(
        '許可しないとアプリが動作しません。',TMsgDlgType.mtInformation,
        [TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOk,0,
        procedure (const AResult: TModalResult)
        begin
          //2回目以降は「□今後は表示しない」チェックボックスが表示される
          RequestPermissions();
        end);
    end
    else
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っている
      TDialogService.MessageDialog(
        '権限が無いため終了します。「設定⇒アプリと通知」から権限を設定してください。',
        TMsgDlgType.mtError,[TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOK,0,
        procedure(const AResult:TModalResult)
        begin
          Application.Terminate;
        end
      );
    end;
  end;
end;

procedure TForm2.RequestPermissions;
var FPmsCamera: string;
begin
  FPmsCamera :=
    JStringToString(TJManifest_permission.JavaClass.CAMERA);
  PermissionsService.RequestPermissions(
    [FPmsCamera],PermissionRequestResult);
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
  if TetheringManager1.RemoteProfiles.Count>0 then
  begin
    Edit1.Text:='接続中';
    img_flag:=true;
  end
  else
  begin
    Edit1.Text:='未接続';
    img_flag:=false;
  end;
end;

end.

7.完成したアプリケーションを起動する

(1)Windowsアプリの実行

右上ペインのProject1.exeをダブルクリックします。
実行⇒実行 又はツールバーの「右三角」ボタンを押して実行します。

(2)Androidアプリの実行

右上ペインのlibProject2.soをダブルクリックします。
実行⇒実行 又はツールバーの「右三角」ボタンを押して実行します。
以下ダイアログが表示されたら「許可」をタップします。

(3)Windowsアプリの操作

起動したらButton1をクリックします。以下ダイアログが表示されたら「アクセスを許可する」をクリックします。

(4)Androidアプリの操作

Button1をクリックします。

(5)WindowsアプリとAndroidアプリがアプリケーションテザリングでネットワーク接続され、Androidアプリのカメラ映像がWindowsアプリに送信され表示される






Copyright 2019 Mam