Android Cameras - a blog

I have spent several weeks trying to get photographs programmatically from an android
camera, with FMX and Delphi.

Here are a couple of my observations from this work, along with a wish list (or was that
whinge list?).

There are two reasonably accessible ways of accessing an android camera:
(1) via CameraComponent
(2) via ActionList / TakePhotoFromCameraAction

Each has its advantages and disadvantages.

First: CameraComponent:

Use:
Insert a tCameraComponent component, and a tImage.
This component does not take a picture, such as by pushing a shutter button. It keeps
transferring the image to your image, and in the Object Inspector you have to set up a
routine to synchronise the threads for this, and a routine to transfer the image, as per:

procedure Form.CameraComponentSampleBufferReady(Sender: TObject; const ATime: TMediaTime);
begin
  TThread.Synchronize(TThread.CurrentThread, DisplayCameraPreviewFrame);
end;

procedure Form.DisplayCameraPreviewFrame;
begin
  CameraComponent1.SampleBufferToBitmap(Image1.Bitmap, True);
end;

Good bits:
This provides access to many of the camera parameters, and is, once you get the hang of it,
not too hard to drive. Unfortunately it is missing a few things. Via the uSettings.pas which
accompanies the demo programme, access can be made to a lot of camera (and torch) options,
and these work well. It is possible to write your own small routines to let you set up photo
type and quality and torch options before executing CameraComponent. You can put the image
where you want it (in a tImage) and write it out to disc. If you want decent resolution
pictures, you will need to set the MaxHeight and MaxWidth parameters to higher than your
camera resolution, or you only get a 70kB poor resolution picture. The torch access is
excellent, allowing you to have your torch on while you are setting up the picture, which
is most valuable (especially on cameras where the torch is either off or a flash only!)

Bad bits:
The documentation needs further work.
The really big lack is that there is no ability to change the camera zoom. This is a major
shortfall. If it could be implemented as GetZoom, SetZoom, GetMaxZoom and GetMinZoom or
something like this, it would be wonderful!
Some of us would like a manual focus option and setting too (note that in the code there are
hooks for focus, but they are null subroutines).

TakePhotoFromCameraAction:

Use:
Insert a tActionList component, and a tImage.
Double-click on it.
At the top left of the box which appears is a button with a down-arrow beside it. Press
on the down arrow. Then insert aTakePhotoFromCameraAction action.
Double-click on this, so you can see it in the Object Inspector.
Here you can set the MaxHeight and MaxWidth parameters, and put in a routine for what to do
after taking the photo. In that routine, copy the image to your image, so you can save it.

Pros:
This, once you get the hang of setting up a tActionList, it is a relatively easy beast to
programme. It takes higher resolution pictures than CameraComponent (after setting the Height
and Width to 8000 it produces .jpg files of over 1Mb). You have access to all the camera
basic options, though you have no way of setting defaults before executing it.

Cons:
Again, the documentation was inadequate for those who have no idea of how to use it. It talks
about the image being available at the end of taking a photo, but you have to chase up the example
to find out enough about it to be able to use it. The image provided (and I suspect that this is
due to actually running the android’s camera programme, and may not be anything Delphi has any
control over) fills the whole screen, sometimes making it quite hard to see the options and the
“OK” and “Retry” buttons, especially in bright sunshine.

With either of them, the MultiResBitmap provided by either of these may have 1 or 2 bitmaps in it.
If there is one, no problems. If there are two, make sure you write the one with a width > 0, as
per the following code:

   //If we use
   //   Image1.Bitmap.SaveToFile(WorkingDirectory + s + '.' + EditExt.Text);
   //   it prangs (zero length file if .jpg extension used) on the Samsung S22 phone.
   m := Image1.MultiResBitmap.Count;
   i := 0;

   if m > 0 then begin
      for i := 0 to m - 1 do
         if round(Image1.MultiResBitmap.Items[i].Bitmap.Width) > 0 then break;
   Image1.MultiResBitmap.Items[i].Bitmap.SaveToFile(PhotoDirectory + s + '.' + EditFileExt.Text);

I hope this is some use to others tearing their hair out trying to take piccies!

1 Like

Thanks for this Chester

I have been watching your efforts as I have plans to do similar things in future.
Greatly appreciate your documentation of your progress.

Thanks for the encouragement, Roger!
I think I have a sample programme which allows the use of either camera technique, and it actually works without strange and baffling Java error messages!
I have tried to fill it with comments to make things a bit clearer - sorry if it is too verbose!
It is available as
http://c443799.r99.cf2.rackcdn.com/CameraCGW.zip

Thanks Chester. I have downloaded your sample and get ‘Undeclared identifiers’ for:
PermissionsService.RequestPermissions(Permissions,
procedure(const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray)
I am running Delphi 10.3 - must I use a higher version of Delphi (e.g.11) or are these types declared in some other unit?
Thanks,
Kim.

G’day, Kim.
That is written for android 11 using Rad/ Delphi 11.1, as it was the fact that the programme for which it was written would not run under android 11. With all the hassles of permissions, I am not surprised that various things don’t work in earlier versions. Sorry! You may have to do some chasing, as the earlier permissions require different handling. I do not know if the camera routines work in earlier versions of Delphi - certainly I had no luck with them in 10.3 or 10.4, but I knew a lot less about using them then.
I would be interested to hear how you get on please.
Chester.

Hi Kim

The definition of the return value was changed in 11 from
TArray<TPermissionStatus> to TClassicPermissionStatusDynArray. You probably just need to revert to the 10.3 definition for the sample to work

{$IFDEF ISD11A_DELPHI}
  PermissionsService.RequestPermissions(APermArray,
  procedure(const APermissions: TClassicStringDynArray;
    const AGrantResults: TClassicPermissionStatusDynArray)
{$ELSE}
  PermissionsService.RequestPermissions(APermArray,
    procedure(const APermissions: TArray<string>;
      const AGrantResults: TArray<TPermissionStatus>)
{$ENDIF}

Full Code

Roger

Thanks Roger and Chester, I have it compiling now albeit with one small tweak as shown in bold below. I will check out the functionality over the next few days. Cheers, Kim.

{$IFDEF ISD11A_DELPHI}
PermissionsService.RequestPermissions(APermArray,
procedure(const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray)
{$ELSE}
PermissionsService.RequestPermissions(Permissions,
procedure(const APermissions: TArray;
const AGrantResults: TArray)
{$ENDIF}