Android使用VLC播放摄像头RTSP流

1.修改项目build.gradle,增加maven库地址

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://dl.bintray.com/videolan/Android" }
    }
}

2.修改模块build.gradle,增加依赖

// VLC
implementation 'org.videolan.android:libvlc-all:3.1.12'

3.布局文件中加入VLCVideoLayout

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <org.videolan.libvlc.util.VLCVideoLayout
        android:id="@+id/video_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

继续阅读Android使用VLC播放摄像头RTSP流

多线程下java.util.ConcurrentModificationException问题解决

private List<String> mData = new ArrayList<>();
private List<String> mCloneData;
private int count = 0;

public void test() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                for (String string : getData()) {
                    Log.i(TAG, string);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                mData.add(count++ + "");
                mCloneData = null;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}

private List<String> getData() {
    if (mCloneData == null) {
        mCloneData = (List<String>) ((ArrayList) mData).clone();
    }
    return mCloneData;
}

参考链接:

java.util.ConcurrentModificationException 异常问题详解

Android VideoView播放本地视频短暂黑屏的解决方法

https://blog.csdn.net/goodlixueyong/article/details/53590573

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.widget.VideoView;

import java.io.File;

public class VideoManager {
    private VideoView mVideoView;
    private MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();

    @SuppressLint("CheckResult")
    public VideoManager(VideoView videoView) {
        mVideoView = videoView;
        // 设置准备完成监听
        mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                // 准备完成后开始播放
                mp.start();
            }
        });
        // 设置播放信息监听
        mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(MediaPlayer mp, int what, int extra) {
                // 视频帧开始渲染时设置背景透明
                if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                    mVideoView.setBackgroundColor(Color.TRANSPARENT);
                }
                return true;
            }
        });
    }

    /**
     * 播放视频
     */
    public void play(String path) {
        // 设置背景
        setBackground(path);
        // 设置视频地址
        mVideoView.setVideoPath(path);
    }

    /**
     * 设置背景
     */
    private void setBackground(String path) {
        File file = new File(path);
        if (file.exists()) {
            mediaMetadataRetriever.setDataSource(file.getAbsolutePath());
            Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(1);
            mVideoView.setBackground(new BitmapDrawable(null, bitmap));
        }
    }
}

Android Studio运行项目时闪退(找不到so文件)

笔者在新电脑上运行之前做的项目时遇到了闪退问题。这个项目之前运行的一直很好,笔者找了很久才找到问题的原因。

笔者在新电脑上运行项目的时候,是运行在一个x86架构的模拟器上面的。笔者的项目中要生成一个so文件,而且笔者的项目中设置了so过滤,只留下了armeabi-v7a架构。然而Android Studio运行项目时只生成了x86架构的so文件,所以打包以后armeabi-v7a目录下就没有这个so文件。

解决方案也很简单,用Android Studio来Build一下APK就可以了,这样会生成所有支持的架构的so文件,然后再运行就不会报错了。

后记:后来经过反复测试发现Android Studio运行项目时只生成了x86架构的so文件的原因是Gradle版本太低。升级Gradle版本到5.4.1,Gradle插件版本到3.5.0就解决了这个问题。


查看CPU支持的ABIs优先级列表:

cat /system/build.prop | grep ro.product.cpu.abilist=

笔者的x86模拟器:

ro.product.cpu.abilist=x86,armeabi-v7a,armeabi

参考链接:

【android】Android系统判断CPU架构信息以及是32位还是64位

安卓项目中so库选择(ndk abiFilters设置armeabi-v7a)

指定ndk需要兼容的架构

defaultConfig {
    ndk {
        abiFilters "armeabi-v7a" // 只兼容armeabi-v7a架构
    }
}

各种CPU架构支持的ABIs优先级列表。

ARMv5 armeabi(1)
ARMv7 armeabi(2)|armeabi-v7a(1)
ARMv8 armeabi(3)|armeabi-v7a(2)|arm64-v8a(1)
x86 armeabi(3)|armeabi-v7a(2)|x86(1)
x86_64 armeabi(4)|armeabi-v7a(3)|x86(2)|x86_64(1)
MIPS mips(1)
MIPS64 mips(2)|mips64(1)

参考链接:

安卓项目中so库选择(ndk abiFilters设置,armeabi,armeabi-v7a,arm64-v8a)

关于abiFilters的使用

Android中的ABI