jamesathey / FastAndroidCamera

Camera preview callbacks with less overhead on Xamarin.Android.
Apache License 2.0
60 stars 16 forks source link

Out Of Memory Exception #8

Open Vandersteen opened 7 years ago

Vandersteen commented 7 years ago

Using xamarin forms, I've implemented a custom renderer to show a camera preview in a tabbed page (snapchat like). When moving away from the page and back (a few times) I get a out of memory exception.

When the page is appearing the following gets called:

       try
        {
            Console.WriteLine("OPENING");
            var watch = System.Diagnostics.Stopwatch.StartNew();
            camera = Camera.Open(0);
            camera.SetDisplayOrientation(90);

            var parameters = camera.GetParameters();
            var bitsPerPixel = Android.Graphics.ImageFormat.GetBitsPerPixel(parameters.PreviewFormat);

            int bufferSize = (parameters.PreviewSize.Width * parameters.PreviewSize.Height * bitsPerPixel) / 8;
            for (uint i = 0; i < 4; ++i)
            {
                using (FastJavaByteArray buffer = new FastJavaByteArray(bufferSize))
                {
                    // allocate new Java byte arrays for Android to use for preview frames
                    camera.AddCallbackBuffer(new FastJavaByteArray(bufferSize));
                }
                // The using block automatically calls Dispose() on the buffer, which is safe
                // because it does not automaticaly destroy the Java byte array. It only releases
                // our JNI reference to that array; the Android Camera (in Java land) still
                // has its own reference to the array.
            }

            watch.Stop();
            System.Diagnostics.Debug.WriteLine("Opening camera: {0}ms", watch.ElapsedMilliseconds);
        }

When the page dissappears:

          if(camera != null) {
                camera.SetPreviewDisplay(null);
        camera.StopPreview();
                camera.SetNonMarshalingPreviewCallback(null);
                camera.Release();
                camera.Dispose();
                camera = null;
            }

The previewCallback:

    public void OnPreviewFrame(IntPtr data, Camera camera)
    {
        using(FastJavaByteArray buff = new FastJavaByteArray(data))
        {
            camera.AddCallbackBuffer(buff);
        }
     }

Logs:

[art] Starting a blocking GC Alloc
[art] Starting a blocking GC Alloc
[art] Alloc sticky concurrent mark sweep GC freed 245(67KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 536us total 5.632ms
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc partial concurrent mark sweep GC freed 211(12KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 553us total 12.668ms
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc concurrent mark sweep GC freed 80(17KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 583us total 18.989ms
[art] Forcing collection of SoftReferences for 1012KB allocation
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc concurrent mark sweep GC freed 11(344B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 606us total 17.917ms
[art] Throwing OutOfMemoryError "Failed to allocate a 1036812 byte allocation with 573032 free bytes and 559KB until OOM"
[art] Starting a blocking GC Alloc
[art] Starting a blocking GC Alloc
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 605us total 12.669ms
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 565us total 20.010ms
[art] Forcing collection of SoftReferences for 1012KB allocation
[art] Starting a blocking GC Alloc
[art] Clamp target GC heap from 103MB to 96MB
[art] Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 596us total 18.817ms
[art] Starting a blocking GC HomogeneousSpaceCompact
[art] Clamp target GC heap from 103MB to 96MB
[art] HomogeneousSpaceCompact marksweep + semispace GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 35.955ms total 35.955ms
[art] Throwing OutOfMemoryError "Failed to allocate a 1036812 byte allocation with 565112 free bytes and 547KB until OOM"
Thread finished: <Thread Pool> #14
Thread started: <Thread Pool> #15
[art] art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception java.lang.OutOfMemoryError: Failed to allocate a 1036812 byte allocation with 565112 free bytes and 547KB until OOM
[art] art/runtime/java_vm_ext.cc:410]   at void md5270abb39e60627f0f200893b490a1ade.FragmentContainer.n_setUserVisibleHint(boolean) (FragmentContainer.java:-2)
[art] art/runtime/java_vm_ext.cc:410]   at void md5270abb39e60627f0f200893b490a1ade.FragmentContainer.setUserVisibleHint(boolean) (FragmentContainer.java:43)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.v4.app.FragmentPagerAdapter.setPrimaryItem(android.view.ViewGroup, int, java.lang.Object) (FragmentPagerAdapter.java:134)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.v4.view.ViewPager.populate(int) (ViewPager.java:1266)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.v4.view.ViewPager.setCurrentItemInternal(int, boolean, boolean, int) (ViewPager.java:668)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.v4.view.ViewPager.setCurrentItemInternal(int, boolean, boolean) (ViewPager.java:630)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.v4.view.ViewPager.setCurrentItem(int, boolean) (ViewPager.java:622)
[art] art/runtime/java_vm_ext.cc:410]   at void md5270abb39e60627f0f200893b490a1ade.TabbedPageRenderer.n_onTabSelected(android.support.design.widget.TabLayout$Tab) (TabbedPageRenderer.java:-2)
[art] art/runtime/java_vm_ext.cc:410]   at void md5270abb39e60627f0f200893b490a1ade.TabbedPageRenderer.onTabSelected(android.support.design.widget.TabLayout$Tab) (TabbedPageRenderer.java:87)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.design.widget.TabLayout.dispatchTabSelected(android.support.design.widget.TabLayout$Tab) (TabLayout.java:1164)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.design.widget.TabLayout.selectTab(android.support.design.widget.TabLayout$Tab, boolean) (TabLayout.java:1157)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.design.widget.TabLayout.selectTab(android.support.design.widget.TabLayout$Tab) (TabLayout.java:1127)
[art] art/runtime/java_vm_ext.cc:410]   at void android.support.design.widget.TabLayout$Tab.select() (TabLayout.java:1426)
[art] art/runtime/java_vm_ext.cc:410]   at boolean android.support.design.widget.TabLayout$TabView.performClick() (TabLayout.java:1536)
[art] art/runtime/java_vm_ext.cc:410]   at void android.view.View$PerformClick.run() (View.java:21168)
[art] art/runtime/java_vm_ext.cc:410]   at void android.os.Handler.handleCallback(android.os.Message) (Handler.java:746)
[art] art/runtime/java_vm_ext.cc:410]   at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:95)
[art] art/runtime/java_vm_ext.cc:410]   at void android.os.Looper.loop() (Looper.java:148)
[art] art/runtime/java_vm_ext.cc:410]   at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:5443)
[art] art/runtime/java_vm_ext.cc:410]   at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
[art] art/runtime/java_vm_ext.cc:410]   at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:728)
[art] art/runtime/java_vm_ext.cc:410]   at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:618)

Am I missing something ?

Xamarin.Forms: 2.3.4

InsomniumBR commented 5 years ago

@Vandersteen probably this object is being created twice and not being disposed. This was pointed out on another issue here.

using (FastJavaByteArray buffer = new FastJavaByteArray(bufferSize))
{
    // allocate new Java byte arrays for Android to use for preview frames
    camera.AddCallbackBuffer(new FastJavaByteArray(bufferSize));
}

Try this:

using (FastJavaByteArray buffer = new FastJavaByteArray(bufferSize))
{
    // allocate new Java byte arrays for Android to use for preview frames
    camera.AddCallbackBuffer(buffer);
}