1

I am developing app to monitor IP camera using my mobile. Below is my MainActivity. I am using libVLC which uses surfaceview to show the video.

public class MainActivity extends AppCompatActivity {

Button prevButton, nextButton;
private int totalPages, currentPage;
RecyclerView recyclerView;
VideoAdapter videoAdapter;
static List<String> cameraList;
Paginator p;

@SuppressLint("AuthLeak")
private String[] vidUrlList = {
        "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
        "https://archive.org/download/ksnn_compilation_master_the_internet/ksnn_compilation_master_the_internet_512kb.mp4",
        "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
        "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov",
        "http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8",
        "rtsp://admin:admin@192.0.0.0:200/12"
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    prevButton = (Button) findViewById(R.id.prev_btn);
    nextButton = (Button) findViewById(R.id.next_btn);
    prevButton.setEnabled(false);

    cameraList = new ArrayList<>(Arrays.asList(vidUrlList));
    p = new Paginator();
    totalPages = Paginator.TOTAL_NUM_ITEMS / Paginator.ITEMS_PER_PAGE;
    currentPage = 0;

    videoAdapter = new VideoAdapter(this, cameraList);
    final RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2);
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(3),
            true));
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));

    nextButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            currentPage += 1;
            recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));
            toggleButtons();
        }
    });

    prevButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            currentPage -= 1;
            recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));
            toggleButtons();
        }
    });
}

@Override
public void onPause() {
    super.onPause();
}

@Override
public void onDestroy() {
    super.onDestroy();
}

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

    private int spanCount;
    private int spacing;
    private boolean includeEdge;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                               RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int column = position % spanCount;

        if (includeEdge) {
            outRect.left = spacing - column * spacing / spanCount;
            outRect.right = (column + 1) * spacing / spanCount;

            if (position < spanCount) {
                outRect.top = spacing;
            }
            outRect.bottom = spacing;
        } else {
            outRect.left = column * spacing / spanCount;
            outRect.right = spacing - (column + 1) * spacing / spanCount;
            if (position >= spanCount) {
                outRect.top = spacing;
            }
        }
    }
}

private int dpToPx(int dp) {
    Resources r = getResources();
    return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
            dp, r.getDisplayMetrics()));
}

private void toggleButtons() {
    if (currentPage == totalPages) {
        nextButton.setEnabled(false);
        prevButton.setEnabled(true);
    } else if (currentPage == 0) {
        prevButton.setEnabled(false);
        nextButton.setEnabled(true);
    } else if (currentPage >= 1 && currentPage <= totalPages) {
        nextButton.setEnabled(true);
        prevButton.setEnabled(true);
    }
}

public static List<String> getModifyList() {
    return cameraList;
}}

I used RecylerView and CardView with Paginator option for dispaying 4 cards in each page. Since i have n number of cameras to maintain i used Paginator. Below is my Paginator Class.

public class Paginator {

static List<String> arrNewList = MainActivity.getModifyList();
public static final int TOTAL_NUM_ITEMS = arrNewList.size();
public static final int ITEMS_PER_PAGE = 4;
public static final int ITEMS_REMAINING = TOTAL_NUM_ITEMS % ITEMS_PER_PAGE;
public static final int LAST_PAGE = TOTAL_NUM_ITEMS / ITEMS_PER_PAGE;

public ArrayList<String> generatePage(int currentPage) {
    int startItem = currentPage * ITEMS_PER_PAGE;
    int numOfData = ITEMS_PER_PAGE;
    ArrayList<String> pageData = new ArrayList<>();

    if (currentPage == LAST_PAGE && ITEMS_REMAINING > 0) {
        for (int i = startItem; i < startItem + ITEMS_REMAINING; i++) {
            pageData.add(arrNewList.get(i));
        }
    } else {
        for (int i = startItem; i < startItem + numOfData; i++) {
            pageData.add(arrNewList.get(i));
        }
    }
    return pageData;
}}

Here i am trying to set adapter in which video is playing only at the last postion of each page. I dont know how to solve this. I am proving my adapter code below. Here i called SurfaceHolder.Callback and IVideoPlayer interfaces. Do come up with the solution.

public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.ViewHolder>
    implements SurfaceHolder.Callback, IVideoPlayer {

Context mContext;
List<String> cameraList;

SurfaceHolder surfaceHolder;
private int mVideoWidth;
private int mVideoHeight;
private final static int VideoSizeChanged = -1;
//private SurfaceView mSurfaceView;
private LibVLC libvlc;
EventHandler mEventHandler;

public VideoAdapter(Context mContext, List<String> cameraList) {
    this.mContext = mContext;
    this.cameraList = cameraList;
}

@Override
public VideoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.camera_adapter, parent, false);
    return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(VideoAdapter.ViewHolder holder, int position) {

    surfaceHolder.addCallback(this);
    String urlList = cameraList.get(position);
    Log.e("List1", "-->" + urlList);
    createPlayer(urlList);
}

@Override
public int getItemCount() {
    return cameraList.size();
}

public NativeCrashHandler.OnNativeCrashListener nativecrashListener = new NativeCrashHandler.OnNativeCrashListener() {
    @Override
    public void onNativeCrash() {
        Log.e("vlcdebug", "nativecrash");
    }
};

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.e("mylog", "surfaceCreated");
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    try {
        if (libvlc != null) {
            Log.e("mylog", "libvlc != null");
            libvlc.attachSurface(surfaceHolder.getSurface(), this);
        } else {
            Log.e("mylog", "libvlc == null");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    Log.e("mylog", "surfaceDestroyed");
}

public class ViewHolder extends RecyclerView.ViewHolder {
    private SurfaceView mSurfaceView;

    public ViewHolder(View itemView) {
        super(itemView);
        mSurfaceView = itemView.findViewById(R.id.player_surface);
        surfaceHolder = mSurfaceView.getHolder();
    }
}

private void createPlayer(String vidUrlList) {
    //releasePlayer();
    try {
        libvlc = new LibVLC();
        mEventHandler = libvlc.getEventHandler();
        libvlc.init(mContext);
        libvlc.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);
        libvlc.setSubtitlesEncoding("");
        libvlc.setAout(LibVLC.AOUT_OPENSLES);
        libvlc.setTimeStretching(true);
        libvlc.setVerboseMode(true);
        libvlc.setNetworkCaching(1000);
        NativeCrashHandler.getInstance().setOnNativeCrashListener(
                nativecrashListener);
        if (LibVlcUtil.isGingerbreadOrLater())
            libvlc.setVout(LibVLC.VOUT_ANDROID_WINDOW);
        else
            libvlc.setVout(LibVLC.VOUT_ANDROID_SURFACE);
        LibVLC.restartInstance(mContext);
        mEventHandler.addHandler(mHandler);
        surfaceHolder.setKeepScreenOn(true);
        MediaList list = libvlc.getMediaList();
        list.clear();
        list.add(new Media(libvlc, LibVLC.PathToURI(vidUrlList)), false);
        //list.add(new Media(libvlc, LibVLC.PathToURI(media)), false);
        //list.add(new Media(libvlc, LibVLC.PathToURI(media)), false);
        libvlc.playIndex(0);
    } catch (Exception e) {
        Toast.makeText(mContext, "Error creating player!", Toast.LENGTH_SHORT).show();
    }
}

private void releasePlayer() {
    try {
        EventHandler.getInstance().removeHandler(mHandler);
        //libvlc.stop();
        libvlc.detachSurface();
        surfaceHolder = null;
        libvlc.closeAout();
        libvlc.destroy();
        libvlc = null;

        mVideoWidth = 0;
        mVideoHeight = 0;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private Handler mHandler = new MyHandler(this);

@Override
public void setSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {
    Message msg = Message.obtain(mHandler, VideoSizeChanged, width,
            height);
    msg.sendToTarget();
}

@Override
public int configureSurface(Surface surface, int width, int height, int hal) {
    Log.d("", "configureSurface: width = " + width + ", height = "
            + height);
    if (LibVlcUtil.isICSOrLater() || surface == null)
        return -1;
    if (width * height == 0)
        return 0;
    if (hal != 0)
        surfaceHolder.setFormat(hal);
    surfaceHolder.setFixedSize(width, height);
    return 1;
}

@Override
public void eventHardwareAccelerationError() {
    //releasePlayer();
    Toast.makeText(mContext, "Error with hardware acceleration",
            Toast.LENGTH_SHORT).show();
}

@Override
public void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {

}

@SuppressLint("HandlerLeak")
private class MyHandler extends Handler {
    private WeakReference<VideoAdapter> mOwner;

    MyHandler(VideoAdapter owner) {
        mOwner = new WeakReference<>(owner);
    }

    @Override
    public void handleMessage(Message msg) {
        try {
            VideoAdapter player = mOwner.get();
            Log.e("mylog", "handleMessage " + msg.toString());

            // Libvlc events
            Bundle b = msg.getData();
            Log.e("mylog", "handleMessage " + msg.getData());
            switch (b.getInt("event")) {
                case EventHandler.MediaPlayerEndReached:
                    Log.e("mylog", "MediaPlayerEndReached");
                    player.releasePlayer();
                    break;
                case EventHandler.MediaPlayerPlaying:
                    Log.e("mylog", "MediaPlayerPlaying");
                    break;
                case EventHandler.MediaPlayerPaused:
                    Log.e("mylog", "MediaPlayerPaused");
                    break;
                case EventHandler.MediaPlayerStopped:
                    Log.e("mylog", "MediaPlayerStopped");
                    break;
                case EventHandler.MediaPlayerPositionChanged:
                    Log.i("vlc", "MediaPlayerPositionChanged");
                    //VideoAdapter.this.notify();
                    break;
                case EventHandler.MediaPlayerEncounteredError:
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}}
Bakyaraj
  • 21
  • 6

1 Answers1

0

The problem is that you get your LibVLC instance using class's member varible like this

// kotlin
private libvlc: LibVLC? = null
...
override fun onBindViewHolder(...) {
    libvlc = LibVLC()
    ...
}

which means each time you call onBindViewHolder, get a new instance of LibVLC and assign it to member variable libvlc, libvlc points to a new address of your new LibVLC instance, and the older one can not be refered again.

In other words, four list items in your RecyclerView share a same LibVLC instance, when you attach view/surface, the later visible views let your libvlc detach views from the older ones the reattach to the newest one, so only the last view can play normally.

可圭共
  • 1
  • 1