[+] android github action

This commit is contained in:
acgist
2023-04-15 15:23:54 +08:00
parent 24d849cde0
commit 6a21e3668c
10 changed files with 147 additions and 123 deletions

View File

@@ -5,7 +5,7 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
taoyao-snail-server: taoyao-signal-server:
name: Build taoyao signal server name: Build taoyao signal server
strategy: strategy:
matrix: matrix:
@@ -13,9 +13,9 @@ jobs:
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
java-version: 17 java-version: 17
- name: Build with Maven - name: Build with Maven
@@ -61,4 +61,24 @@ jobs:
# 不能直接运行 # 不能直接运行
# npm run dev # npm run dev
working-directory: ./taoyao-client-media working-directory: ./taoyao-client-media
taoyao-client-android:
name: Build taoyao client android
strategy:
matrix:
runs-on: [ macos-latest, ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.runs-on }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: 17
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Build with Gradle
if: runner.os != 'windows'
run: ./taoyao-client-android/taoyao/gradlew --no-daemon assembleRelease
- name: Build with Gradle
if: runner.os == 'windows'
run: ./taoyao-client-android/taoyao/gradlew.bat --no-daemon assembleRelease

View File

@@ -156,21 +156,21 @@ public final class Taoyao implements ITaoyao {
*/ */
private final Map<Long, Message> requestMessage; private final Map<Long, Message> requestMessage;
/** /**
* 循环消息Handler * 消息Handler
*/ */
private final Handler loopMessageHandler; private final Handler messageHandler;
/** /**
* 循环消息线程 * 消息线程
*/ */
private final HandlerThread loopMessageThread; private final HandlerThread messageThread;
/** /**
* 消息处理Handler * 执行Handler
*/ */
private final Handler executeMessageHandler; private final Handler executeHandler;
/** /**
* 消息处理线程 * 执行线程
*/ */
private final HandlerThread executeMessageThread; private final HandlerThread executeThread;
/** /**
* 心跳Handler * 心跳Handler
*/ */
@@ -224,15 +224,15 @@ public final class Taoyao implements ITaoyao {
this.batteryManager = context.getSystemService(BatteryManager.class); this.batteryManager = context.getSystemService(BatteryManager.class);
this.locationManager = context.getSystemService(LocationManager.class); this.locationManager = context.getSystemService(LocationManager.class);
this.requestMessage = new ConcurrentHashMap<>(); this.requestMessage = new ConcurrentHashMap<>();
this.loopMessageThread = new HandlerThread("LoopMessageThread"); this.messageThread = new HandlerThread("MessageThread");
this.loopMessageThread.setDaemon(true); this.messageThread.setDaemon(true);
this.loopMessageThread.start(); this.messageThread.start();
this.loopMessageHandler = new Handler(this.loopMessageThread.getLooper()); this.messageHandler = new Handler(this.messageThread.getLooper());
this.loopMessageHandler.post(this::loopMessage); this.messageHandler.post(this::connect);
this.executeMessageThread = new HandlerThread("ExecuteMessageThread"); this.executeThread = new HandlerThread("ExecuteThread");
this.executeMessageThread.setDaemon(true); this.executeThread.setDaemon(true);
this.executeMessageThread.start(); this.executeThread.start();
this.executeMessageHandler = new Handler(this.executeMessageThread.getLooper()); this.executeHandler = new Handler(this.executeThread.getLooper());
this.heartbeatThread = new HandlerThread("HeartbeatThread"); this.heartbeatThread = new HandlerThread("HeartbeatThread");
this.heartbeatThread.setDaemon(true); this.heartbeatThread.setDaemon(true);
this.heartbeatThread.start(); this.heartbeatThread.start();
@@ -281,26 +281,20 @@ public final class Taoyao implements ITaoyao {
// socket.setSoTimeout(this.timeout); // socket.setSoTimeout(this.timeout);
this.socket.connect(new InetSocketAddress(this.host, this.port), this.timeout); this.socket.connect(new InetSocketAddress(this.host, this.port), this.timeout);
if (this.socket.isConnected()) { if (this.socket.isConnected()) {
this.connect = true;
this.input = this.socket.getInputStream(); this.input = this.socket.getInputStream();
this.output = this.socket.getOutputStream(); this.output = this.socket.getOutputStream();
this.clientRegister(); this.clientRegister();
this.connect = true;
this.taoyaoListener.onConnected(); this.taoyaoListener.onConnected();
synchronized (this) { this.messageHandler.post(this::pull);
this.notifyAll();
}
} else { } else {
this.connect = false; this.connect = false;
synchronized (this) { this.messageHandler.postDelayed(this::connect, this.timeout);
try {
this.wait(this.timeout);
} catch (InterruptedException e) {
Log.d(Taoyao.class.getSimpleName(), "信令等待异常", e);
}
}
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(Taoyao.class.getSimpleName(), "连接信令异常:" + this.host + ":" + this.port, e); Log.e(Taoyao.class.getSimpleName(), "连接信令异常:" + this.host + ":" + this.port, e);
this.connect = false;
this.messageHandler.postDelayed(this::connect, this.timeout);
} }
return this.connect; return this.connect;
} }
@@ -308,17 +302,13 @@ public final class Taoyao implements ITaoyao {
/** /**
* 循环读取信令消息 * 循环读取信令消息
*/ */
private void loopMessage() { private void pull() {
int length = 0; int length = 0;
short messageLength = 0; short messageLength = 0;
final byte[] bytes = new byte[1024]; final byte[] bytes = new byte[1024];
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (!this.close) { while (!this.close && this.connect) {
try { try {
// 重连
while (!this.close && !this.connect) {
this.connect();
}
// 读取 // 读取
while (!this.close && (length = this.input.read(bytes)) >= 0) { while (!this.close && (length = this.input.read(bytes)) >= 0) {
buffer.put(bytes, 0, length); buffer.put(bytes, 0, length);
@@ -362,6 +352,9 @@ public final class Taoyao implements ITaoyao {
this.disconnect(); this.disconnect();
} }
} }
if (!this.close && !this.connect) {
this.messageHandler.post(this::connect);
}
} }
/** /**
@@ -461,8 +454,8 @@ public final class Taoyao implements ITaoyao {
this.close = true; this.close = true;
this.disconnect(); this.disconnect();
this.heartbeatThread.quitSafely(); this.heartbeatThread.quitSafely();
this.loopMessageThread.quitSafely(); this.messageThread.quitSafely();
this.executeMessageThread.quitSafely(); this.executeThread.quitSafely();
this.rooms.values().forEach(Room::close); this.rooms.values().forEach(Room::close);
this.sessionClients.values().forEach(SessionClient::close); this.sessionClients.values().forEach(SessionClient::close);
} }
@@ -525,7 +518,7 @@ public final class Taoyao implements ITaoyao {
request.notifyAll(); request.notifyAll();
} }
} else { } else {
this.executeMessageHandler.post(() -> this.dispatch(content, header, message)); this.executeHandler.post(() -> this.dispatch(content, header, message));
} }
} }

View File

@@ -8,6 +8,7 @@ import android.media.projection.MediaProjection;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
import com.acgist.taoyao.media.client.PhotographClient; import com.acgist.taoyao.media.client.PhotographClient;
import com.acgist.taoyao.media.client.RecordClient; import com.acgist.taoyao.media.client.RecordClient;
@@ -87,7 +88,13 @@ public final class MediaManager {
* 视频质量 * 视频质量
*/ */
private String videoQuantity; private String videoQuantity;
/**
* 通道数量
*/
private int channelCount; private int channelCount;
/**
* 关键帧频率
*/
private int iFrameInterval; private int iFrameInterval;
/** /**
* 视频来源类型 * 视频来源类型
@@ -220,10 +227,13 @@ public final class MediaManager {
} }
/** /**
* @return 是否可用 * @return 是否可用:所有配置加载完成
*/ */
public boolean available() { public boolean available() {
return this.mainHandler != null && this.context != null && this.taoyao != null; return
this.taoyao != null &&
this.context != null &&
this.mediaProperties != null;
} }
/** /**
@@ -265,7 +275,8 @@ public final class MediaManager {
*/ */
public PeerConnectionFactory newClient(VideoSourceType videoSourceType) { public PeerConnectionFactory newClient(VideoSourceType videoSourceType) {
synchronized (this) { synchronized (this) {
while(this.mediaProperties == null) { while(!this.available()) {
Log.i(MediaManager.class.getSimpleName(), "等待配置");
try { try {
this.wait(1000); this.wait(1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -409,14 +420,14 @@ public final class MediaManager {
// 高音过滤 // 高音过滤
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true")); mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAudioMirroring", "false")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAudioMirroring", "false"));
// 自动增益 // 自动增益AGC
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true")); mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl2", "true")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl2", "true"));
// 回声消除 // 回声消除AEC
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true")); mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation2", "true")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation2", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googDAEchoCancellation", "true")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googDAEchoCancellation", "true"));
// 噪音处理 // 噪音处理NS
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true")); mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression2", "true")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression2", "true"));
// mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true")); // mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true"));
@@ -545,11 +556,7 @@ public final class MediaManager {
public void updateVideoConfig(MediaVideoProperties mediaVideoProperties) { public void updateVideoConfig(MediaVideoProperties mediaVideoProperties) {
this.mediaVideoProperties = mediaVideoProperties; this.mediaVideoProperties = mediaVideoProperties;
if(this.videoCapturer != null) { if(this.videoCapturer != null) {
try { this.stopCapture("次码流", this.videoCapturer);
this.videoCapturer.stopCapture();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "暂停视频捕获异常", e);
}
this.videoCapturer.startCapture(this.mediaVideoProperties.getWidth(), this.mediaVideoProperties.getHeight(), this.mediaVideoProperties.getFrameRate()); this.videoCapturer.startCapture(this.mediaVideoProperties.getWidth(), this.mediaVideoProperties.getHeight(), this.mediaVideoProperties.getFrameRate());
} }
} }
@@ -615,11 +622,7 @@ public final class MediaManager {
} }
this.shareClientCount--; this.shareClientCount--;
if(this.shareClientCount <= 0) { if(this.shareClientCount <= 0) {
try { this.stopCapture("次码流", this.videoCapturer);
this.videoCapturer.stopCapture();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "关闭视频捕获(次码流)异常", e);
}
} }
} }
} }
@@ -627,24 +630,18 @@ public final class MediaManager {
public String photograph() { public String photograph() {
synchronized (this) { synchronized (this) {
String filepath; String filepath;
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
final PhotographClient photographClient = new PhotographClient(this.imageQuantity, this.imagePath);
if(this.clientCount <= 0) { if(this.clientCount <= 0) {
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
final PhotographClient photographClient = new PhotographClient(this.imageQuantity, this.imagePath);
filepath = photographClient.photograph(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), VideoSourceType.BACK, this.context); filepath = photographClient.photograph(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), VideoSourceType.BACK, this.context);
return filepath; } else if(this.recordClient != null) {
} this.photographClient = photographClient;
this.photographClient = new PhotographClient(this.imageQuantity, this.imagePath);
if(this.recordClient == null) {
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
this.recordVideoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
filepath = this.photographClient.waitForPhotograph(); filepath = this.photographClient.waitForPhotograph();
try {
this.recordVideoCapturer.stopCapture();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "关闭视频捕获(主码流)异常", e);
}
} else { } else {
this.photographClient = photographClient;
this.recordVideoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), PhotographClient.CAPTURER_SIZE);
filepath = this.photographClient.waitForPhotograph(); filepath = this.photographClient.waitForPhotograph();
this.stopCapture("主码流", this.recordVideoCapturer);
} }
this.photographClient = null; this.photographClient = null;
return filepath; return filepath;
@@ -663,9 +660,8 @@ public final class MediaManager {
mediaVideoProperties.getBitrate(), mediaVideoProperties.getFrameRate(), this.iFrameInterval, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getBitrate(), mediaVideoProperties.getFrameRate(), this.iFrameInterval, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(),
this.videoPath, this.taoyao, this.mainHandler this.videoPath, this.taoyao, this.mainHandler
); );
this.newClient(VideoSourceType.BACK);
this.recordVideoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
this.recordClient.start(); this.recordClient.start();
this.recordVideoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
return this.recordClient; return this.recordClient;
} }
} }
@@ -677,16 +673,22 @@ public final class MediaManager {
} else { } else {
this.recordClient.close(); this.recordClient.close();
this.recordClient = null; this.recordClient = null;
try { this.stopCapture("主码流", this.recordVideoCapturer);
this.recordVideoCapturer.stopCapture();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "关闭视频捕获(主码流)异常", e);
}
this.closeClient();
} }
} }
} }
private void stopCapture(String name, VideoCapturer videoCapturer) {
if(this.videoCapturer == null) {
return;
}
try {
videoCapturer.stopCapture();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "关闭视频捕获异常:" + name, e);
}
}
/** /**
* @param flag Config.WHAT_* * @param flag Config.WHAT_*
* @param videoTrack 视频媒体流Track * @param videoTrack 视频媒体流Track
@@ -703,12 +705,10 @@ public final class MediaManager {
surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL); surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
// 硬件拉伸 // 硬件拉伸
surfaceViewRenderer.setEnableHardwareScaler(true); surfaceViewRenderer.setEnableHardwareScaler(true);
// surfaceViewRenderer.setFpsReduction();
// 加载OpenSL ES // 加载OpenSL ES
surfaceViewRenderer.init(this.shareEglContext, null); surfaceViewRenderer.init(this.shareEglContext, null);
// 强制播放 // 添加播放
if(!videoTrack.enabled()) {
videoTrack.setEnabled(true);
}
videoTrack.addSink(surfaceViewRenderer); videoTrack.addSink(surfaceViewRenderer);
}); });
// 页面加载 // 页面加载

View File

@@ -62,10 +62,9 @@ public class LocalClient extends RoomClient {
return; return;
} }
ListUtils.getOnlyOne(this.mediaStream.videoTracks, videoTrack -> { ListUtils.getOnlyOne(this.mediaStream.videoTracks, videoTrack -> {
videoTrack.setEnabled(true);
if(this.surfaceViewRenderer == null) { if(this.surfaceViewRenderer == null) {
this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_LOCAL_VIDEO, videoTrack); this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_LOCAL_VIDEO, videoTrack);
} else {
videoTrack.setEnabled(true);
} }
return videoTrack; return videoTrack;
}); });

View File

@@ -2,6 +2,7 @@ package com.acgist.taoyao.media.client;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
@@ -13,7 +14,6 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.SessionConfiguration;
import android.media.Image; import android.media.Image;
@@ -36,7 +36,6 @@ import java.nio.ByteBuffer;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
/** /**
* 拍照终端 * 拍照终端
@@ -45,6 +44,8 @@ import java.util.concurrent.TimeUnit;
*/ */
public class PhotographClient { public class PhotographClient {
public static final int CAPTURER_SIZE = 1;
private final int quantity; private final int quantity;
private final String filename; private final String filename;
private final String filepath; private final String filepath;
@@ -53,7 +54,6 @@ public class PhotographClient {
private Surface surface; private Surface surface;
private ImageReader imageReader; private ImageReader imageReader;
private CameraDevice cameraDevice; private CameraDevice cameraDevice;
private CameraManager cameraManager;
private CameraCaptureSession cameraCaptureSession; private CameraCaptureSession cameraCaptureSession;
public PhotographClient(int quantity, String path) { public PhotographClient(int quantity, String path) {
@@ -65,8 +65,6 @@ public class PhotographClient {
Log.i(RecordClient.class.getSimpleName(), "拍摄照片文件:" + this.filepath); Log.i(RecordClient.class.getSimpleName(), "拍摄照片文件:" + this.filepath);
} }
// ================ 拉流拍照 ================ //
public String photograph(VideoFrame videoFrame) { public String photograph(VideoFrame videoFrame) {
if(this.wait) { if(this.wait) {
this.wait = false; this.wait = false;
@@ -153,17 +151,31 @@ public class PhotographClient {
return new YuvImage(nv21, ImageFormat.NV21, width, height, null); return new YuvImage(nv21, ImageFormat.NV21, width, height, null);
} }
// ================ Camera2拍照 ================ //
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
public String photograph(int width, int height, VideoSourceType type, Context context) { public String photograph(int width, int height, VideoSourceType type, Context context) {
this.cameraManager = context.getSystemService(CameraManager.class); final CameraManager cameraManager = context.getSystemService(CameraManager.class);
this.imageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1); this.imageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, PhotographClient.CAPTURER_SIZE);
this.surface = this.imageReader.getSurface(); this.surface = this.imageReader.getSurface();
this.imageReader.setOnImageAvailableListener(this.imageAvailableListener, null); this.imageReader.setOnImageAvailableListener(this.imageAvailableListener, null);
try { try {
final String cameraId = String.valueOf(type == VideoSourceType.BACK ? CameraCharacteristics.LENS_FACING_BACK : CameraCharacteristics.LENS_FACING_FRONT); String cameraId = null;
this.cameraManager.openCamera(cameraId, this.cameraDeviceStateCallback, null); final String[] cameraIdList = cameraManager.getCameraIdList();
for (String id : cameraIdList) {
final CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
if(cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK && type == VideoSourceType.BACK) {
cameraId = id;
break;
} else if(cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT && type == VideoSourceType.FRONT) {
cameraId = id;
break;
} else {
}
}
if(cameraId == null) {
PhotographClient.this.closeCamera();
return null;
}
cameraManager.openCamera(cameraId, this.cameraDeviceStateCallback, null);
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
Log.e(PhotographClient.class.getSimpleName(), "拍照异常", e); Log.e(PhotographClient.class.getSimpleName(), "拍照异常", e);
PhotographClient.this.closeCamera(); PhotographClient.this.closeCamera();
@@ -171,25 +183,22 @@ public class PhotographClient {
return this.filepath; return this.filepath;
} }
private ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { private ImageReader.OnImageAvailableListener imageAvailableListener = (ImageReader imageReader) -> {
@Override final Image image = imageReader.acquireLatestImage();
public void onImageAvailable(ImageReader imageReader) { final Image.Plane[] planes = image.getPlanes();
final Image image = imageReader.acquireLatestImage(); final ByteBuffer byteBuffer = planes[0].getBuffer();
final ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer(); final byte[] bytes = new byte[byteBuffer.remaining()];
final byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes);
byteBuffer.get(bytes); final File file = new File(PhotographClient.this.filepath);
final File file = new File(PhotographClient.this.filepath); try (
try ( final OutputStream output = new FileOutputStream(file);
final OutputStream output = new FileOutputStream(file); ) {
) { output.write(bytes,0,bytes.length);
output.write(bytes,0,bytes.length); } catch (IOException e) {
} catch (IOException e) { Log.e(PhotographClient.class.getSimpleName(), "拍照异常", e);
Log.e(PhotographClient.class.getSimpleName(), "拍照异常", e); } finally {
PhotographClient.this.closeCamera(); image.close();
} finally { PhotographClient.this.closeCamera();
image.close();
PhotographClient.this.closeCamera();
}
} }
}; };

View File

@@ -1,5 +1,6 @@
package com.acgist.taoyao.media.client; package com.acgist.taoyao.media.client;
import android.media.AudioFormat;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
import android.media.MediaFormat; import android.media.MediaFormat;
@@ -152,6 +153,7 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
} }
Log.i(RecordClient.class.getSimpleName(), "录制视频文件:" + this.filepath); Log.i(RecordClient.class.getSimpleName(), "录制视频文件:" + this.filepath);
super.init(); super.init();
this.mediaManager.newClient(VideoSourceType.BACK);
if ( if (
this.audioThread == null || !this.audioThread.isAlive() || this.audioThread == null || !this.audioThread.isAlive() ||
this.videoThread == null || !this.videoThread.isAlive() this.videoThread == null || !this.videoThread.isAlive()
@@ -173,8 +175,10 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
// audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, this.sampleRate); // audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, this.sampleRate);
// audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, this.channelCount); // audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, this.channelCount);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, this.audioBitRate); audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, this.audioBitRate);
// audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, AudioFormat.ENCODING_PCM_16BIT);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
// audioFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR); // audioFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
// audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024 * 8 * 8); // 设置缓冲大小
this.audioCodec = MediaCodec.createEncoderByType(audioType); this.audioCodec = MediaCodec.createEncoderByType(audioType);
this.audioCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); this.audioCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (Exception e) { } catch (Exception e) {
@@ -449,6 +453,7 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
file.delete(); file.delete();
} }
this.notifyAll(); this.notifyAll();
this.mediaManager.closeClient();
} }
} }

View File

@@ -54,10 +54,9 @@ public class RemoteClient extends RoomClient {
.map(v -> (VideoTrack) v) .map(v -> (VideoTrack) v)
.collect(Collectors.toList()), .collect(Collectors.toList()),
videoTrack -> { videoTrack -> {
videoTrack.setEnabled(true);
if(this.surfaceViewRenderer == null) { if(this.surfaceViewRenderer == null) {
this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_REMOTE_VIDEO, videoTrack); this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_REMOTE_VIDEO, videoTrack);
} else {
videoTrack.setEnabled(true);
} }
return videoTrack; return videoTrack;
} }

View File

@@ -233,10 +233,9 @@ public class SessionClient extends Client {
return; return;
} }
ListUtils.getOnlyOne(this.remoteMediaStream.videoTracks, videoTrack -> { ListUtils.getOnlyOne(this.remoteMediaStream.videoTracks, videoTrack -> {
videoTrack.setEnabled(true);
if(this.surfaceViewRenderer == null) { if(this.surfaceViewRenderer == null) {
this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_REMOTE_VIDEO, videoTrack); this.surfaceViewRenderer = this.mediaManager.buildSurfaceViewRenderer(Config.WHAT_NEW_REMOTE_VIDEO, videoTrack);
} else {
videoTrack.setEnabled(true);
} }
return videoTrack; return videoTrack;
}); });

View File

@@ -69,3 +69,8 @@ make -C worker
* [Kurento](https://github.com/Kurento/kurento-media-server) * [Kurento](https://github.com/Kurento/kurento-media-server)
* [Medooze](https://github.com/medooze/media-server) * [Medooze](https://github.com/medooze/media-server)
* [Mediasoup](https://github.com/versatica/mediasoup) * [Mediasoup](https://github.com/versatica/mediasoup)
## 协议
* https://www.ortc.org
* https://www.webrtc.org

View File

@@ -12,8 +12,3 @@
## 信令格式 ## 信令格式
[信令格式](https://localhost:8888/protocol/list) [信令格式](https://localhost:8888/protocol/list)
## 协议
* https://www.ortc.org
* https://www.webrtc.org