niqdev / ipcam-view

MJPEG video streaming on Android
https://goo.gl/a2HM8C
MIT License
406 stars 160 forks source link

NegativeArraySizeException and Resetting to invalid mark #121

Open smartyw opened 3 years ago

smartyw commented 3 years ago

Hi

I've created a very simple test application (single activity) with defaults throughout and am testing with a UCam247 camera. Unfortunately, when I start the stream I get the following exceptions:

2021-04-13 07:47:16.811 6237-6307/com.bittysoftware.mjpegtest E/MjpegViewDefault: encountered exception during render
    java.io.IOException: Resetting to invalid mark
        at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)
        at java.io.FilterInputStream.reset(FilterInputStream.java:226)
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:74)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:431)
2021-04-13 07:47:16.903 6237-6307/com.blah.mjpegtest E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.blah.mjpegtest, PID: 6237
    java.lang.NegativeArraySizeException: -1
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:67)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:431)

My layout looks like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:stream="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.github.niqdev.mjpeg.MjpegSurfaceView
        android:id="@+id/mjpegview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        stream:type="stream_default" />

</androidx.constraintlayout.widget.ConstraintLayout>

And my Activity:

package com.blah.mjpegtest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.github.niqdev.mjpeg.DisplayMode;
import com.github.niqdev.mjpeg.Mjpeg;
import com.github.niqdev.mjpeg.MjpegSurfaceView;
import com.github.niqdev.mjpeg.MjpegView;

public class MainActivity extends AppCompatActivity {
    private String TAG = "MainActivity";
    private MjpegView mjpegview;
    private int TIMEOUT = 5; //seconds
    private final String url = "http://192.168.0.53/live/0/mjpeg.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mjpegview = ((MjpegSurfaceView) findViewById(R.id.mjpegview));
        startStream();
    }

    private DisplayMode calculateDisplayMode() {
        int orientation = getResources().getConfiguration().orientation;
        return orientation == Configuration.ORIENTATION_LANDSCAPE ?
                DisplayMode.FULLSCREEN : DisplayMode.BEST_FIT;
    }

    private void startStream() {
        Log.d(TAG,"Starting stream");
        // credentials removed for GitHub issue posting
        Mjpeg.newInstance()
                .credential("xxxxxxxx", "xxxxxxxx")
                .open(url, TIMEOUT)
                .subscribe(
                        inputStream -> {
                            mjpegview.setSource(inputStream);
                            mjpegview.setDisplayMode(calculateDisplayMode());
                            mjpegview.flipHorizontal(false);
                            mjpegview.flipVertical(false);
                            mjpegview.setRotate(0);
                            mjpegview.showFps(true);
                        },
                        throwable -> {
                            Log.e(getClass().getSimpleName(), "mjpeg error", throwable);
                            Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
                        });
    }

    @Override
    protected void onResume() {
        super.onResume();
//        startStream();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mjpegview.stopPlayback();
    }
}

I can open the stream from a WebView (but the frame rate is very, very slow) so it would appear that the stream itself is OK.

Thanks in anticipation.

niqdev commented 3 years ago

Hi, I'm just wondering if this is a regression introduced with #83. Are you on the latest version? Would you mind trying out the old one available on bintray and see if it works? Thanks

smartyw commented 3 years ago

Hi @niqdev ,

I'm not familiar with bintray so I just changed the version number in the build.gradle dependencies entry to 1.7.0 without changing the repositories specified. Hope that still makes for a valid test.

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'com.github.niqdev:ipcam-view:1.7.0'
}

Anyway I got what appears to be the same problem:

2021-04-13 12:53:00.515 6672-6717/com.blah.mjpegtest E/MjpegViewDefault: encountered exception during render
    java.io.IOException: Resetting to invalid mark
        at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)
        at java.io.FilterInputStream.reset(FilterInputStream.java:226)
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:79)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:141)
2021-04-13 12:53:00.727 6672-6717/com.bittysoftware.mjpegtest E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.bittysoftware.mjpegtest, PID: 6672
    java.lang.NegativeArraySizeException: -1
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:72)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:141)

I can capture a trace with Wireshark if you think that would be useful. It's not a public camera so it's not possible for you to test against it, I'm afraid.

Cheers

niqdev commented 3 years ago

Eheh... I'm not familiar myself with the code either, what I did was blindly wrap an existing implementation 😅 Have you tried to clone this repo and run the sample app in debug? Do you have the same result? From the error, it looks like the stream you are trying to read is empty or invalid 🤷‍♂️ Try with a public cam and see if you have the same behaviour and compare what's the difference. Feel free to a pr if you think there is an issue and you wanna fix it!

smartyw commented 3 years ago

Ah OK :-)

I think the stream is valid since a) I have another Android app that can handle it no problem (there are other issues unrelated with video streaming though) b) A WebView can render the stream, albeit with a very low frame rate.

Thanks anyway.

niqdev commented 3 years ago

Please let me know if you find a solution to your issue or a better/alternative approach! Thanks

smartyw commented 3 years ago

Will do. Fingers crossed!

smartyw commented 3 years ago

I tested with the sample app and got the same result. Not sure this advances the understanding of the problem exactly but it's worth noting.

021-04-13 18:54:50.997 7558-7594/com.github.niqdev.ipcam E/MjpegViewDefault: encountered exception during render
    java.io.IOException: Resetting to invalid mark
        at java.io.BufferedInputStream.reset(BufferedInputStream.java:450)
        at java.io.FilterInputStream.reset(FilterInputStream.java:226)
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:74)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:431)
2021-04-13 18:54:51.279 7558-7594/com.github.niqdev.ipcam E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.github.niqdev.ipcam, PID: 7558
    java.lang.NegativeArraySizeException: -1
        at com.github.niqdev.mjpeg.MjpegInputStreamDefault.readMjpegFrame(MjpegInputStreamDefault.java:67)
        at com.github.niqdev.mjpeg.MjpegViewDefault$MjpegViewThread.run(MjpegViewDefault.java:431)
smartyw commented 3 years ago

I'm new to this stuff but.... there's a -1 value returned by various methods when expected or required values have not been found. It's the -1 that ultimately causes the NegativeArraySizeException.

For example getEndOfSeqeunce(...) (sic) and consequently getStartOfSequence(...) also.

From what I can tell, the code looks for start / end values of 0xFFD8 and 0xFFD9 in a buffer. But in my case this is what's in the buffer:

0xFFE000104A46494600010100000100010000FFDB00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101

The 0xFFE0 and 0xFFDB stand out. Google found these values here for me in this article:

www.programmersought.com/article/20515526149/

I'm wondering if my stream contains JFIF encoded JPEGs and that the API does not support this encoding.

Seem plausible? It's not much more than guesswork but maybe someone with more knowledge will be able to say more.

I never heard of JFIF until today. If I have the time I'll do some reading but that's by no means guaranteed.

https://web.archive.org/web/20120301195630/http:/www.jpeg.org/public/jfif.pdf

niqdev commented 3 years ago

oh that's interesting, thanks for all the details! never heard about JFIF either 😅