B4A Library Bitmaps to Video

This is a library which creates a video from a series of bitmaps.

The library uses the surface input capabilities of Android's mediacodec (https://developer.android.com/reference/android/media/MediaCodec) that were introduced in Android 4.3 (API 18), hence 4.3 is the earliest version of Android that supports this library. A lot of the code that I used came from snippets than can be found here: https://bigflake.com/mediacodec/.

There is very little parameter sanity checking so be careful. For example, setting up a video encoding with a weird resolution can lock up your device, though, for me, the device has always restarted itself after a few seconds. Different devices behave differently. My S7 seems to be very forgiving and will create videos at any resolution provided the width and height are divisible by two. Another of my devices only likes the standard resolutions. Speaking of which: https://developer.android.com/guide/topics/media/media-formats. Stick to the standard resolutions and you shouldn't have any problem.

Likewise with the other setup parameters. Going lower than 3FPS sometimes caused playback issues. I haven't played around with the bitrate and IFrame_Interval settings since I found the commonly quoted values of 2,000,000 bps and 1 IFrame per second worked well for my requirements. (Look at the previously referenced web pages for more info.) Finally for the codec (MIME_Type) parameter, I always use "video/avc" (H.264) though you can play around with the other codecs supported by your device. You can get the codec list by calling the encoderCodecs method.

Multiple videos can be generated simultaneously but remember to send the correct state information to each encoder instance.

BitmapToVideoEncoder

Author: CaptKronos
Version: 1.1
  • BitmapToVideoEncoder
    • Functions:
      • encoderCodecs As String
        Returns a comma separated string of the available encoder codecs together with their supported MIME types.
        It's the MIME type that is passed to the setup method.
        Example:<code>
        Dim codecs As String = aBitmapToVideoEncoder.encoderCodecs</code>
      • endEncoding (theMediacodecParams As com.cyfer.bitmapstovideo.BitmapToVideoEncoder.mediacodecParams)
        Ends the encoding session. Pass the object returned from the call to setup.
        Example:<code>
        aBitmapToVideoEncoder.endEncoding(state)</code>
      • setup (Filename As String, Width As Int, Height As Int, MIME_Type As String, Codec_Name As String, Bitrate As Int, Frame_Rate As Int, IFrame_Interval As Int) As com.cyfer.bitmapstovideo.BitmapToVideoEncoder.mediacodecParams
        Initialises a new encoding session. Returns an object that holds the encoder state. This object must be passed to the write and end methods.
        More than one session can be active simultaneously - make sure to send the correct state object!
        Example:<code>
        Dim aBitmapToVideoEncoder As BitmapToVideoEncoder
        Dim state As JavaObject=aBitmapToVideoEncoder.setup("/sdcard/Movies/test.mp4", 1920, 1080, "video/avc", "OMX.google.h264.encoder", 2000000, 15, 1)</code>
      • writeBitmap (theMediacodecParams As com.cyfer.bitmapstovideo.BitmapToVideoEncoder.mediacodecParams, bitmap As android.graphics.Bitmap) As Boolean
        Sends a bitmap to the encoder. Pass the object returned from the call to setup.
        Example:<code>
        Dim success As Boolean = aBitmapToVideoEncoder.writeBitmap(state, bitmap)</code>
      • writeImageFile (theMediacodecParams As com.cyfer.bitmapstovideo.BitmapToVideoEncoder.mediacodecParams, filename As String) As Boolean
        Sends an image file (e.g. a jpeg) to the encoder. Pass the object returned from the call to setup.
        Example:<code>
        Dim success As Boolean = aBitmapToVideoEncoder.writeImageFile(state, "/sdcard/Pictures/test.jpg")</code>

The use of the library is straightforward:

B4X:
Sub CreateVideo
    Dim state As JavaObject
    Dim aBitmapToVideoEncoder As BitmapToVideoEncoder
    state = aBitmapToVideoEncoder.setup("/sdcard/Movies/test.mp4",  _
                            1920, 1080, "video/avc", "OMX.google.h264.encoder", 2000000, 15, 1)
    Dim bmp As Bitmap=generateVideoFrame(0), ret As Boolean = True
    Dim n As Int = 1
    Do While bmp.IsInitialized And ret=True
        ret=aBitmapToVideoEncoder.writeBitmap(state, bmp)
        bmp=generateVideoFrame(n)
        n=n+1
    Loop
    aBitmapToVideoEncoder.endEncoding(state)
End Sub

'return Null when finished
Sub generateVideoFrame(n As Int) As Bitmap
 
End Sub
 

Attachments

  • BitmapToVideoEncoder_v1.1.zip
    29.7 KB · Views: 533
Last edited:

SoportePatrol

Member
Licensed User
Longtime User
Hello, thank you very much for this great library, I try to use but it gives me the following error:

"java.lang.OutOfMemoryError: Failed to allocate a 1228812 byte allocation with 672768 free bytes and 657KB until OOM"

this is my code:

B4X:
Sub Globals
    Dim imgByte() As Byte
    Private IntervalMs As Int = 150
    Private lastPreviewSaved As Long
End Sub

' Frame Source is stored in imgByte
Sub Camera1_Preview (PreviewPic() As Byte)
    If DateTime.Now > lastPreviewSaved + IntervalMs Then
        imgByte = camEx.PreviewImageToJpeg(PreviewPic, 70)
        lastPreviewSaved = DateTime.Now
    End If
End Sub

Sub CreateVideo
    Dim state As JavaObject
    Dim aBitmapToVideoEncoder As BitmapToVideoEncoder
   
    state = aBitmapToVideoEncoder.setup("/sdcard/Movies/testX.mp4", 640, 480, "video/avc", 2000000, 15, 1)
    Dim bmp As Bitmap=generateVideoFrame(0)
    Dim ret As Boolean = True
    Dim n As Int = 1
    Do While bmp.IsInitialized And ret=True
        ret=aBitmapToVideoEncoder.writeBitmap(state, bmp)
        bmp=generateVideoFrame(n)
        n=n+1
    Loop
    aBitmapToVideoEncoder.endEncoding(state)
End Sub

'the imgByte is converted to stream and then to bitmap
Sub generateVideoFrame(n As Int) As Bitmap
    Dim In As InputStream
    In.InitializeFromBytesArray(imgByte, 0, imgByte.Length)
    Dim bmp As Bitmap
    bmp.Initialize2(In)
    Return bmp
End Sub

'start video recording
Sub Camera1_Ready (Success As Boolean)
    If Success Then
        Starter.csu.CallSubPlus(Me, "CreateVideo", 5000)
    End If
End Sub
 

CaptKronos

Active Member
Licensed User
Longtime User
At what point do you get that error? When calling writeBitmap?

Perhaps try adding

B4X:
SetApplicationAttribute(android:largeHeap,"true")

to the manifest but that is a fairly random suggestion.
 

SoportePatrol

Member
Licensed User
Longtime User
thanks, after editing the manifest and moving the "writeBitmap" function to Camera1_Preview, it worked perfectly!!!
 

CaptKronos

Active Member
Licensed User
Longtime User
Updated the library to v1.1.

Added the parameter Codec_name to Setup. Previously Setup would use the first codec that matched the requested MIME_type. I have since discovered (from personal experience and from Googling) that codecs not provided by Google don't seem to work reliably. I hadn't noticed this before because all my devices had the Google codecs ordered before the non-Google ones and hence by default the Google ones were being used. I recently tested with a Samsung S3 running a CyanogenMod ROM which had its codecs ordered as follows (only showing the video ones):

B4X:
OMX.SEC.MPEG4.Encoder{video/mp4v-es}
OMX.SEC.H263.Encoder{video/3gpp}
OMX.SEC.AVC.Encoder{video/avc}
OMX.google.h263.encoder{video/3gpp}
OMX.google.h264.encoder{video/avc}
OMX.google.mpeg4.encoder{video/mp4v-es}
OMX.google.vp8.encoder{video/x-vnd.on2.vp8}

This meant that calling Setup with a MIME type of (for example) video/avc would have the effect of setting OMX.SEC.AVC.Encoder as the codec. With v1.0 of the library it is not possible to request the Google codec. With version 1.1 of Setup, the request to use the Google codec corresponding to video/avc would be:

B4X:
Setup(outFn, fileRes.x, fileRes.y, "video/avc", "OMX.google.h264.encoder", bitRate, FPS, IFrames)

The codec names, along with the MIME types, can be obtained from calling encoderCodecs().
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hi

Interesting Lib, would it be possible to have a aBitmapToVideoEncoder.writeBitmap2 method that returns the encoded frame as a byte array, and similarly a decode2 that would return the encoded byte array as a decode bmp ?

Regards

John
 

CaptKronos

Active Member
Licensed User
Longtime User
Hi John, not quite sure what you are asking. Do you want writeBitmap2 to return the data after a single frame has been encoded into mp4 (or whichever format has been requested)? I'm not sure that's possible and why would you want to anyway? This library just pumps bitmaps into Android's video encoder engine; it doesn't do the encoding itself. As far as decode2, do you want to read frames, as bitmaps, from a video? If so, that's definitely possible, but you wouldn't use this library to do so.

I have probably misunderstood what you are asking.
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hi John, not quite sure what you are asking. Do you want writeBitmap2 to return the data after a single frame has been encoded into mp4 (or whichever format has been requested)? I'm not sure that's possible and why would you want to anyway? This library just pumps bitmaps into Android's video encoder engine; it doesn't do the encoding itself. As far as decode2, do you want to read frames, as bitmaps, from a video? If so, that's definitely possible, but you wouldn't use this library to do so.

I have probably misunderstood what you are asking.


Thanks for the response, it's ok, its just an idea I had.
 
Top