安卓4.4用java代码实现录屏录音功能

这个方法是从其他地方看到的,我只是做了转载和整合,其中的原理和细节还不是很清楚。

实际上是执行adb shell命令screenrecord去录取屏幕,只不过是在手机端执行这条命令,因此需要root权限(在PC端执行不需要),录制视频最长3分钟,没有声音,所以要另外录音,最后再把音频和视频合成

直接上代码:

先看两个工具类

RootCmdUtils.java用于执行终端命令

package com.example.record;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import android.util.Log;

/**
 * Android运行linux命令
 */
public final class RootCmdUtils {
	private static final String TAG = "RootCmd";
	private static boolean mHaveRoot = false;

	/**
	 * 判断机器Android是否已经root,即是否获取root权限
	 */
	public static boolean haveRoot() {
		if (!mHaveRoot) {
			int ret = execRootCmdSilent("echo test"); // 通过执行测试命令来检测
			if (ret != -1) {
				Log.e(TAG, "have root!");
				mHaveRoot = true;
			} else {
				Log.e(TAG, "not root!");
			}
		} else {
			Log.e(TAG, "mHaveRoot = true, have root!");
		}
		return mHaveRoot;
	}

	/**
	 * 执行命令并且输出结果
	 */
	public static String execRootCmd(String cmd) {
		String result = "";
		DataOutputStream dos = null;
		DataInputStream dis = null;

		try {
			Process p = Runtime.getRuntime().exec("su");// 经过Root处理的android系统即有su命令
			dos = new DataOutputStream(p.getOutputStream());
			dis = new DataInputStream(p.getInputStream());

			Log.e(TAG, cmd);
			dos.writeBytes(cmd + "\n");
			dos.flush();
			dos.writeBytes("exit\n");
			dos.flush();
			String line = null;
			while ((line = dis.readLine()) != null) {
				Log.e("result", line);
				result += line;
			}
			p.waitFor();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (dos != null) {
				try {
					dos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (dis != null) {
				try {
					dis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * 执行命令但不关注结果输出
	 */
	public static int execRootCmdSilent(String cmd) {
		int result = -1;
		DataOutputStream dos = null;

		try {
			Process p = Runtime.getRuntime().exec("su");
			dos = new DataOutputStream(p.getOutputStream());

			Log.e(TAG, cmd);
			dos.writeBytes(cmd + "\n");
			dos.flush();
			dos.writeBytes("exit\n");
			dos.flush();
			p.waitFor();
			result = p.exitValue();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (dos != null) {
				try {
					dos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * 执行命令但不关注结果输出
	 */
	public static void execRootCmdSilentOnThread(final String cmd) {
		new Thread() {
			public void run() {
				DataOutputStream dos = null;
				try {
					Process p = Runtime.getRuntime().exec("su");
					dos = new DataOutputStream(p.getOutputStream());

					Log.e(TAG, cmd);
					dos.writeBytes(cmd + "\n");
					dos.flush();
					dos.writeBytes("exit\n");
					dos.flush();
					p.waitFor();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if (dos != null) {
						try {
							dos.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}.start();
	}
}

AudioRecoderUtils.java用于录音

package com.example.record;

import java.io.File;
import java.io.IOException;

import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.util.TimeUtils;

public class AudioRecoderUtils {

	// 文件路径
	private String filePath;
	// 文件夹路径
	// private String FolderPath;

	private MediaRecorder mMediaRecorder;
	private final String TAG = "fan";
	public static final int MAX_LENGTH = 1000 * 60 * 10;// 最大录音时长1000*60*10;

	private OnAudioStatusUpdateListener audioStatusUpdateListener;

	/**
	 * 文件存储默认sdcard/record
	 */

	public AudioRecoderUtils(String filePath) {

//		File path = new File(filePath);
//		if (!path.exists())
//			path.mkdirs();
		this.filePath = filePath;
		// this.FolderPath = filePath;
	}

	private long startTime;
	private long endTime;

	/**
	 * 开始录音 使用amr格式 录音文件
	 * 
	 * @return
	 */
	public void startRecord() {
		// 开始录音
		/* ①Initial:实例化MediaRecorder对象 */
		if (mMediaRecorder == null)
			mMediaRecorder = new MediaRecorder();
		try {
			/* ②setAudioSource/setVedioSource */
			mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风
			/* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */
			mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
			/*
			 * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
			 * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
			 */
			mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

//			filePath = FolderPath + "tmp" + ".aac";
			/* ③准备 */
			mMediaRecorder.setOutputFile(filePath);
			mMediaRecorder.setMaxDuration(MAX_LENGTH);
			mMediaRecorder.prepare();
			/* ④开始 */
			mMediaRecorder.start();
			// AudioRecord audioRecord.
			/* 获取开始时间* */
			startTime = System.currentTimeMillis();
			updateMicStatus();
			Log.e("fan", "startTime" + startTime);
		} catch (IllegalStateException e) {
			Log.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
		} catch (IOException e) {
			Log.e(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
		}
	}

	/**
	 * 停止录音
	 */
	public long stopRecord() {
		if (mMediaRecorder == null)
			return 0L;
		endTime = System.currentTimeMillis();

		// 有一些网友反应在5.0以上在调用stop的时候会报错,翻阅了一下谷歌文档发现上面确实写的有可能会报错的情况,捕获异常清理一下就行了,感谢大家反馈!
		try {
			mMediaRecorder.stop();
			mMediaRecorder.reset();
			mMediaRecorder.release();
			mMediaRecorder = null;

			audioStatusUpdateListener.onStop(filePath);
			filePath = "";

		} catch (RuntimeException e) {

			// mMediaRecorder.reset();
			// mMediaRecorder.release();
			// mMediaRecorder = null;
			e.printStackTrace();
//			File file = new File(filePath);
//			if (file.exists())
//				file.delete();
//
//			filePath = "";

		}
		return endTime - startTime;
	}

	/**
	 * 取消录音
	 */
	public void cancelRecord() {

		try {

			mMediaRecorder.stop();
			mMediaRecorder.reset();
			mMediaRecorder.release();
			mMediaRecorder = null;

		} catch (RuntimeException e) {
			mMediaRecorder.reset();
			mMediaRecorder.release();
			mMediaRecorder = null;
		}
		File file = new File(filePath);
		if (file.exists())
			file.delete();

		filePath = "";

	}

	private final Handler mHandler = new Handler();
	private Runnable mUpdateMicStatusTimer = new Runnable() {
		public void run() {
			updateMicStatus();
		}
	};

	private int BASE = 1;
	private int SPACE = 100;// 间隔取样时间

	public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
		this.audioStatusUpdateListener = audioStatusUpdateListener;
	}

	/**
	 * 更新麦克状态
	 */
	private void updateMicStatus() {

		if (mMediaRecorder != null) {
			double ratio = (double) mMediaRecorder.getMaxAmplitude() / BASE;
			double db = 0;// 分贝
			if (ratio > 1) {
				db = 20 * Math.log10(ratio);
				if (null != audioStatusUpdateListener) {
					audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime);
				}
			}
			mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);
		}
	}

	public interface OnAudioStatusUpdateListener {
		/**
		 * 录音中...
		 * 
		 * @param db   当前声音分贝
		 * @param time 录音时长
		 */
		public void onUpdate(double db, long time);

		/**
		 * 停止录音
		 * 
		 * @param filePath 保存路径
		 */
		public void onStop(String filePath);
	}

}

MainActivity.java

package com.example.record;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private TextView tv;
	private EditText et;
	private String fileName = "";
	private AudioRecoderUtils audioRecoderUtils;
	private boolean isRecording = false;
	private String folder;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
					.setMessage("文件已保存在" + folder + fileName + "(合成).mp4").create();
			dialog.show();
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv = findViewById(R.id.tv);
		et = findViewById(R.id.et);
		File file = new File("/sdcard", "0000test");
		if (file.exists() == false) {
			file.mkdirs();
		}
		folder = file.getAbsolutePath() + "/";
	}

	// 录屏
	public void start(View view) {
		if (isRecording == true) {
			Toast.makeText(getApplicationContext(), "不要重复点击哦", Toast.LENGTH_LONG).show();
			return;
		}

		if (RootCmdUtils.haveRoot() == false) {
			Toast.makeText(getApplicationContext(), "手机没有root,不能录屏", Toast.LENGTH_LONG).show();
			return;
		}
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
		fileName = sdf.format(new Date());
		String videoPath = folder + fileName + ".mp4";
		String cmd = "screenrecord " + videoPath;

		// 开始录屏
		RootCmdUtils.execRootCmdSilentOnThread(cmd);
		Log.e("录屏", "开始录屏");
		Toast.makeText(MainActivity.this, "开始录屏", Toast.LENGTH_LONG).show();

		// 开始录制声音
		audioRecoderUtils = new AudioRecoderUtils(folder + fileName + ".aac");
		audioRecoderUtils.startRecord();
		Log.e("录屏", "开始录制声音");

		isRecording = true;
	}

	public void stop(View view) {
		if (isRecording == false) {
			return;
		}
		String pids = RootCmdUtils.execRootCmd("ps  | grep screenrecord");
		tv.setText(pids);

		if (pids.isEmpty()) {
			return;
		}
		String[] split = pids.split("\\s+");
		for (int i = 0; i < split.length; i++) {
			// root是执行这个进程的用户,它后面一列是这个进程的pid
			if (split[i].contains("root")) {
				String cmd = "kill -2 " + split[i + 1];// 相当于ctrl+C结束进程
				RootCmdUtils.execRootCmdSilent(cmd);
			}
		}
		// 停止声音录制
		audioRecoderUtils.stopRecord();
		Toast.makeText(getApplicationContext(), "结束", Toast.LENGTH_LONG).show();
		isRecording = false;
	}

	public void mux(View v) {
		new Thread() {
			public void run() {
				try {
					String videoPath = folder + fileName + ".mp4";
					String audioPath = folder + fileName + ".aac";
					String targetFile = folder + fileName + "(合成).mp4";
					Movie countVideo = MovieCreator.build(videoPath);
					Movie countAudioEnglish = MovieCreator.build(audioPath);
					Track audioTrackEnglish = countAudioEnglish.getTracks().get(0);
					countVideo.addTrack(audioTrackEnglish);
					Container out = new DefaultMp4Builder().build(countVideo);
					FileOutputStream fos = new FileOutputStream(new File(targetFile));
					out.writeContainer(fos.getChannel());
					fos.close();
					handler.sendEmptyMessage(0);
				} catch (IOException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			};
		}.start();

	}
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.record.MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="start"
            android:text="开始录制" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="stop"
            android:text="结束录制" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="mux"
            android:text="合并音视频" />
    </LinearLayout>

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ps  | grep &apos;screenrecord&apos;" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hello_world" />

</LinearLayout>

AndroidManifest.xml权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

另外,合并音视频用的是第三方jar包:(百度云下载)密码:acka

aspectjrt-1.8.0.jar、isoparser-1.0.1.jar

猜你喜欢

转载自blog.csdn.net/qq_40582463/article/details/81914645
今日推荐