实现流程:利用ndk动态注册方式将sd卡上面指定的视频进行拆分(暂且分四部分),拆分完成之后再进行合并,验证合并完成之后的视频能否正常播放。
先熟悉几个操作模拟器的命令:
adb shell、ls、ls -l、cd
adb shell回车后会出现一个#,这时候输入ls可以看到所有文件夹名,输入ls -l可以看到详细的文件夹,打开文件夹输入 cd 文件夹名,返回上一层输入cd ..
开始写代码
1.准备一个视频文件xiaoxingyun.mkv(大概90M的样子),利用push命令放入sd卡
hhh:Desktop huozhenpeng$ adb push xiaoxingyun.mkv /sdcard/
push 完成:xiaoxingyun.mkv: 1 file pushed. 12.2 MB/s (95102663 bytes in 7.448s)
利用ls -l命令查看下:
2.先来进行文件的拆分
在MainActivity中定义一个native方法:
public native void spliteByJNI(String sourceFilePath,String splitFilePath,int num);
利用javap命令看下该方法的签名,动态注册需要用到
public native void spliteByJNI(java.lang.String, java.lang.String, int);
descriptor: (Ljava/lang/String;Ljava/lang/String;I)V
实现代码:
native-lib.cpp
#include <jni.h>
#include <string>
#include<stdlib.h>
#include <android/log.h>
#include <assert.h>
#define TAG "HZP_JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
// 获取数组的大小
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
//获取文件大小
long getFileSize(const char *path)
{
FILE *fp=fopen(path,"rb");
fseek(fp,0,SEEK_END);
long size=ftell(fp);
fclose(fp);
return size;
}
JNIEXPORT void JNICALL native_spliteByJNI
(JNIEnv *env, jclass clazz,jstring sourcepath,jstring split_path,jint num)
{
LOGI("JNI begin 动态注册的方法 ");
LOGI("JNI begin 拆分开始 ");
//首先获取到源文件路径和要拆分成的文件的路径
const char * source_p=env->GetStringUTFChars(sourcepath,NULL);
const char * split_p=env->GetStringUTFChars(split_path,NULL);
//申请一个二维的char数组,用于存放拆分成的文件的名字
char ** patches=(char **)malloc(sizeof(char *)*num);
//循环为每个文件名申请地址
for(int i=0;i<num;i++)
{
patches[i]=(char *)malloc(sizeof(char)*100);//我们认为每个文件的名字世超不过100个字符的
//把要拆分成的文件的名字进行值替换(替换其中的%d)xiaoxingyun_%d.mkv
sprintf(patches[i],split_p,i);
}
//进行文件拆分,先获取源文件的大小
long fileSize=getFileSize(source_p);
//源文件
FILE *fp=fopen(source_p,"rb");
/**
* 拆分逻辑:如果可以恰好平分,则全部是相等的,如果不可以,前面的(num-1)个是相等的额,剩余部分给第num个
*/
if(fileSize%num==0)
{
int part=fileSize/num;
for(int i=0;i<num;i++)
{
FILE *fps=fopen(patches[i],"wb");
//开始写入
for(int j=0;j<part;j++)
{
fputc(fgetc(fp),fps);
}
fclose(fps);
}
} else{
int part=fileSize/(num-1);
for(int i=0;i<num-1;i++)
{
LOGI("JNI ",env->NewStringUTF(patches[i]));
FILE *fps=fopen(patches[i],"wb");
//开始写入
for(int j=0;j<part;j++)
{
fputc(fgetc(fp),fps);
}
fclose(fps);
}
//写入最后一个
FILE*fpl=fopen(patches[num-1],"wb");
for(int j=0;j<fileSize%(num-1);j++)
{
fputc(fgetc(fp),fpl);
}
fclose(fpl);
}
fclose(fp);
for(int i=0;i<num;i++)
{
free(patches[i]);
}
free(patches);
env->ReleaseStringUTFChars(sourcepath,source_p);
env->ReleaseStringUTFChars(split_path,split_p);
LOGI("JNI begin 拆分结束 ");
}
const JNINativeMethod gMethods[] = {
{
"spliteByJNI","(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_spliteByJNI
}
};
int registerNatives(JNIEnv* engv)
{
LOGI("registerNatives begin");
jclass clazz;
clazz = engv -> FindClass("com/example/huozhenpeng/myapplication/MainActivity");
if (clazz == NULL) {
LOGI("clazz is null");
return JNI_FALSE;
}
if (engv ->RegisterNatives( clazz, gMethods, NELEM(gMethods)) < 0) {
LOGI("RegisterNatives error");
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGI("jni_OnLoad begin");
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGI("ERROR: GetEnv failed\n");
return -1;
}
assert(env != NULL);
registerNatives(env);
return JNI_VERSION_1_4;
}
写代码过程中可能会遇到bug,ndk的调试可以用lldb命令:用过xcode的应该比较熟悉
MainActivity.java
package com.example.huozhenpeng.myapplication;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestCameraAccess();
tv_split= (TextView) findViewById(R.id.tv_split);
tv_merge= (TextView) findViewById(R.id.tv_merge);
tv_split.setOnClickListener(this);
tv_merge.setOnClickListener(this);
sourceFilePath= Environment.getExternalStorageDirectory().getAbsolutePath().toString()+"/xiaoxingyun.mkv";
splitFilePath=Environment.getExternalStorageDirectory().getAbsolutePath().toString()+"/xiaoxingyun_%d.mkv";
}
private int REQUEST_CAMERA_CODE=1;
private void requestCameraAccess() {
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_CODE);
}
private TextView tv_split;
private TextView tv_merge;
private String sourceFilePath;
private String splitFilePath;
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.tv_merge:
break;
case R.id.tv_split:
spliteByJNI(sourceFilePath,splitFilePath,4);
break;
}
}
public native void spliteByJNI(String sourceFilePath,String splitFilePath,int num);
}
布局文件就不写了
看下结果: