Jump to content
Sign in to follow this  
Renate Schaaf

Project Bitmaps2Video on GitHub

Recommended Posts

This is my video-project on GitHub:

https://github.com/rmesch/Bitmaps2Video

 

I am presenting it here, because it is useful as it is, but could use some ideas for improvement.

 

Features:

  • A Delphi-class to support encoding of a series of bitmaps and video clips to a video file
  • Requires the ffmpeg-library and is intended as an easy to use interface to this library
  • Versions for Win32/Win64 and a cross-platform version currently supporting Win32/Win64/Android32/Android64
  • Most popular file formats and codecs supported, more codecs contained in FFMpeg can be registered
  • Rudimentary support for adding an audio-stream
  • Demos for both versions, set up to compile and run "out of the box", as library files and support for their deployment to Android are included

 

There are some problem areas though, the most important one in my opinion being threading issues with TBitmap under Android. For more see the readme and the demos.

 

Critique, ideas, bug reports most welcome, maybe someone would even like to contribute, that would be delightful. There have been valuable contributions so far, but there are some areas which could use the input of an expert.

 

Thanks for reading,

Renate

  • Like 4
  • Thanks 1

Share this post


Link to post

Hello,

Thanks for this work, I was looking form something like this. More precisely, I want to make a movie from animated 3D scenes in a TViewport3D in Firemonkey.

So I took your UMultidemo example. I put a Tviewport3D on the form, and in it a TCube, with an associated TFloatanimation making it rotate around an axis for 7 seconds.

With an additional TImagelist on the form, I make a list of bitmaps with

TForm1.FloatAnimation1Process
...
  BM:=Viewport3D1.MakeScreenshot;
  Imagelist1.Add(BM);
  CX:=BM.Size.cx;
  CY:=BM.Size.cy;
...

CX and CY being vars of the unit. Yes, they're rewritten each time with the same value, but I can improve that later.

The Imagelist1.Add is taken from the TImageListHelper here :
https://stackoverflow.com/questions/36013186/how-to-insert-images-to-timagelist-in-firemonkey-programmaticaly
adding FMX.ImgList and FMX.MultiResBitmap to "uses".

And then, I just modified TForm1.MakeSlideshow by replacing
            // 23 sec of movie
            I := 1;
            while I < 4 do
            begin
              if I > 1 then
                bme.CrossFade(am, bm, 1000);
              bme.AddStillImage(bm, 3000);
              bme.CrossFade(bm, am, 1000);
              bme.AddStillImage(am, 3000);
              inc(I);
            end;
            // Hold end frame for 2 seconds
            bme.Freeze(2000);
with
            for j :=0 to Imagelist1.Count-1 do
            begin
             am.Assign(Imagelist1.Bitmap(TSizef.Create(CX,CY),j));
             bme.AddStillImage(am, 17);
            end;

Since the Timer on animations is 60 times per second and 1000/60 = 16.6666

And then, I start the animation in TForm1.Create, excute the program, wait till the animation is over, press the "Make a simple slideshow" without changing the options combos and I get a movie with the rotating cube in the viewport.

 

Exactly what I wanted. ... almost.

 

I'm supposed to have 60 bitmaps per second, and with an animation lasting 7 seconds Imagelist1.Count is 400 instead of 420. No idea why. Other times it's 397...
But the movie lasts 13 seconds. Yes, the frame rate combo was on 30 frames per second, so I changed for 60, tried again... and another movie lasting 13 seconds.

I tried the other way round by leaving 30 frames per seconds the combo and instead writing in the code :
             bme.AddStillImage(am, 8);
The movie still lasted 13 seconds !
Finally, I did both : 8 milliseconds in AddStillImage and 60 frames per second in the combo, and I got a movie lasting 6 seconds.

I suppose there are effects of "round"ing that lead to this uncertainty.

 

Now for the size. I thought it would be even better having a movie with the same size as the viewport.

So I thought I could replace in MakeSlideshow
          Height := MovieHeights[HeightCombo.ItemIndex];
with
          Height := CY;

But this time it fails.

By debugging, I found that in TBitmapEncoderM.Create in UBitmaps2VideoM.pas
it's avcodec_open2(c, codec, @CodecSetup.OptionsDictionary); that fails.

and this time it's a function of a dll of FFMPEG, and for the time being, I don't want to dive deep into the specs...

Is there a relatively easy way (other than making my own DLLs and rewriting FFMEG, and from within Delphi, not by using some "MS DOS command line" tricks "ffmpeg -option1 -option2...") to customize the size of my movie to anything like 123 x 456 or should I stick to 720, 540 or 1080 for the height and the predifined aspect ratios ?

 

And eventually, is there an easy way to write meta data (author, etc.) to the file. I have seen a few things in the comments of FFMPEG.pas, but no syntax guidelines.

 

Share this post


Link to post
30 minutes ago, prodeldpen said:

I suppose there are effects of "round"ing that lead to this uncertainty.

Right. You want to add 1 frame of your animation at a time, but you use bme.addStillImage, which is meant for adding the same image for multiple frames. So it will only work (roughly) correctly if the ShowTime is much larger than the frame time of the movie.

Try to use bme.AddFrame instead.

36 minutes ago, prodeldpen said:

customize the size of my movie to anything like 123 x 456

That just won't work, it's a codec limitation. You have to use at least even numbers, for some codecs the sizes might even have to be multiples of 4. I would stick to multiples of 4 to be on the safe side.

Another thing you might consider is to shorten the chain from animation to movie. To show the animation and make screenshots seems a bit roundabout to me, there must be a shorter way.

45 minutes ago, prodeldpen said:

And eventually, is there an easy way to write meta data (author, etc.) to the file.

There must be, but I haven't yet bothered to look at it 🙂, maybe I will.

  • Like 1

Share this post


Link to post

Thanks for your answer. I will try with AddFrame. I wish I knew how to bypass "makescreenshot", I would even prefer Embarcadero to provide a nice native "makemovie" !

Share this post


Link to post

Just in case you might be interested, I found out how to easily add metadata :

In TBitmapEncoderM.Create in UBitmaps2VideoM.pas
Just between the lines
  Assert(ret >= 0, 'avformat_alloc.. error' + inttostr(ret));
and
  stream := avformat_new_stream(oc, nil);

I only need to add something like
  av_dict_set(@oc.metadata, 'title', 'test metadata', 0);
  av_dict_set(@oc.metadata, 'composer', 'me', 0);
  av_dict_set(@oc.metadata, 'genre', 'programming', 0);

And it appears in Windows explorer with a right click and properties, or with the adequate displayed headers in Windows explorer.

 

For the size of the video, multiples of 4 are fine, it works perfectly with
          Height := (CY div 4) * 4;
          Width := (CX div 4) * 4;
so i have "almost" the size of the viewport.

I

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×