-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
941 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.libyuv; | ||
|
||
import android.graphics.Bitmap; | ||
|
||
public class LibyuvUtil { | ||
|
||
public static void loadLibrary() { | ||
System.loadLibrary("yuv-sample"); | ||
} | ||
|
||
/** | ||
* 将 NV21 转 I420 | ||
*/ | ||
public static native void convertNV21ToI420(byte[] src, byte[] dst, int width, int height); | ||
|
||
/** | ||
* 压缩 I420 数据 | ||
* <p> | ||
* 执行顺序为:缩放->旋转->镜像 | ||
* | ||
* @param src 原始数据 | ||
* @param srcWidth 原始宽度 | ||
* @param srcHeight 原始高度 | ||
* @param dst 输出数据 | ||
* @param dstWidth 输出宽度 | ||
* @param dstHeight 输出高度 | ||
* @param degree 旋转(90, 180, 270) | ||
* @param isMirror 镜像(镜像在旋转之后) | ||
*/ | ||
public static native void compressI420(byte[] src, int srcWidth, int srcHeight, | ||
byte[] dst, int dstWidth, int dstHeight, | ||
int degree, boolean isMirror); | ||
|
||
/** | ||
* 将 I420 数据注入到 Bitmap 中 | ||
*/ | ||
public static native void convertI420ToBitmap(byte[] src, Bitmap dst, int width, int height); | ||
|
||
/** | ||
* 将 I420 转 NV12 | ||
*/ | ||
public static native void convertI420ToNV12(byte[] src, byte[] dst, int width, int height); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
app/src/main/java/com/renyu/androidimagelibrary/RecordActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package com.renyu.androidimagelibrary; | ||
|
||
import android.hardware.Camera; | ||
import android.os.Bundle; | ||
import android.util.Log; | ||
import android.view.SurfaceHolder; | ||
import android.view.SurfaceView; | ||
import android.view.ViewGroup; | ||
|
||
import androidx.annotation.Nullable; | ||
import androidx.appcompat.app.AppCompatActivity; | ||
|
||
import com.blankj.utilcode.util.TimeUtils; | ||
import com.libyuv.LibyuvUtil; | ||
import com.renyu.commonlibrary.params.InitParams; | ||
import com.zhaoss.weixinrecorded.util.CameraHelp; | ||
import com.zhaoss.weixinrecorded.util.RecordUtil; | ||
|
||
import java.io.File; | ||
import java.util.Date; | ||
import java.util.concurrent.ArrayBlockingQueue; | ||
|
||
import io.microshow.rxffmpeg.RxFFmpegInvoke; | ||
|
||
public class RecordActivity extends AppCompatActivity { | ||
private RecordUtil recordUtil; | ||
private String videoPath; | ||
private String audioPath; | ||
private CameraHelp mCameraHelp; | ||
|
||
private SurfaceView surfaceView; | ||
private SurfaceHolder mSurfaceHolder; | ||
|
||
private ArrayBlockingQueue<byte[]> mYUVQueue = new ArrayBlockingQueue<>(10); | ||
|
||
@Override | ||
protected void onCreate(@Nullable Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.activity_record); | ||
|
||
LibyuvUtil.loadLibrary(); | ||
mCameraHelp = new CameraHelp(); | ||
|
||
surfaceView = findViewById(R.id.surfaceView); | ||
surfaceView.post(() -> { | ||
int width = surfaceView.getWidth(); | ||
int height = surfaceView.getHeight(); | ||
float viewRatio = width * 1f / height; | ||
float videoRatio = 9f / 16f; | ||
ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams(); | ||
if (viewRatio > videoRatio) { | ||
layoutParams.height = (int) (width / viewRatio); | ||
} else { | ||
layoutParams.width = (int) (height * viewRatio); | ||
} | ||
surfaceView.setLayoutParams(layoutParams); | ||
}); | ||
mCameraHelp.setPreviewCallback((data, camera) -> { | ||
if (mYUVQueue.size() >= 10) { | ||
mYUVQueue.poll(); | ||
} | ||
mYUVQueue.add(data); | ||
}); | ||
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { | ||
@Override | ||
public void surfaceCreated(SurfaceHolder holder) { | ||
mSurfaceHolder = holder; | ||
mCameraHelp.openCamera(RecordActivity.this, Camera.CameraInfo.CAMERA_FACING_BACK, mSurfaceHolder); | ||
} | ||
|
||
@Override | ||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { | ||
|
||
} | ||
|
||
@Override | ||
public void surfaceDestroyed(SurfaceHolder holder) { | ||
mCameraHelp.release(); | ||
} | ||
}); | ||
surfaceView.setOnClickListener(v -> mCameraHelp.callFocusMode()); | ||
|
||
findViewById(R.id.start_record).setOnClickListener((v) -> { | ||
startRecord(); | ||
}); | ||
findViewById(R.id.stop_record).setOnClickListener((v) -> { | ||
endRecord(); | ||
}); | ||
} | ||
|
||
private void startRecord() { | ||
videoPath = InitParams.IMAGE_PATH + File.separator + System.currentTimeMillis() + ".h264"; | ||
audioPath = InitParams.IMAGE_PATH + File.separator + System.currentTimeMillis() + ".pcm"; | ||
final boolean isFrontCamera = mCameraHelp.getCameraId() == Camera.CameraInfo.CAMERA_FACING_FRONT; | ||
final int rotation; | ||
if (isFrontCamera) { | ||
rotation = 270; | ||
} else { | ||
rotation = 90; | ||
} | ||
recordUtil = new RecordUtil(videoPath, audioPath, mCameraHelp.getWidth(), mCameraHelp.getHeight(), rotation, isFrontCamera, mYUVQueue); | ||
recordUtil.start(); | ||
} | ||
|
||
private void endRecord() { | ||
if (recordUtil != null) { | ||
recordUtil.stop(); | ||
recordUtil = null; | ||
} | ||
|
||
// 生成Mp4 | ||
String commandMp4 = "ffmpeg -i " + videoPath + " -vcodec copy -f mp4 /storage/emulated/0/1/test.mp4"; | ||
// 生成aac | ||
String commandAAC = "ffmpeg -f s16le -ar 44100 -ac 1 -i " + audioPath + " -acodec libfdk_aac -b:a 64000 -y /storage/emulated/0/1/test.m4a"; | ||
// 合成文件 | ||
String commandConvert = "ffmpeg -i /storage/emulated/0/1/test.mp4 -i /storage/emulated/0/1/test.m4a -vcodec copy -acodec copy /storage/emulated/0/1/output.mp4"; | ||
|
||
new Thread(() -> { | ||
Log.d("TAG", "转换开始时间:" + TimeUtils.date2String(new Date())); | ||
RxFFmpegInvoke.getInstance().runFFmpegCmd(commandMp4.split(" ")); | ||
RxFFmpegInvoke.getInstance().runFFmpegCmd(commandAAC.split(" ")); | ||
RxFFmpegInvoke.getInstance().runFFmpegCmd(commandConvert.split(" ")); | ||
Log.d("TAG", "转换结束时间:" + TimeUtils.date2String(new Date())); | ||
}).start(); | ||
} | ||
} |
Oops, something went wrong.