├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── encodings.xml
├── gradle.xml
├── jarRepositories.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── libs
│ ├── ijkplayer-java-release.aar
│ └── libvlc-all-4.0.0-eap8.aar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── company
│ │ └── ijkpalyer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── company
│ │ │ └── ijkpalyer
│ │ │ ├── ActivityVLC4.java
│ │ │ ├── MainActivity.java
│ │ │ ├── dk
│ │ │ ├── IjkPlayer.java
│ │ │ ├── IjkPlayerFactory.java
│ │ │ └── RawDataSourceProvider.java
│ │ │ ├── ijk
│ │ │ ├── IjkPlayerActivity.java
│ │ │ └── MyIjkPlayerActivity.java
│ │ │ └── utils
│ │ │ ├── FileUtil.java
│ │ │ └── LogUtils.java
│ ├── jniLibs
│ │ ├── arm64-v8a
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ │ ├── armeabi-v7a
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ │ ├── armeabi
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ │ ├── x86
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ │ └── x86_64
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_ijk.xml
│ │ ├── activity_main.xml
│ │ ├── activity_my_ijk_player.xml
│ │ └── activity_vlc4.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── vlc_logo.jpg
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── company
│ └── ijkpalyer
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 移步:VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
2 | ## 移步:VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
3 |
4 | #### 此Demo(不维护了): 支持录像,截图功能(点播不支持录像,只支持直播噢!),支持RTSP,RTMP,HTTP等等在线直播(支持所有CPU架构),您可以做二次开发添加其他功能!!
5 | #### 此Demo(不维护了): 支持录像,截图功能(点播不支持录像,只支持直播噢!),支持RTSP,RTMP,HTTP等等在线直播(支持所有CPU架构),您可以做二次开发添加其他功能!!
6 | #### 此Demo(不维护了): 支持录像,截图功能(点播不支持录像,只支持直播噢!),支持RTSP,RTMP,HTTP等等在线直播(支持所有CPU架构),您可以做二次开发添加其他功能!!
7 | *
8 | #### 此Demo(不维护了): 因为此项目只支持直播的时候,才能录制视频~~。我现在使用VLC,作为播放器来实现,录像,截图功能(点播,直播都支持!) 请移步:VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
9 | #### 此Demo(不维护了): 因为此项目只支持直播的时候,才能录制视频。我现在使用VLC,作为播放器来实现,录像,截图功能(点播,直播都支持!) 请移步:VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
10 | #### 此Demo(不维护了): 因为此项目只支持直播的时候,才能录制视频。我现在使用VLC,作为播放器来实现,录像,截图功能(点播,直播都支持!) 请移步:VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
11 | *
12 | #### 如果帮助的到了您,请您不要吝啬你的Star,谢谢您的点赞(Star),3Q
13 | #### 如果帮助的到了您,请您不要吝啬你的Star,谢谢您的点赞(Star),3Q
14 | #### 如果帮助的到了您,请您不要吝啬你的Star,谢谢您的点赞(Star),3Q
15 |
16 | #### VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
17 | *
18 | *
19 | #### 百度ijkplayer录像 截图功能有很多教程,但是很多都是会,崩溃或者闪退,等等问题!!!!所以在此我觉得把这个Demo开源出来,希望能帮助到需要的人(不维护了)!
20 | #### VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
21 |
22 |
23 | ## 此项目已经停止更新,目前使用VLC做二次开发(点播,直播,都能实现截图,录像功能,健壮性更强)VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
24 | ## 此项目已经停止更新,目前使用VLC做二次开发(点播,直播,都能实现截图,录像功能,健壮性更强)VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
25 | ## 此项目已经停止更新,目前使用VLC做二次开发(点播,直播,都能实现截图,录像功能,健壮性更强)VLC录像截图Demo地址:[VlcRecordPlayer](https://github.com/ILoveLin/VlcRecordPlayer)
26 |
27 | #### 项目使用
28 | * 备注:您不需要编译任何东西,只需要下载Demo,拷贝main目录下的 jniLibs包下的所有so依赖库 和app/libs目录下 ijkplayer-java-release.aar (这个是ijk工程里面的java代码)到你自己的项目里面.
29 | *
30 | * 第一步:(只支持直播噢!)
31 | * 只需要下载Demo,拷贝main目录下的 jniLibs包下的所有so依赖库 和app目录下 ijkplayer-java-release.aar (这个是ijk工程里面的java代码)
32 | * 您在app的build.gradle中android节点里面找到-->defaultConfig节点--->然后添加
33 | * ndk {abiFilters 'arm64-v8a','armeabi' ,'armeabi-v7a', 'x86', 'x86_64'} // 设置支持的SO库架构
34 | * 然后在 dependencies中添加 implementation files('libs/ijkplayer-java-release.aar') //第一步拷贝的aar的依赖
35 | *
36 | * 第二步:(记得动态获取权限在调用以下API,不然会失败哦)
37 | * 项目中如何使用?(只支持直播噢!)
38 | *
39 | * 直播中:开始录像: mPlayer.startRecord(mRecordPath); //mRecordPath 是录像存入的文件路径
40 | * 直播中:结束录像: mPlayer.stopRecord();
41 | * 直播中:截图: Bitmap srcBitmap = Bitmap.createBitmap(1920,1080, Bitmap.Config.ARGB_8888); mPlayer.getCurrentFrame(srcBitmap); //PS:异步的哦,记得开线程!!!!
42 | *
43 |
44 | * 重要的事情说三遍!!!
45 | * What ?还是不懂?请您下载Demo,查看MainActivity里面的简单代码,我相信您一看就会!
46 | * What ?还是不懂?请您下载Demo,查看MainActivity里面的简单代码,我相信您一看就会!
47 | * What ?还是不懂?请您下载Demo,查看MainActivity里面的简单代码,我相信您一看就会!
48 | *
49 | #### 简单说明
50 | * 网上有很多修改底层C的代码,实现录像和截图,但是好多都会程序奔溃和闪退,这个Demo做了优化
51 |
52 | * 修改底层C代码增加了三个native的API:
53 | * 我们使用的其实都是底层调用这三个方法:
54 | * public native int startRecord(String var1); //开始录像
55 | * public native int stopRecord(); //结束录像
56 | * public native boolean getCurrentFrame(Bitmap var1); //截图
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.iml
3 | .gradle
4 | /local.properties
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | /gradle/
17 | /.idea/
18 | /gradle.properties
19 | *.apk
20 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdkVersion 30
7 | buildToolsVersion "30.0.2"
8 |
9 | defaultConfig {
10 | applicationId "com.company.myapplication"
11 | minSdkVersion 21
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 | ndk {
16 | // 设置支持的SO库架构
17 | // abiFilters 'armeabi-v7a'
18 | abiFilters 'arm64-v8a','armeabi' ,'armeabi-v7a', 'x86', 'x86_64'
19 | }
20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 | compileOptions.encoding = "UTF-8"
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | // sourceSets.main {
37 | // jniLibs.srcDirs = ['libs']
38 | // }
39 | }
40 |
41 | dependencies {
42 |
43 | implementation files('libs/ijkplayer-java-release.aar')
44 | implementation files('libs/libvlc-all-4.0.0-eap8.aar')
45 | implementation 'com.hjq:xxpermissions:9.6'
46 | // # 必选,内部默认使用系统mediaplayer进行解码
47 | // implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.1'
48 | // implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.1'
49 | // implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.1'
50 |
51 |
52 | //// # required, enough for most devices.
53 | // implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
54 | // implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'
55 | //
56 | //// # Other ABIs: optional
57 | // implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
58 | // implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
59 | // implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
60 | // implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'
61 | //
62 | //// # ExoPlayer as IMediaPlayer: optional, experimental
63 | // implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'
64 |
65 |
66 | implementation 'androidx.appcompat:appcompat:1.2.0'
67 | implementation 'com.google.android.material:material:1.2.1'
68 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
69 | testImplementation 'junit:junit:4.+'
70 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
71 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
72 | implementation 'xyz.doikki.android.dkplayer:dkplayer-java:3.3.7'
73 |
74 | }
--------------------------------------------------------------------------------
/app/libs/ijkplayer-java-release.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/libs/ijkplayer-java-release.aar
--------------------------------------------------------------------------------
/app/libs/libvlc-all-4.0.0-eap8.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/libs/libvlc-all-4.0.0-eap8.aar
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/company/ijkpalyer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.company.myapplication", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/ActivityVLC4.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.SurfaceTexture;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.os.Environment;
9 | import android.util.Log;
10 | import android.view.Surface;
11 | import android.view.SurfaceView;
12 | import android.view.TextureView;
13 | import android.view.View;
14 | import android.widget.Button;
15 | import android.widget.Toast;
16 |
17 | import androidx.appcompat.app.AppCompatActivity;
18 |
19 | import com.hjq.permissions.OnPermissionCallback;
20 | import com.hjq.permissions.Permission;
21 | import com.hjq.permissions.XXPermissions;
22 |
23 | import org.videolan.libvlc.LibVLC;
24 | import org.videolan.libvlc.Media;
25 | import org.videolan.libvlc.MediaPlayer;
26 | import org.videolan.libvlc.interfaces.IVLCVout;
27 |
28 | import java.io.File;
29 | import java.text.SimpleDateFormat;
30 | import java.util.ArrayList;
31 | import java.util.Date;
32 | import java.util.List;
33 | import java.util.Locale;
34 |
35 | //import org.videolan.libvlc.media.MediaPlayer;
36 |
37 | /**
38 | * vlc 播放器 4.0.0 版本还是测试版本
39 | * 播放视频会卡顿 仅供参考
40 | */
41 | public class ActivityVLC4 extends AppCompatActivity implements View.OnClickListener {
42 | private static final String TAG = "MainActivity";
43 | //苹果公司点播的的流地址
44 | // public static final String path = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8";
45 | //我自己公司rtsp的流地址
46 | // public static String path = "http://192.168.67.210:3333/api/stream/audio?session=123456";
47 | public static String path = "http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8";
48 |
49 | // public static final String path = "rtsp://root:root@192.168.66.31:7788/session0.mpg";
50 | private SurfaceView texture_video;
51 | private LibVLC mLibVLC = null;
52 | private MediaPlayer mMediaPlayer = null;
53 | private TextureView textureView;
54 | private Surface mSurface;
55 | private SurfaceView surfaceView;
56 | private Button btn_end_record;
57 | private Button btn_frame;
58 | private Button btn_start_record;
59 | private String mRecordPath;
60 | private Button start_live;
61 |
62 |
63 | @Override
64 | protected void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 | setContentView(R.layout.activity_vlc4);
67 | // textureView = findViewById(R.id.texture_video);
68 | textureView = findViewById(R.id.texture_view);
69 | textureView.setSurfaceTextureListener(listener);
70 |
71 | btn_start_record = findViewById(R.id.btn_start_record);
72 | btn_end_record = findViewById(R.id.btn_end_record);
73 | start_live = findViewById(R.id.start_live);
74 | btn_frame = findViewById(R.id.btn_frame);
75 | start_live.setOnClickListener(this);
76 | btn_start_record.setOnClickListener(this);
77 | btn_end_record.setOnClickListener(this);
78 | btn_frame.setOnClickListener(this);
79 | requestPermission();
80 |
81 | }
82 |
83 | @Override
84 | public void onClick(View v) {
85 | switch (v.getId()) {
86 | case R.id.start_live: //开始
87 | mMediaPlayer.play();
88 | Toast.makeText(this, "开始-播放", Toast.LENGTH_SHORT).show();
89 | break;
90 | case R.id.btn_start_record: //开始
91 | ///storage/emulated/0/MyMovies/2023-03-10 14:42:50.mp4
92 | ///storage/emulated/0/MyMovies/2023-03-10 14:49:33.mp4
93 | Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show();
94 | File file = new File(Environment.getExternalStorageDirectory() + "/MyMovies");
95 | // /storage/emulated/0/RecordVideos
96 | Log.e(TAG, "===file.exists()===" + file.exists());
97 | Log.e(TAG, "===file.mkdirs()===" + file.mkdirs());
98 | Log.e(TAG, "===file.mkdirs()=file==" + file.getAbsolutePath());
99 | if (!file.exists()) {
100 | file.mkdirs();
101 | Log.e(TAG, "===000===" + file.exists());
102 | }
103 | String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(System.currentTimeMillis()));
104 | String s = file.getAbsolutePath() + "/" + date + ".mp4";
105 | Log.e(TAG, "===s===" + s);
106 |
107 | mRecordPath = s;
108 | //没有找到结束 的方法,所以不知道录像是否成功
109 | mMediaPlayer.record(mRecordPath);
110 | break;
111 | case R.id.btn_end_record: //结束
112 |
113 | scanFile(this, mRecordPath);
114 | Toast.makeText(this, "结束录像", Toast.LENGTH_SHORT).show();
115 |
116 | break;
117 | case R.id.btn_frame: //截图
118 |
119 | Toast.makeText(this, "截图", Toast.LENGTH_SHORT).show();
120 |
121 |
122 | break;
123 |
124 |
125 | }
126 | }
127 |
128 | //刷新相册,出现刚刚录像视频
129 | public static void scanFile(Context context, String filePath) {
130 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
131 | intent.setData(Uri.fromFile(new File(filePath)));
132 | context.sendBroadcast(intent);
133 | }
134 |
135 | @Override
136 | protected void onDestroy() {
137 | super.onDestroy();
138 | mMediaPlayer.release();
139 | mLibVLC.release();
140 | }
141 |
142 | @Override
143 | protected void onStart() {
144 | super.onStart();
145 |
146 |
147 | }
148 |
149 | @Override
150 | protected void onStop() {
151 | super.onStop();
152 |
153 | mMediaPlayer.stop();
154 | mMediaPlayer.detachViews();
155 | }
156 |
157 |
158 | private TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
159 | @Override
160 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
161 | createPlayer();
162 | // mSurface = new Surface(surface);
163 | // mMediaPlayer.setSurface(mSurface);
164 | }
165 |
166 | @Override
167 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
168 |
169 | }
170 |
171 | @Override
172 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
173 | textureView.setSurfaceTextureListener(null);
174 | textureView = null;
175 | mSurface = null;
176 | return true;
177 | }
178 |
179 | @Override
180 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
181 |
182 | }
183 | };
184 |
185 | private void createPlayer() {
186 |
187 | final ArrayList args = new ArrayList<>();//VLC参数
188 |
189 | args.add("--rtsp-tcp");//强制rtsp-tcp,加快加载视频速度
190 |
191 | args.add("--aout=opensles");
192 |
193 | args.add("--audio-time-stretch");
194 |
195 |
196 | //args.add("--sub-source=marq{marquee=\"%Y-%m-%d,%H:%M:%S\",position=10,color=0xFF0000,size=40}");//这行是可以再vlc窗口右下角添加当前时间的
197 |
198 | args.add("-vvv");
199 |
200 | mLibVLC = new LibVLC(this, args);
201 | mMediaPlayer = new MediaPlayer(mLibVLC);
202 | // Rect surfaceFrame = textureView.getHolder().getSurfaceFrame();
203 |
204 | //设置vlc视频铺满布局
205 | //
206 | //mMediaPlayer.getVLCVout().setWindowSize(layout_video.getWidth(), layout_video.getHeight());//宽,高 播放窗口的大小
207 | //
208 | //mMediaPlayer.setAspectRatio(layout_video.getWidth()+":"+layout_video.getHeight());//宽,高 画面大小
209 |
210 | mMediaPlayer.setScale(0);//这行必须加,为了让视图填满布局
211 |
212 | //添加视图
213 |
214 | IVLCVout vout = mMediaPlayer.getVLCVout();
215 |
216 | vout.setVideoView(textureView);
217 |
218 | vout.attachViews();
219 |
220 | Uri uri = Uri.parse(path);//rtsp流地址或其他流地址//"https://media.w3.org/2010/05/sintel/trailer.mp4"
221 |
222 | final Media media = new Media(mLibVLC, uri);
223 |
224 | int cache = 1000;
225 |
226 | media.addOption(":network-caching=" + cache);
227 |
228 | media.addOption(":file-caching=" + cache);
229 |
230 | media.addOption(":live-cacheing=" + cache);
231 |
232 | media.addOption(":sout-mux-caching=" + cache);
233 |
234 | media.addOption(":codec=mediacodec,iomx,all");
235 |
236 | mMediaPlayer.setMedia(media);//
237 |
238 | media.setHWDecoderEnabled(false, false);//设置后才可以录制和截屏,这行必须放在mMediaPlayer.setMedia(media)后面,因为setMedia会设置setHWDecoderEnabled为true
239 |
240 | mMediaPlayer.setEventListener(new MediaPlayer.EventListener() {
241 | @Override
242 | public void onEvent(MediaPlayer.Event event) {
243 | if (event.type == MediaPlayer.Event.Playing) {
244 | Log.w("main", "正在播放");
245 | } else {
246 |
247 | }
248 |
249 | }
250 | });
251 | }
252 |
253 | private void requestPermission() {
254 |
255 | XXPermissions.with(this)
256 | // 不适配 Android 11 可以这样写
257 | //.permission(Permission.Group.STORAGE)
258 | // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
259 | .permission(Permission.MANAGE_EXTERNAL_STORAGE)
260 | .request(new OnPermissionCallback() {
261 |
262 | @Override
263 | public void onGranted(List permissions, boolean all) {
264 | if (all) {
265 | Toast.makeText(ActivityVLC4.this, "获取存储权限成功", Toast.LENGTH_SHORT).show();
266 | }
267 | }
268 |
269 | @Override
270 | public void onDenied(List permissions, boolean never) {
271 | if (never) {
272 | Toast.makeText(ActivityVLC4.this, "被永久拒绝授权,请手动授予存储权限", Toast.LENGTH_SHORT).show();
273 |
274 | // 如果是被永久拒绝就跳转到应用权限系统设置页面
275 | XXPermissions.startPermissionActivity(ActivityVLC4.this, permissions);
276 | } else {
277 | Toast.makeText(ActivityVLC4.this, "获取存储权限失败", Toast.LENGTH_SHORT).show();
278 | }
279 | }
280 | });
281 |
282 |
283 | // XXPermissions.with(MainActivity.this)
284 | // // 不适配 Android 11 可以这样写
285 | // //.permission(Permission.Group.STORAGE)
286 | // // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
287 | //// .permission(Permission.MANAGE_EXTERNAL_STORAGE)
288 | // .permission(Permission.WRITE_EXTERNAL_STORAGE)
289 | // .permission(Permission.READ_EXTERNAL_STORAGE)
290 | // .request(new OnPermission() {
291 | //
292 | // @Override
293 | // public void hasPermission(List granted, boolean all) {
294 | // if (all) {
295 | //// showToast("获取存储权限成功");
296 | // }
297 | // }
298 | //
299 | // @Override
300 | // public void noPermission(List denied, boolean never) {
301 | // if (never) {
302 | //// showToast("被永久拒绝授权,请手动授予存储权限");
303 | // // 如果是被永久拒绝就跳转到应用权限系统设置页面
304 | // XXPermissions.startPermissionActivity(MainActivity.this, denied);
305 | // } else {
306 | //// showToast("获取存储权限失败");
307 | // }
308 | // }
309 | // });
310 |
311 | }
312 | // //截图
313 | // public Bitmap capture() {
314 | // Bitmap srcBitmap = Bitmap.createBitmap(1920,
315 | // 1080, Bitmap.Config.ARGB_8888);
316 | // mPlayer.getCurrentFrame(srcBitmap);
317 | // return srcBitmap;
318 | // }
319 | }
320 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | import com.company.ijkpalyer.ijk.IjkPlayerActivity;
10 | import com.company.ijkpalyer.ijk.MyIjkPlayerActivity;
11 |
12 | /**
13 | * 原版ijk,aar的录像,没有集成DK播放器
14 | */
15 | public class MainActivity extends AppCompatActivity {
16 | private static final String TAG = "MainActivity";
17 | //苹果公司点播的的流地址
18 | // public static final String path = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8";
19 | //我自己公司rtsp的流地址
20 | // public static final String path = "rtsp://root:root@192.168.66.31:7788/session0.mpg";
21 | // public static String path = "http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8";
22 | // public static String path = "http://192.168.67.210:3333/api/stream/video?session=123456";
23 | public static String path = "http://220.161.87.62:8800/hls/0/index.m3u8";
24 |
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_main);
30 | //跳转DKPlayer播放器--切换成自己自定义可以录像的ijk内核
31 | findViewById(R.id.go_record_ijkdkplaeyr).setOnClickListener(new View.OnClickListener() {
32 | @Override
33 | public void onClick(View v) {
34 | Intent intent = new Intent(MainActivity.this, IjkPlayerActivity.class);
35 | startActivity(intent);
36 | }
37 | });
38 | //自己写的ijk测试播放器
39 | findViewById(R.id.go_my_ijkdkplaeyr).setOnClickListener(new View.OnClickListener() {
40 | @Override
41 | public void onClick(View v) {
42 | Intent intent = new Intent(MainActivity.this, MyIjkPlayerActivity.class);
43 | startActivity(intent);
44 | }
45 | });
46 |
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/dk/IjkPlayer.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.dk;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.content.res.AssetFileDescriptor;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.text.TextUtils;
9 | import android.util.Log;
10 | import android.view.Surface;
11 | import android.view.SurfaceHolder;
12 |
13 | import java.util.Map;
14 |
15 | import tv.danmaku.ijk.media.player.IMediaPlayer;
16 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
17 | import tv.danmaku.ijk.media.player.misc.ITrackInfo;
18 | import tv.danmaku.ijk.media.player.misc.IjkTrackInfo;
19 | import xyz.doikki.videoplayer.player.AbstractPlayer;
20 | import xyz.doikki.videoplayer.player.VideoViewManager;
21 |
22 | public class IjkPlayer extends AbstractPlayer implements IMediaPlayer.OnErrorListener,
23 | IMediaPlayer.OnCompletionListener, IMediaPlayer.OnInfoListener,
24 | IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnPreparedListener,
25 | IMediaPlayer.OnVideoSizeChangedListener, IjkMediaPlayer.OnNativeInvokeListener {
26 |
27 | public IjkMediaPlayer mMediaPlayer;
28 | private int mBufferedPercent;
29 | private final Context mAppContext;
30 |
31 | public IjkPlayer(Context context) {
32 | mAppContext = context;
33 | }
34 | //IjkMediaPlayer
35 | public IjkMediaPlayer getIjkMediaPlayer(){
36 |
37 | Log.e("TAG", "===测试=222==" + mMediaPlayer);
38 |
39 | return mMediaPlayer;
40 | }
41 |
42 | @Override
43 | public void initPlayer() {
44 | mMediaPlayer = new IjkMediaPlayer();
45 | //native日志
46 | IjkMediaPlayer.native_setLogLevel(VideoViewManager.getConfig().mIsEnableLog ? IjkMediaPlayer.IJK_LOG_INFO : IjkMediaPlayer.IJK_LOG_SILENT);
47 | setOptions();
48 | mMediaPlayer.setOnErrorListener(this);
49 | mMediaPlayer.setOnCompletionListener(this);
50 | mMediaPlayer.setOnInfoListener(this);
51 | mMediaPlayer.setOnBufferingUpdateListener(this);
52 | mMediaPlayer.setOnPreparedListener(this);
53 | mMediaPlayer.setOnVideoSizeChangedListener(this);
54 | mMediaPlayer.setOnNativeInvokeListener(this);
55 | Log.e("TAG", "===测试=111==" + mMediaPlayer);
56 |
57 | }
58 |
59 | @Override
60 | public void setOptions() {
61 | }
62 |
63 | @Override
64 | public void setDataSource(String path, Map headers) {
65 | try {
66 | Uri uri = Uri.parse(path);
67 | if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
68 | RawDataSourceProvider rawDataSourceProvider = RawDataSourceProvider.create(mAppContext, uri);
69 | mMediaPlayer.setDataSource(rawDataSourceProvider);
70 | } else {
71 | //处理UA问题
72 | if (headers != null) {
73 | String userAgent = headers.get("User-Agent");
74 | if (!TextUtils.isEmpty(userAgent)) {
75 | mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", userAgent);
76 | // 移除header中的User-Agent,防止重复
77 | headers.remove("User-Agent");
78 | }
79 | }
80 | mMediaPlayer.setDataSource(mAppContext, uri, headers);
81 | }
82 | } catch (Exception e) {
83 | mPlayerEventListener.onError();
84 | }
85 | }
86 |
87 | @Override
88 | public void setDataSource(AssetFileDescriptor fd) {
89 | try {
90 | mMediaPlayer.setDataSource(new RawDataSourceProvider(fd));
91 | } catch (Exception e) {
92 | mPlayerEventListener.onError();
93 | }
94 | }
95 |
96 | @Override
97 | public void pause() {
98 | try {
99 | mMediaPlayer.pause();
100 | } catch (IllegalStateException e) {
101 | mPlayerEventListener.onError();
102 | }
103 | }
104 |
105 | @Override
106 | public void start() {
107 | try {
108 | mMediaPlayer.start();
109 | } catch (IllegalStateException e) {
110 | mPlayerEventListener.onError();
111 | }
112 | }
113 |
114 | @Override
115 | public void stop() {
116 | try {
117 | mMediaPlayer.stop();
118 | } catch (IllegalStateException e) {
119 | mPlayerEventListener.onError();
120 | }
121 | }
122 |
123 | @Override
124 | public void prepareAsync() {
125 | try {
126 | mMediaPlayer.prepareAsync();
127 | } catch (IllegalStateException e) {
128 | mPlayerEventListener.onError();
129 | }
130 | }
131 |
132 | @Override
133 | public void reset() {
134 | mMediaPlayer.reset();
135 | mMediaPlayer.setOnVideoSizeChangedListener(this);
136 | setOptions();
137 | }
138 |
139 | @Override
140 | public boolean isPlaying() {
141 | return mMediaPlayer.isPlaying();
142 | }
143 |
144 | @Override
145 | public void seekTo(long time) {
146 | try {
147 | mMediaPlayer.seekTo((int) time);
148 | } catch (IllegalStateException e) {
149 | mPlayerEventListener.onError();
150 | }
151 | }
152 |
153 | @Override
154 | public void release() {
155 | mMediaPlayer.setOnErrorListener(null);
156 | mMediaPlayer.setOnCompletionListener(null);
157 | mMediaPlayer.setOnInfoListener(null);
158 | mMediaPlayer.setOnBufferingUpdateListener(null);
159 | mMediaPlayer.setOnPreparedListener(null);
160 | mMediaPlayer.setOnVideoSizeChangedListener(null);
161 | new Thread() {
162 | @Override
163 | public void run() {
164 | try {
165 | mMediaPlayer.release();
166 | } catch (Exception e) {
167 | e.printStackTrace();
168 | }
169 | }
170 | }.start();
171 | }
172 |
173 | @Override
174 | public long getCurrentPosition() {
175 | return mMediaPlayer.getCurrentPosition();
176 | }
177 |
178 | @Override
179 | public long getDuration() {
180 | return mMediaPlayer.getDuration();
181 | }
182 |
183 | @Override
184 | public int getBufferedPercentage() {
185 | return mBufferedPercent;
186 | }
187 |
188 | @Override
189 | public void setSurface(Surface surface) {
190 | mMediaPlayer.setSurface(surface);
191 | }
192 |
193 | @Override
194 | public void setDisplay(SurfaceHolder holder) {
195 | mMediaPlayer.setDisplay(holder);
196 | }
197 |
198 | @Override
199 | public void setVolume(float v1, float v2) {
200 | mMediaPlayer.setVolume(v1, v2);
201 | }
202 |
203 | @Override
204 | public void setLooping(boolean isLooping) {
205 | mMediaPlayer.setLooping(isLooping);
206 | }
207 |
208 | @Override
209 | public void setSpeed(float speed) {
210 | mMediaPlayer.setSpeed(speed);
211 | }
212 |
213 | @Override
214 | public float getSpeed() {
215 | return mMediaPlayer.getSpeed(0);
216 | }
217 |
218 | @Override
219 | public long getTcpSpeed() {
220 | return mMediaPlayer.getTcpSpeed();
221 | }
222 |
223 | @Override
224 | public boolean onError(IMediaPlayer mp, int what, int extra) {
225 | mPlayerEventListener.onError();
226 | return true;
227 | }
228 |
229 | @Override
230 | public void onCompletion(IMediaPlayer mp) {
231 | mPlayerEventListener.onCompletion();
232 | }
233 |
234 | @Override
235 | public boolean onInfo(IMediaPlayer mp, int what, int extra) {
236 | mPlayerEventListener.onInfo(what, extra);
237 | return true;
238 | }
239 |
240 | @Override
241 | public void onBufferingUpdate(IMediaPlayer mp, int percent) {
242 | mBufferedPercent = percent;
243 | }
244 |
245 | @Override
246 | public void onPrepared(IMediaPlayer mp) {
247 | mPlayerEventListener.onPrepared();
248 | // 修复播放纯音频时状态出错问题
249 | if (!isVideo()) {
250 | mPlayerEventListener.onInfo(AbstractPlayer.MEDIA_INFO_RENDERING_START, 0);
251 | }
252 | }
253 |
254 | private boolean isVideo() {
255 | IjkTrackInfo[] trackInfo = mMediaPlayer.getTrackInfo();
256 | if (trackInfo == null) return false;
257 | for (IjkTrackInfo info : trackInfo) {
258 | if (info.getTrackType() == ITrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
259 | return true;
260 | }
261 | }
262 | return false;
263 | }
264 |
265 | @Override
266 | public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
267 | int videoWidth = mp.getVideoWidth();
268 | int videoHeight = mp.getVideoHeight();
269 | if (videoWidth != 0 && videoHeight != 0) {
270 | mPlayerEventListener.onVideoSizeChanged(videoWidth, videoHeight);
271 | }
272 | }
273 |
274 | @Override
275 | public boolean onNativeInvoke(int what, Bundle args) {
276 | return true;
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/dk/IjkPlayerFactory.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.dk;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
7 | import xyz.doikki.videoplayer.player.PlayerFactory;
8 |
9 | public class IjkPlayerFactory extends PlayerFactory {
10 |
11 | private IjkMediaPlayer ijkMediaPlayer;
12 | public static IjkPlayer ijkPlayer;
13 |
14 | public static IjkPlayerFactory create() {
15 | return new IjkPlayerFactory();
16 | }
17 |
18 | @Override
19 | public IjkPlayer createPlayer(Context context) {
20 | ijkPlayer = new IjkPlayer(context);
21 | ijkMediaPlayer = ijkPlayer.getIjkMediaPlayer();
22 | Log.e("TAG", "===测试=333==" + ijkMediaPlayer);
23 |
24 | return ijkPlayer;
25 | }
26 |
27 | public IjkMediaPlayer getIjkMediaPlayer() {
28 | Log.e("TAG", "===测试=444==" + ijkMediaPlayer);
29 |
30 | return ijkMediaPlayer;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/dk/RawDataSourceProvider.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.dk;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetFileDescriptor;
5 | import android.net.Uri;
6 |
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.FileNotFoundException;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 |
12 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
13 |
14 | public class RawDataSourceProvider implements IMediaDataSource {
15 | private AssetFileDescriptor mDescriptor;
16 |
17 | private byte[] mMediaBytes;
18 |
19 | public RawDataSourceProvider(AssetFileDescriptor descriptor) {
20 | this.mDescriptor = descriptor;
21 | }
22 |
23 | @Override
24 | public int readAt(long position, byte[] buffer, int offset, int size) {
25 | if (position + 1 >= mMediaBytes.length) {
26 | return -1;
27 | }
28 |
29 | int length;
30 | if (position + size < mMediaBytes.length) {
31 | length = size;
32 | } else {
33 | length = (int) (mMediaBytes.length - position);
34 | if (length > buffer.length)
35 | length = buffer.length;
36 |
37 | length--;
38 | }
39 | System.arraycopy(mMediaBytes, (int) position, buffer, offset, length);
40 |
41 | return length;
42 | }
43 |
44 | @Override
45 | public long getSize() throws IOException {
46 | long length = mDescriptor.getLength();
47 | if (mMediaBytes == null) {
48 | InputStream inputStream = mDescriptor.createInputStream();
49 | mMediaBytes = readBytes(inputStream);
50 | }
51 |
52 |
53 | return length;
54 | }
55 |
56 | @Override
57 | public void close() throws IOException {
58 | if (mDescriptor != null)
59 | mDescriptor.close();
60 |
61 | mDescriptor = null;
62 | mMediaBytes = null;
63 | }
64 |
65 | private byte[] readBytes(InputStream inputStream) throws IOException {
66 | ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
67 |
68 | int bufferSize = 1024;
69 | byte[] buffer = new byte[bufferSize];
70 |
71 | int len;
72 | while ((len = inputStream.read(buffer)) != -1) {
73 | byteBuffer.write(buffer, 0, len);
74 | }
75 |
76 | return byteBuffer.toByteArray();
77 | }
78 |
79 | public static RawDataSourceProvider create(Context context, Uri uri) {
80 | try {
81 | AssetFileDescriptor fileDescriptor = context.getContentResolver().openAssetFileDescriptor(uri, "r");
82 | return new RawDataSourceProvider(fileDescriptor);
83 |
84 | } catch (FileNotFoundException e) {
85 | e.printStackTrace();
86 | }
87 | return null;
88 | }
89 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/ijk/IjkPlayerActivity.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.ijk;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.os.Environment;
9 | import android.util.Log;
10 | import android.view.Surface;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.EditText;
14 | import android.widget.ImageView;
15 | import android.widget.Toast;
16 |
17 | import androidx.appcompat.app.AppCompatActivity;
18 |
19 | import com.company.ijkpalyer.R;
20 | import com.company.ijkpalyer.dk.IjkPlayerFactory;
21 | import com.hjq.permissions.OnPermissionCallback;
22 | import com.hjq.permissions.Permission;
23 | import com.hjq.permissions.XXPermissions;
24 |
25 | import java.io.File;
26 | import java.text.SimpleDateFormat;
27 | import java.util.Date;
28 | import java.util.List;
29 | import java.util.Locale;
30 |
31 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
32 | import xyz.doikki.videoplayer.player.BaseVideoView;
33 | import xyz.doikki.videoplayer.player.VideoView;
34 |
35 | /**
36 | * DK播放器,切换成自己的ijk可以录像的播放器 arr内核+jniLibs.so 文件
37 | */
38 | public class IjkPlayerActivity extends AppCompatActivity implements View.OnClickListener {
39 | private static final String TAG = "IjkPlayerActivity";
40 | //苹果公司点播的的流地址
41 | public static final String path = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8";
42 | //我自己公司rtsp的流地址
43 | // public static final String path = "rtsp://root:root@192.168.66.31:7788/session0.mpg";
44 | // public static String path = "http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8";
45 | // public static String path = "http://220.161.87.62:8800/hls/0/index.m3u8";
46 | // public static String path = "http://192.168.67.210:3333/api/stream/video?session=123456";
47 |
48 | private Surface mSurface;
49 | private IjkMediaPlayer mPlayer;
50 | private Button btn_end_record;
51 | private Button btn_frame;
52 | private Button btn_start_record;
53 | private String mRecordPath;
54 | private Button start_live;
55 | private VideoView mVideoView;
56 | private IjkPlayerFactory ijkPlayerFactory;
57 | private EditText et_get_path;
58 | private String currentPath;
59 | private ImageView imageview;
60 | private Button btn_16_9;
61 | private Button btn_default;
62 | private Button btn_yuanshi;
63 | private Button btn_no_voice;
64 |
65 | @Override
66 | protected void onCreate(Bundle savedInstanceState) {
67 | super.onCreate(savedInstanceState);
68 | setContentView(R.layout.activity_ijk);
69 | btn_start_record = findViewById(R.id.btn_start_record);
70 | btn_end_record = findViewById(R.id.btn_end_record);
71 | btn_frame = findViewById(R.id.btn_frame);
72 | start_live = findViewById(R.id.start_live);
73 | et_get_path = findViewById(R.id.et_get_path);
74 | imageview = findViewById(R.id.imageview);
75 | mVideoView = findViewById(R.id.player);
76 | btn_16_9 = findViewById(R.id.btn_16_9);
77 | btn_default = findViewById(R.id.btn_default);
78 | btn_yuanshi = findViewById(R.id.btn_yuanshi);
79 | btn_no_voice = findViewById(R.id.btn_no_voice);
80 | btn_16_9.setOnClickListener(this);
81 | btn_default.setOnClickListener(this);
82 | btn_yuanshi.setOnClickListener(this);
83 | btn_no_voice.setOnClickListener(this);
84 | btn_start_record.setOnClickListener(this);
85 | btn_end_record.setOnClickListener(this);
86 | btn_frame.setOnClickListener(this);
87 | start_live.setOnClickListener(this);
88 | //使用IjkPlayer解码
89 | ijkPlayerFactory = IjkPlayerFactory.create();
90 | mVideoView.setPlayerFactory(ijkPlayerFactory);
91 | requestPermission();
92 | mVideoView.setUrl(path); //设置视频地址
93 | mVideoView.start(); //开始播放,不调用则不自动播放
94 | responseListener();
95 |
96 | }
97 |
98 | private void responseListener() {
99 | mVideoView.addOnStateChangeListener(new BaseVideoView.OnStateChangeListener() {
100 | @Override
101 | public void onPlayerStateChanged(int playerState) {
102 | }
103 |
104 | @Override
105 | public void onPlayStateChanged(int playState) {
106 | switch (playState) {
107 | case VideoView.STATE_BUFFERING://缓存中
108 | Log.e(TAG, "==DKPlayer===缓存中");
109 | break;
110 | case VideoView.STATE_BUFFERED://缓存完毕
111 | Log.e(TAG, "==DKPlayer===缓存完毕");
112 | break;
113 | case VideoView.STATE_PLAYING://播放
114 | Log.e(TAG, "==DKPlayer===播放");
115 | break;
116 | case VideoView.STATE_PAUSED://播放暂停
117 | Log.e(TAG, "==DKPlayer===播放暂停");
118 | break;
119 | case VideoView.STATE_ERROR://播放失败
120 | Log.e(TAG, "==DKPlayer===播放失败");
121 |
122 | break;
123 | }
124 | }
125 | });
126 |
127 | }
128 |
129 | //刷新相册,出现刚刚录像视频
130 | public static void scanFile(Context context, String filePath) {
131 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
132 | intent.setData(Uri.fromFile(new File(filePath)));
133 | context.sendBroadcast(intent);
134 | }
135 |
136 |
137 | @Override
138 | public void onClick(View v) {
139 | switch (v.getId()) {
140 | case R.id.btn_default: //默认
141 | mVideoView.setScreenScaleType(VideoView.SCREEN_SCALE_DEFAULT);
142 | break;
143 | case R.id.btn_yuanshi: //原始
144 | mVideoView.setScreenScaleType(VideoView.SCREEN_SCALE_ORIGINAL);
145 | break;
146 | case R.id.btn_16_9: //16:9
147 | mVideoView.setScreenScaleType(VideoView.SCREEN_SCALE_16_9);
148 | break;
149 | case R.id.btn_no_voice: //静音
150 | if (mVideoView.isMute()) {
151 | mVideoView.setMute(false);
152 | Toast.makeText(IjkPlayerActivity.this, "开启-声音", Toast.LENGTH_SHORT).show();
153 | } else {
154 | mVideoView.setMute(true);
155 | Toast.makeText(IjkPlayerActivity.this, "开启-静音", Toast.LENGTH_SHORT).show();
156 | }
157 | break;
158 | case R.id.start_live: //开始播放
159 | mVideoView.release();
160 | currentPath = et_get_path.getText().toString().trim();
161 | mVideoView.setUrl(currentPath); //设置视频地址
162 | mVideoView.start(); //开始播放,不调用则不自动播放
163 | /**
164 | * 必须播放之后 才能拿到IjkMediaPlayer==mPlayer;
165 | */
166 | break;
167 | case R.id.btn_start_record: //开始录像
168 | //获取可以录像的IjkMediaPlayer
169 | mPlayer = IjkPlayerFactory.ijkPlayer.getIjkMediaPlayer();
170 | Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show();
171 | record();
172 | break;
173 | case R.id.btn_end_record: //结束录像
174 | //获取可以录像的IjkMediaPlayer
175 | mPlayer = IjkPlayerFactory.ijkPlayer.getIjkMediaPlayer();
176 | mPlayer.stopRecord();
177 | scanFile(this, mRecordPath);
178 | Toast.makeText(this, "结束录像", Toast.LENGTH_SHORT).show();
179 |
180 | break;
181 | case R.id.btn_frame: //截图
182 | //1,DK播放器自带截图功能
183 | Bitmap bitmap = mVideoView.doScreenShot();
184 | imageview.setImageBitmap(bitmap);
185 | Toast.makeText(this, "截图", Toast.LENGTH_SHORT).show();
186 | break;
187 |
188 |
189 | }
190 | }
191 |
192 | private void record() {
193 | File file = new File(Environment.getExternalStorageDirectory() + "/MyMovies");
194 | // /storage/emulated/0/RecordVideos
195 | Log.e(TAG, "===file.exists()===" + file.exists());
196 | Log.e(TAG, "===file.mkdirs()===" + file.mkdirs());
197 | Log.e(TAG, "===file.mkdirs()=file==" + file.getAbsolutePath());
198 | if (!file.exists()) {
199 | file.mkdirs();
200 | Log.e(TAG, "===000===" + file.exists());
201 | }
202 | String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(System.currentTimeMillis()));
203 | String s = file.getAbsolutePath() + "/" + date + ".mp4";
204 | Log.e(TAG, "===s===" + s);
205 | String replace = s.replace("-", "_");
206 | Log.e(TAG, "===s==replace=" + replace);
207 | String replace1 = replace.replace(" ", "_");
208 | Log.e(TAG, "===s==replace1=" + replace1);
209 | String replace2 = replace1.replace(":", "_");
210 | Log.e(TAG, "===s==replace2=" + replace2);
211 | mRecordPath = replace2;
212 | Log.e(TAG, "===mRecordPath===" + mRecordPath);
213 |
214 | mPlayer.startRecord(mRecordPath);
215 | }
216 |
217 |
218 | @Override
219 | protected void onPause() {
220 | super.onPause();
221 | mVideoView.pause();
222 | }
223 |
224 | @Override
225 | protected void onDestroy() {
226 | super.onDestroy();
227 | mVideoView.release();
228 | }
229 |
230 | @Override
231 | protected void onResume() {
232 | super.onResume();
233 | mVideoView.resume();
234 | }
235 |
236 |
237 | @Override
238 | public void onBackPressed() {
239 | if (!mVideoView.onBackPressed()) {
240 | super.onBackPressed();
241 | }
242 | }
243 |
244 | private void requestPermission() {
245 |
246 | XXPermissions.with(this)
247 | // 不适配 Android 11 可以这样写
248 | //.permission(Permission.Group.STORAGE)
249 | // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
250 | .permission(Permission.MANAGE_EXTERNAL_STORAGE)
251 | .request(new OnPermissionCallback() {
252 |
253 | @Override
254 | public void onGranted(List permissions, boolean all) {
255 | if (all) {
256 | Toast.makeText(IjkPlayerActivity.this, "获取存储权限成功", Toast.LENGTH_SHORT).show();
257 | }
258 | }
259 |
260 | @Override
261 | public void onDenied(List permissions, boolean never) {
262 | if (never) {
263 | Toast.makeText(IjkPlayerActivity.this, "被永久拒绝授权,请手动授予存储权限", Toast.LENGTH_SHORT).show();
264 |
265 | // 如果是被永久拒绝就跳转到应用权限系统设置页面
266 | XXPermissions.startPermissionActivity(IjkPlayerActivity.this, permissions);
267 | } else {
268 | Toast.makeText(IjkPlayerActivity.this, "获取存储权限失败", Toast.LENGTH_SHORT).show();
269 | }
270 | }
271 | });
272 |
273 |
274 | // XXPermissions.with(MainActivity.this)
275 | // // 不适配 Android 11 可以这样写
276 | // //.permission(Permission.Group.STORAGE)
277 | // // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
278 | //// .permission(Permission.MANAGE_EXTERNAL_STORAGE)
279 | // .permission(Permission.WRITE_EXTERNAL_STORAGE)
280 | // .permission(Permission.READ_EXTERNAL_STORAGE)
281 | // .request(new OnPermission() {
282 | //
283 | // @Override
284 | // public void hasPermission(List granted, boolean all) {
285 | // if (all) {
286 | //// showToast("获取存储权限成功");
287 | // }
288 | // }
289 | //
290 | // @Override
291 | // public void noPermission(List denied, boolean never) {
292 | // if (never) {
293 | //// showToast("被永久拒绝授权,请手动授予存储权限");
294 | // // 如果是被永久拒绝就跳转到应用权限系统设置页面
295 | // XXPermissions.startPermissionActivity(MainActivity.this, denied);
296 | // } else {
297 | //// showToast("获取存储权限失败");
298 | // }
299 | // }
300 | // });
301 |
302 | }
303 | // //截图
304 | // public Bitmap capture() {
305 | // Bitmap srcBitmap = Bitmap.createBitmap(1920,
306 | // 1080, Bitmap.Config.ARGB_8888);
307 | // mPlayer.getCurrentFrame(srcBitmap);
308 | // return srcBitmap;
309 | // }
310 | }
311 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/ijk/MyIjkPlayerActivity.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.ijk;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.SurfaceTexture;
7 | import android.media.AudioManager;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.os.Environment;
11 | import android.provider.MediaStore;
12 | import android.util.Log;
13 | import android.view.Surface;
14 | import android.view.SurfaceView;
15 | import android.view.TextureView;
16 | import android.view.View;
17 | import android.widget.Button;
18 | import android.widget.Toast;
19 |
20 | import androidx.appcompat.app.AppCompatActivity;
21 |
22 | import com.company.ijkpalyer.R;
23 | import com.hjq.permissions.OnPermissionCallback;
24 | import com.hjq.permissions.Permission;
25 | import com.hjq.permissions.XXPermissions;
26 |
27 | import java.io.File;
28 | import java.io.IOException;
29 | import java.text.SimpleDateFormat;
30 | import java.util.Calendar;
31 | import java.util.Date;
32 | import java.util.List;
33 | import java.util.Locale;
34 |
35 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
36 |
37 | /**
38 | * company:江西神州医疗设备有限公司
39 | * author: LoveLin
40 | * time:2023/7/6 10:43
41 | * desc:自己写的ijk测试播放器
42 | */
43 | public class MyIjkPlayerActivity extends AppCompatActivity implements View.OnClickListener {
44 | private static final String TAG = "MainActivity";
45 | //苹果公司点播的的流地址
46 | // public static final String path = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8";
47 | //我自己公司rtsp的流地址
48 | // public static final String path = "rtsp://root:root@192.168.66.31:7788/session0.mpg";
49 | // public static String path = "http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8";
50 | // public static String path = "http://192.168.67.210:3333/api/stream/video?session=123456";
51 | public static String path = "http://220.161.87.62:8800/hls/0/index.m3u8";
52 |
53 | private SurfaceView surfaceView;
54 | private TextureView textureView;
55 | private Surface mSurface;
56 | private IjkMediaPlayer mPlayer;
57 | private Button btn_end_record;
58 | private Button btn_frame;
59 | private Button btn_start_record;
60 | private String mRecordPath;
61 |
62 | @Override
63 | protected void onCreate(Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | setContentView(R.layout.activity_my_ijk_player);
66 | textureView = findViewById(R.id.texture_view);
67 | btn_start_record = findViewById(R.id.btn_start_record);
68 | btn_end_record = findViewById(R.id.btn_end_record);
69 | btn_frame = findViewById(R.id.btn_frame);
70 | textureView.setSurfaceTextureListener(listener);
71 | btn_start_record.setOnClickListener(this);
72 | btn_end_record.setOnClickListener(this);
73 | btn_frame.setOnClickListener(this);
74 | requestPermission();
75 |
76 | }
77 |
78 | //刷新相册,出现刚刚录像视频
79 | public static void scanFile(Context context, String filePath) {
80 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
81 | intent.setData(Uri.fromFile(new File(filePath)));
82 | context.sendBroadcast(intent);
83 | }
84 |
85 | private void record() {
86 | File file = new File(Environment.getExternalStorageDirectory() + "/MyMovies");
87 | // /storage/emulated/0/RecordVideos
88 | Log.e(TAG, "===file.exists()===" + file.exists());
89 | Log.e(TAG, "===file.mkdirs()===" + file.mkdirs());
90 | Log.e(TAG, "===file.mkdirs()=file==" + file.getAbsolutePath());
91 | if (!file.exists()) {
92 | file.mkdirs();
93 | Log.e(TAG, "===000===" + file.exists());
94 | }
95 | String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(System.currentTimeMillis()));
96 | String s = file.getAbsolutePath() + "/" + date + ".mp4";
97 | Log.e(TAG, "===s===" + s);
98 | String replace = s.replace("-", "_");
99 | Log.e(TAG, "===s==replace=" + replace);
100 | String replace1 = replace.replace(" ", "_");
101 | Log.e(TAG, "===s==replace1=" + replace1);
102 | String replace2 = replace1.replace(":", "_");
103 | Log.e(TAG, "===s==replace2=" + replace2);
104 | mRecordPath = replace2;
105 | Log.e(TAG, "===mRecordPath===" + mRecordPath);
106 |
107 | mPlayer.startRecord(mRecordPath);
108 | }
109 |
110 | @Override
111 | public void onClick(View v) {
112 | switch (v.getId()) {
113 | case R.id.btn_start_record: //开始
114 | Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show();
115 | record();
116 | break;
117 | case R.id.btn_end_record: //结束
118 | mPlayer.stopRecord();
119 | // FileUtil.RefreshAlbum(mRecordPath, true, this);
120 | //
121 | scanFile(this, mRecordPath);
122 | Toast.makeText(this, "结束录像", Toast.LENGTH_SHORT).show();
123 |
124 | break;
125 | case R.id.btn_frame: //截图
126 | new Thread() {
127 | @Override
128 | public void run() {
129 | super.run();
130 | Bitmap bitmap = textureView.getBitmap();
131 | //插入相册 ,显示刚刚的截图
132 | MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, "IMG" + Calendar.getInstance().getTime(), null);
133 |
134 | }
135 | }.start();
136 |
137 | Toast.makeText(this, "截图", Toast.LENGTH_SHORT).show();
138 | break;
139 |
140 |
141 | }
142 | }
143 |
144 | private void createPlayer() {
145 | if (mPlayer == null) {
146 | mPlayer = new IjkMediaPlayer();
147 | // 设置倍速,应该是0-2的float类型,可以测试一下
148 | mPlayer.setSpeed(1.0f);
149 | // 设置调用prepareAsync不自动播放,即调用start才开始播放
150 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
151 | mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
152 | /**
153 | * rtsp直播配置如下参数不然 录像会出问题
154 | */
155 | if (path.startsWith("rtsp")) {
156 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", 1);
157 | //播放前的探测Size,默认是1M, 改小一点会出画面更快
158 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 200);
159 | //每处理一个packet之后刷新io上下文
160 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);
161 | //是否开启预缓冲,一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验
162 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
163 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 48); //
164 | // 设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小
165 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
166 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
167 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
168 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 0);
169 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2);
170 | // ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
171 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
172 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100);
173 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
174 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
175 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 8);
176 | mPlayer.setOption(1, "analyzemaxduration", 100L);
177 | mPlayer.setOption(1, "probesize", 10240L); //下探视频时间
178 | mPlayer.setOption(1, "flush_packets", 1L);
179 | mPlayer.setOption(4, "framedrop", 1L);
180 | mPlayer.setOption(4, "max_cached_duration", 600); //直播
181 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);//直播
182 | mPlayer.setOption(4, "packet-buffering", 0L);
183 | } else {
184 | /**
185 | * rtmp http 配置如下参数即可
186 | */
187 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
188 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
189 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
190 | mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
191 | //
192 | }
193 |
194 |
195 | try {
196 | // mPlayer.setDataSource("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4");
197 | mPlayer.setDataSource(path);
198 | mPlayer.start();
199 | } catch (IOException e) {
200 | e.printStackTrace();
201 | }
202 | mPlayer.prepareAsync();
203 | }
204 | }
205 |
206 | private void release() {
207 | if (mPlayer != null) {
208 | mPlayer.stop();
209 | mPlayer.release();
210 | mPlayer = null;
211 | }
212 | IjkMediaPlayer.native_profileEnd();
213 | }
214 |
215 | //
216 | private TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
217 | @Override
218 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
219 | createPlayer();
220 | mSurface = new Surface(surface);
221 | mPlayer.setSurface(mSurface);
222 | }
223 |
224 | @Override
225 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
226 |
227 | }
228 |
229 | @Override
230 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
231 | textureView.setSurfaceTextureListener(null);
232 | textureView = null;
233 | mSurface = null;
234 | return true;
235 | }
236 |
237 | @Override
238 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
239 |
240 | }
241 | };
242 |
243 | @Override
244 | protected void onDestroy() {
245 | super.onDestroy();
246 | release();
247 | }
248 |
249 |
250 | public void handleTextureView(View view) {
251 | mPlayer.setSurface(mSurface);
252 | if (!mPlayer.isPlaying())
253 | mPlayer.start();
254 | }
255 |
256 | private void requestPermission() {
257 |
258 | XXPermissions.with(this)
259 | // 不适配 Android 11 可以这样写
260 | //.permission(Permission.Group.STORAGE)
261 | // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
262 | .permission(Permission.MANAGE_EXTERNAL_STORAGE)
263 | .request(new OnPermissionCallback() {
264 |
265 | @Override
266 | public void onGranted(List permissions, boolean all) {
267 | if (all) {
268 | Toast.makeText(MyIjkPlayerActivity.this, "获取存储权限成功", Toast.LENGTH_SHORT).show();
269 | }
270 | }
271 |
272 | @Override
273 | public void onDenied(List permissions, boolean never) {
274 | if (never) {
275 | Toast.makeText(MyIjkPlayerActivity.this, "被永久拒绝授权,请手动授予存储权限", Toast.LENGTH_SHORT).show();
276 |
277 | // 如果是被永久拒绝就跳转到应用权限系统设置页面
278 | XXPermissions.startPermissionActivity(MyIjkPlayerActivity.this, permissions);
279 | } else {
280 | Toast.makeText(MyIjkPlayerActivity.this, "获取存储权限失败", Toast.LENGTH_SHORT).show();
281 | }
282 | }
283 | });
284 |
285 |
286 | // XXPermissions.with(MainActivity.this)
287 | // // 不适配 Android 11 可以这样写
288 | // //.permission(Permission.Group.STORAGE)
289 | // // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
290 | //// .permission(Permission.MANAGE_EXTERNAL_STORAGE)
291 | // .permission(Permission.WRITE_EXTERNAL_STORAGE)
292 | // .permission(Permission.READ_EXTERNAL_STORAGE)
293 | // .request(new OnPermission() {
294 | //
295 | // @Override
296 | // public void hasPermission(List granted, boolean all) {
297 | // if (all) {
298 | //// showToast("获取存储权限成功");
299 | // }
300 | // }
301 | //
302 | // @Override
303 | // public void noPermission(List denied, boolean never) {
304 | // if (never) {
305 | //// showToast("被永久拒绝授权,请手动授予存储权限");
306 | // // 如果是被永久拒绝就跳转到应用权限系统设置页面
307 | // XXPermissions.startPermissionActivity(MainActivity.this, denied);
308 | // } else {
309 | //// showToast("获取存储权限失败");
310 | // }
311 | // }
312 | // });
313 |
314 | }
315 | // //截图
316 | // public Bitmap capture() {
317 | // Bitmap srcBitmap = Bitmap.createBitmap(1920,
318 | // 1080, Bitmap.Config.ARGB_8888);
319 | // mPlayer.getCurrentFrame(srcBitmap);
320 | // return srcBitmap;
321 | // }
322 | }
323 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/utils/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.utils;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.ContentValues;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.media.MediaScannerConnection;
8 | import android.net.Uri;
9 | import android.os.Environment;
10 | import android.os.StatFs;
11 | import android.provider.MediaStore;
12 | import android.text.TextUtils;
13 | import android.text.format.Formatter;
14 |
15 | import java.io.File;
16 |
17 | public class FileUtil {
18 |
19 | private static MediaScannerConnection mMediaScanner;
20 |
21 | public static String getROMTotalSize(final Context context) {
22 | File path = Environment.getExternalStorageDirectory();
23 | StatFs stat = new StatFs(path.getPath());
24 | long blockSize = stat.getBlockSize();
25 | long totalBlocks = stat.getBlockCount();
26 | String availMemStr = formateFileSize(context, blockSize * totalBlocks);
27 | return availMemStr;
28 |
29 | }
30 |
31 | //调用系统函数,字符串转换 long -String KB/MB
32 | public static String formateFileSize(Context context, long size) {
33 | return Formatter.formatFileSize(context, size);
34 | }
35 |
36 | public static String getROMAvailableSize(final Context context) {
37 | File path = Environment.getExternalStorageDirectory();
38 | StatFs stat = new StatFs(path.getPath());
39 | long blockSize = stat.getBlockSize();
40 | long availableBlocks = stat.getAvailableBlocks();
41 | String availMemStr = formateFileSize(context, blockSize * availableBlocks);
42 | return availMemStr;
43 |
44 | }
45 |
46 | static final int ERROR = -1;
47 |
48 | /**
49 | * 外部存储是否可用
50 | *
51 | * @return
52 | */
53 | static public boolean externalMemoryAvailable() {
54 | return Environment.getExternalStorageState().equals(
55 | Environment.MEDIA_MOUNTED);
56 | }
57 |
58 | /**
59 | * 获取手机内部可用空间大小
60 | *
61 | * @return
62 | */
63 | static public String getAvailableInternalMemorySize(final Context context) {
64 | File path = Environment.getDataDirectory();
65 | StatFs stat = new StatFs(path.getPath());
66 | long blockSize = stat.getBlockSize();
67 | long availableBlocks = stat.getAvailableBlocks();
68 | return formateFileSize(context, availableBlocks * blockSize);
69 | }
70 |
71 | /**
72 | * 获取手机内部空间大小
73 | *
74 | * @return
75 | */
76 | static public String getTotalInternalMemorySize(final Context context) {
77 | File path = Environment.getDataDirectory();
78 | StatFs stat = new StatFs(path.getPath());
79 | long blockSize = stat.getBlockSize();
80 | long totalBlocks = stat.getBlockCount();
81 | return formateFileSize(context, totalBlocks * blockSize);
82 | }
83 |
84 | /**
85 | * 获取手机外部可用空间大小
86 | *
87 | * @return
88 | */
89 | static public String getAvailableExternalMemorySize(final Context context) {
90 | if (externalMemoryAvailable()) {
91 | File path = Environment.getExternalStorageDirectory();
92 | StatFs stat = new StatFs(path.getPath());
93 | long blockSize = stat.getBlockSize();
94 | long availableBlocks = stat.getAvailableBlocks();
95 | return formateFileSize(context, availableBlocks * blockSize);
96 | } else {
97 | return "ERROR";
98 | }
99 | }
100 |
101 | /**
102 | * 获取手机外部空间大小
103 | *
104 | * @return
105 | */
106 | static public String getTotalExternalMemorySize(final Context context) {
107 | if (externalMemoryAvailable()) {
108 | File path = Environment.getExternalStorageDirectory();
109 | StatFs stat = new StatFs(path.getPath());
110 | long blockSize = stat.getBlockSize();
111 | long totalBlocks = stat.getBlockCount();
112 | return formateFileSize(context, totalBlocks * blockSize);
113 | } else {
114 | return "ERROR";
115 | }
116 | }
117 |
118 | static public String formatSize(long size) {
119 | String suffix = null;
120 |
121 | if (size >= 1024) {
122 | suffix = "KiB";
123 | size /= 1024;
124 | if (size >= 1024) {
125 | suffix = "MiB";
126 | size /= 1024;
127 | }
128 | }
129 |
130 | StringBuilder resultBuffer = new StringBuilder(Long.toString(size));
131 | int commaOffset = resultBuffer.length() - 3;
132 | while (commaOffset > 0) {
133 | resultBuffer.insert(commaOffset, ',');
134 | commaOffset -= 3;
135 | }
136 |
137 | if (suffix != null)
138 | resultBuffer.append(suffix);
139 | return resultBuffer.toString();
140 | }
141 |
142 | //针对非系统影音资源文件夹
143 | public static void insertIntoMediaStore(Context context, boolean isVideo, File saveFile, long createTime) {
144 | ContentResolver mContentResolver = context.getContentResolver();
145 | if (createTime == 0)
146 | createTime = System.currentTimeMillis();
147 | ContentValues values = new ContentValues();
148 | values.put(MediaStore.MediaColumns.TITLE, saveFile.getName());
149 | values.put(MediaStore.MediaColumns.DISPLAY_NAME, saveFile.getName());
150 | //值一样,但是还是用常量区分对待
151 | values.put(isVideo ? MediaStore.Video.VideoColumns.DATE_TAKEN
152 | : MediaStore.Images.ImageColumns.DATE_TAKEN, createTime);
153 | values.put(MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis());
154 | values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis());
155 | if (!isVideo)
156 | values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
157 | values.put(MediaStore.MediaColumns.DATA, saveFile.getAbsolutePath());
158 | values.put(MediaStore.MediaColumns.SIZE, saveFile.length());
159 | values.put(MediaStore.MediaColumns.MIME_TYPE, isVideo);
160 | //插入
161 | mContentResolver.insert(isVideo
162 | ? MediaStore.Video.Media.EXTERNAL_CONTENT_URI
163 | : MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
164 | }
165 |
166 | // 获取video的mine_type,暂时只支持mp4,3gp
167 | private static String getVideoMimeType(String path) {
168 | String lowerPath = path.toLowerCase();
169 | if (lowerPath.endsWith("mp4") || lowerPath.endsWith("mpeg4")) {
170 | return "video/mp4";
171 | } else if (lowerPath.endsWith("3gp")) {
172 | return "video/3gp";
173 | }
174 | return "video/mp4";
175 | }
176 |
177 | public static void RefreshAlbum(String fileAbsolutePath, boolean isVideo, Context mContext) {
178 | mMediaScanner = new MediaScannerConnection(mContext, new MediaScannerConnection.MediaScannerConnectionClient() {
179 | @Override
180 | public void onMediaScannerConnected() {
181 | if (mMediaScanner.isConnected()) {
182 | LogUtils.e("相册更新问题:连接成功 ");
183 | LogUtils.e("相册更新问题:连接成功 isVideo:"+isVideo);
184 |
185 | if (isVideo) {
186 | mMediaScanner.scanFile(fileAbsolutePath, "video/mp4");
187 | } else {
188 | mMediaScanner.scanFile(fileAbsolutePath, "image/png");
189 | // mMediaScanner.scanFile(fileAbsolutePath, "image/jpeg");
190 | }
191 | } else {
192 | LogUtils.e("相册更新问题:无法更新图库,未连接,广播通知更新图库,异常情况下 ");
193 | }
194 | }
195 |
196 | @Override
197 |
198 | public void onScanCompleted(String path, Uri uri) {
199 | LogUtils.e("相册更新问题:扫描完成 path: " + path + "uri:" + uri);
200 | }
201 |
202 | });
203 |
204 | mMediaScanner.connect();
205 |
206 | }
207 |
208 | public static void scanFile(Context context, String filePath) {
209 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
210 | intent.setData(Uri.fromFile(new File(filePath)));
211 | context.sendBroadcast(intent);
212 | }
213 |
214 | /**
215 | * 保存视频
216 | *
217 | * @param context
218 | * @param file
219 | */
220 | public static void saveVideo(Context context, File file) {
221 | //是否添加到相册
222 | ContentResolver localContentResolver = context.getContentResolver();
223 | ContentValues localContentValues = getVideoContentValues(context, file, System.currentTimeMillis());
224 | Uri localUri = localContentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, localContentValues);
225 | context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri));
226 | }
227 |
228 | public static ContentValues getVideoContentValues(Context paramContext, File paramFile, long paramLong) {
229 | ContentValues localContentValues = new ContentValues();
230 | localContentValues.put("title", paramFile.getName());
231 | localContentValues.put("_display_name", paramFile.getName());
232 | localContentValues.put("mime_type", "video/mp4");
233 | localContentValues.put("datetaken", Long.valueOf(paramLong));
234 | localContentValues.put("date_modified", Long.valueOf(paramLong));
235 | localContentValues.put("date_added", Long.valueOf(paramLong));
236 | localContentValues.put("_data", paramFile.getAbsolutePath());
237 | localContentValues.put("_size", Long.valueOf(paramFile.length()));
238 | return localContentValues;
239 | }
240 |
241 |
242 | /**
243 | * 删除SD卡中的文件或目录
244 | *
245 | * @param path
246 | * @return
247 | */
248 | public static boolean deleteSDFile(String path) {
249 | return deleteSDFile(path, false);
250 | }
251 |
252 | /**
253 | * 删除SD卡中的文件或目录
254 | *
255 | * @param path
256 | * @param deleteParent true为删除父目录
257 | * @return
258 | */
259 | public static boolean deleteSDFile(String path, boolean deleteParent) {
260 | if (TextUtils.isEmpty(path)) {
261 | return false;
262 | }
263 |
264 | File file = new File(path);
265 | if (!file.exists()) {
266 | //不存在
267 | return true;
268 | }
269 | return deleteFile(file, deleteParent);
270 | }
271 |
272 | /**
273 | * @param file
274 | * @param deleteParent true为删除父目录
275 | * @return
276 | */
277 | public static boolean deleteFile(File file, boolean deleteParent) {
278 | boolean flag = false;
279 | if (file == null) {
280 | return flag;
281 | }
282 | if (file.isDirectory()) {
283 | //是文件夹
284 | File[] files = file.listFiles();
285 | if (files.length > 0) {
286 | for (int i = 0; i < files.length; i++) {
287 | flag = deleteFile(files[i], true);
288 | if (!flag) {
289 | return flag;
290 | }
291 | }
292 | }
293 | if (deleteParent) {
294 | flag = file.delete();
295 | }
296 | } else {
297 | flag = file.delete();
298 | }
299 | file = null;
300 | return flag;
301 | }
302 |
303 | /**
304 | * 添加到媒体数据库
305 | *
306 | * @param context 上下文
307 | */
308 | public static Uri fileScanVideo(Context context, String videoPath, int videoWidth, int videoHeight,
309 | int videoTime) {
310 |
311 | File file = new File(videoPath);
312 | if (file.exists()) {
313 |
314 | Uri uri = null;
315 |
316 | long size = file.length();
317 | String fileName = file.getName();
318 | long dateTaken = System.currentTimeMillis();
319 |
320 | ContentValues values = new ContentValues(11);
321 | values.put(MediaStore.Video.Media.DATA, videoPath); // 路径;
322 | values.put(MediaStore.Video.Media.TITLE, fileName); // 标题;
323 | values.put(MediaStore.Video.Media.DURATION, videoTime * 1000); // 时长
324 | values.put(MediaStore.Video.Media.WIDTH, videoWidth); // 视频宽
325 | values.put(MediaStore.Video.Media.HEIGHT, videoHeight); // 视频高
326 | values.put(MediaStore.Video.Media.SIZE, size); // 视频大小;
327 | values.put(MediaStore.Video.Media.DATE_TAKEN, dateTaken); // 插入时间;
328 | values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);// 文件名;
329 | values.put(MediaStore.Video.Media.DATE_MODIFIED, dateTaken / 1000);// 修改时间;
330 | values.put(MediaStore.Video.Media.DATE_ADDED, dateTaken / 1000); // 添加时间;
331 | values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
332 |
333 | ContentResolver resolver = context.getContentResolver();
334 |
335 | if (resolver != null) {
336 | try {
337 | uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
338 | } catch (Exception e) {
339 | e.printStackTrace();
340 | uri = null;
341 | }
342 | }
343 |
344 | if (uri == null) {
345 | MediaScannerConnection.scanFile(context, new String[]{videoPath}, new String[]{"video/*"}, new MediaScannerConnection.OnScanCompletedListener() {
346 | @Override
347 | public void onScanCompleted(String path, Uri uri) {
348 |
349 | }
350 | });
351 | }
352 |
353 | return uri;
354 | }
355 |
356 | return null;
357 | }
358 |
359 | /**
360 | * SD卡存在并可以使用
361 | */
362 | public static boolean isSDExists() {
363 | return Environment.getExternalStorageState().equals(
364 | Environment.MEDIA_MOUNTED);
365 | }
366 |
367 | /**
368 | * 获取SD卡的剩余容量,单位是Byte
369 | *
370 | * @return
371 | */
372 | public static long getSDFreeMemory() {
373 | try {
374 | if (isSDExists()) {
375 | File pathFile = Environment.getExternalStorageDirectory();
376 | // Retrieve overall information about the space on a filesystem.
377 | // This is a Wrapper for Unix statfs().
378 | StatFs statfs = new StatFs(pathFile.getPath());
379 | // 获取SDCard上每一个block的SIZE
380 | long nBlockSize = statfs.getBlockSize();
381 | // 获取可供程序使用的Block的数量
382 | // long nAvailBlock = statfs.getAvailableBlocksLong();
383 | long nAvailBlock = statfs.getAvailableBlocks();
384 | // 计算SDCard剩余大小Byte
385 | long nSDFreeSize = nAvailBlock * nBlockSize;
386 | return nSDFreeSize;
387 | }
388 | } catch (Exception ex) {
389 | ex.printStackTrace();
390 | }
391 | return 0;
392 | }
393 | }
394 |
--------------------------------------------------------------------------------
/app/src/main/java/com/company/ijkpalyer/utils/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer.utils;
2 |
3 | import android.util.Log;
4 |
5 | public class LogUtils {
6 |
7 | private static final String TAG = "ijkPlayer";
8 |
9 | public static final boolean DEBUG = true;
10 |
11 | public static void i(String msg){
12 | if(!DEBUG){
13 | return;
14 | }
15 | String componName = Thread.currentThread().getStackTrace()[3].getClassName();
16 | String className = componName.substring(componName.lastIndexOf(".")+1, componName.length());
17 | String methodName = Thread.currentThread().getStackTrace()[3].getMethodName();
18 | Log.i(TAG, TAG+" class:"+className+" called:"+methodName+" "+msg);
19 | }
20 |
21 | public static void e(String msg){
22 | if(!DEBUG){
23 | return;
24 | }
25 | String componName = Thread.currentThread().getStackTrace()[3].getClassName();
26 | String className = componName.substring(componName.lastIndexOf(".")+1, componName.length());
27 | String methodName = Thread.currentThread().getStackTrace()[3].getMethodName();
28 | // Log.e(TAG, TAG + " class:" + className + " " + msg);
29 | Log.e(TAG, msg);
30 |
31 | // Log.e(TAG, TAG+" class:"+className+" called:"+methodName+" "+msg);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/arm64-v8a/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/arm64-v8a/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/arm64-v8a/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi-v7a/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi-v7a/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi-v7a/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi-v7a/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi-v7a/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi-v7a/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/armeabi/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/armeabi/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86_64/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86_64/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/jniLibs/x86_64/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_ijk.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
16 |
17 |
23 |
24 |
32 |
33 |
40 |
41 |
42 |
45 |
46 |
47 |
54 |
55 |
62 |
63 |
70 |
71 |
72 |
75 |
76 |
77 |
84 |
85 |
92 |
99 |
106 |
107 |
108 |
109 |
113 |
114 |
123 |
124 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_my_ijk_player.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
15 |
16 |
19 |
20 |
21 |
29 |
30 |
38 |
39 |
47 |
48 |
49 |
50 |
56 |
57 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_vlc4.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
19 |
20 |
21 |
29 |
30 |
38 |
39 |
47 |
48 |
49 |
50 |
56 |
57 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/vlc_logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xhdpi/vlc_logo.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | IjkRecordPlayer
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/test/java/com/company/ijkpalyer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.company.ijkpalyer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | mavenCentral()
5 | maven {url 'https://maven.aliyun.com/repository/google/'}
6 | maven {url 'https://maven.aliyun.com/repository/public/'}
7 | // 阿里云云效仓库(Gradle 插件):https://maven.aliyun.com/mvn/guide
8 | maven {url 'https://maven.aliyun.com/repository/gradle-plugin/'}
9 | maven { url 'https://jitpack.io' }
10 | }
11 | dependencies {
12 | classpath "com.android.tools.build:gradle:4.1.0"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | mavenCentral()
22 | maven {url 'https://maven.aliyun.com/repository/google/'}
23 | maven {url 'https://maven.aliyun.com/repository/public/'}
24 | // 阿里云云效仓库(Gradle 插件):https://maven.aliyun.com/mvn/guide
25 | maven {url 'https://maven.aliyun.com/repository/gradle-plugin/'}
26 | maven { url 'https://jitpack.io' }
27 | }
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1024m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ILoveLin/IjkRecordPlayer/ea8f5c029e8b1fe15c85bdfe964b453e625564cb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Dec 30 17:02:35 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "IjkRecordPlayer"
--------------------------------------------------------------------------------