[*] 兼容安卓9、解决录像浏览器无法播放问题
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
* 过时方法
|
* 过时方法
|
||||||
* 缩进调整:`tab->space`
|
* 缩进调整:`tab->space`
|
||||||
* 方法调整:`getter/setter`
|
* 方法改造:`getter/setter`
|
||||||
* 注释优化:详细描述、单位描述
|
* 注释优化:详细描述、单位描述
|
||||||
* 日志优化:
|
* 日志优化:
|
||||||
* 添加测试:
|
* 添加测试:
|
||||||
@@ -30,13 +30,17 @@
|
|||||||
|
|
||||||
## 计划任务
|
## 计划任务
|
||||||
|
|
||||||
|
* 发布版本:1.0.0
|
||||||
* 开机自启
|
* 开机自启
|
||||||
* 录制底噪
|
* 录制底噪
|
||||||
|
* 性能测试
|
||||||
|
* 稳定性测试
|
||||||
* 分辨率调整
|
* 分辨率调整
|
||||||
* 服务端录制
|
* 服务端录制
|
||||||
* 安卓预览按钮
|
* 安卓预览按钮
|
||||||
* 降低视频录制大小
|
* 降低视频录制大小
|
||||||
* 防止重复邀请拉取
|
* 防止重复邀请拉取
|
||||||
|
* 码率等等参数配置验证
|
||||||
* 查询消费者生产者信息
|
* 查询消费者生产者信息
|
||||||
* 浏览器WebRTC监控页面关闭:`chrome://webrtc-internals/`
|
* 浏览器WebRTC监控页面关闭:`chrome://webrtc-internals/`
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# 桃夭安卓
|
# 桃夭安卓
|
||||||
|
|
||||||
|
## 版本
|
||||||
|
|
||||||
|
* SDK:28~32
|
||||||
|
* Andoird:9~12
|
||||||
|
|
||||||
## WebRTC源码修改
|
## WebRTC源码修改
|
||||||
|
|
||||||
### JavaAudioDeviceModule
|
### JavaAudioDeviceModule
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ android {
|
|||||||
namespace 'com.acgist.client.boot'
|
namespace 'com.acgist.client.boot'
|
||||||
compileSdk 32
|
compileSdk 32
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 30
|
minSdk 28
|
||||||
targetSdk 32
|
targetSdk 32
|
||||||
versionCode 100
|
versionCode 100
|
||||||
versionName "1.0.0"
|
versionName "1.0.0"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ android {
|
|||||||
namespace 'com.acgist.taoyao.client'
|
namespace 'com.acgist.taoyao.client'
|
||||||
compileSdk 32
|
compileSdk 32
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 30
|
minSdk 28
|
||||||
targetSdk 32
|
targetSdk 32
|
||||||
versionCode 100
|
versionCode 100
|
||||||
versionName "1.0.0"
|
versionName "1.0.0"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.location.LocationManager;
|
|||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
@@ -1254,7 +1255,15 @@ public final class Taoyao implements ITaoyao {
|
|||||||
if(wifiInfo == null) {
|
if(wifiInfo == null) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return this.wifiManager.calculateSignalLevel(wifiInfo.getRssi()) / this.wifiManager.getMaxSignalLevel() * 100;
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
return this.wifiManager.calculateSignalLevel(wifiInfo.getRssi()) / this.wifiManager.getMaxSignalLevel() * 100;
|
||||||
|
} else {
|
||||||
|
// 0 ~ -50 : 优秀
|
||||||
|
// -50 ~ -70 : 良好
|
||||||
|
// -70 ~ -100: 较差
|
||||||
|
final int rssi = wifiInfo.getRssi();
|
||||||
|
return rssi <= -100 ? 0 : (100 + rssi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ android {
|
|||||||
compileSdk 32
|
compileSdk 32
|
||||||
ndkVersion "23.1.7779620"
|
ndkVersion "23.1.7779620"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 30
|
minSdk 28
|
||||||
targetSdk 32
|
targetSdk 32
|
||||||
versionCode 100
|
versionCode 100
|
||||||
versionName "1.0.0"
|
versionName "1.0.0"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.media.MediaMuxer;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.acgist.taoyao.boot.utils.DateUtils;
|
import com.acgist.taoyao.boot.utils.DateUtils;
|
||||||
@@ -122,6 +123,11 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
* 视频Handler
|
* 视频Handler
|
||||||
*/
|
*/
|
||||||
private Handler videoHandler;
|
private Handler videoHandler;
|
||||||
|
/**
|
||||||
|
* 是否已经开始录制
|
||||||
|
* 不能使用多线程wait/notify录制音频没有结束
|
||||||
|
*/
|
||||||
|
private boolean start = false;
|
||||||
/**
|
/**
|
||||||
* 媒体合成器
|
* 媒体合成器
|
||||||
*/
|
*/
|
||||||
@@ -216,16 +222,16 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
if (!this.close && this.videoActive) {
|
if (!this.close && this.videoActive) {
|
||||||
Log.i(RecordClient.class.getSimpleName(), "开始录制文件:" + this.filename);
|
Log.i(RecordClient.class.getSimpleName(), "开始录制文件:" + this.filename);
|
||||||
this.mediaMuxer.start();
|
this.mediaMuxer.start();
|
||||||
this.notifyAll();
|
this.start = true;
|
||||||
} else if (!this.close) {
|
} else {
|
||||||
try {
|
|
||||||
this.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(RecordClient.class.getSimpleName(), "录制线程等待异常", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (outputIndex >= 0) {
|
} else if (outputIndex >= 0) {
|
||||||
|
if(!this.start) {
|
||||||
|
// 还没开始直接丢弃数据防止通道阻塞
|
||||||
|
this.audioCodec.releaseOutputBuffer(outputIndex, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(pts == 0L) {
|
if(pts == 0L) {
|
||||||
pts = bufferInfo.presentationTimeUs;
|
pts = bufferInfo.presentationTimeUs;
|
||||||
}
|
}
|
||||||
@@ -234,6 +240,7 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
|
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
|
||||||
bufferInfo.presentationTimeUs -= pts;
|
bufferInfo.presentationTimeUs -= pts;
|
||||||
this.mediaMuxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);
|
this.mediaMuxer.writeSampleData(trackIndex, outputBuffer, bufferInfo);
|
||||||
|
// TODO:验证第二个参数作用是否复用
|
||||||
this.audioCodec.releaseOutputBuffer(outputIndex, false);
|
this.audioCodec.releaseOutputBuffer(outputIndex, false);
|
||||||
// Log.d(RecordClient.class.getSimpleName(), "录制音频帧(时间戳):" + (bufferInfo.presentationTimeUs / 1_000_000F));
|
// Log.d(RecordClient.class.getSimpleName(), "录制音频帧(时间戳):" + (bufferInfo.presentationTimeUs / 1_000_000F));
|
||||||
// if (bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
|
// if (bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
|
||||||
@@ -298,6 +305,7 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
this.videoActive = true;
|
this.videoActive = true;
|
||||||
final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
while (!this.close) {
|
while (!this.close) {
|
||||||
|
// TODO:验证是否其他类型也要releaseOutputBuffer
|
||||||
outputIndex = this.videoCodec.dequeueOutputBuffer(bufferInfo, WAIT_TIME_US);
|
outputIndex = this.videoCodec.dequeueOutputBuffer(bufferInfo, WAIT_TIME_US);
|
||||||
if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||||
// } else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
// } else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||||
@@ -308,17 +316,16 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
if (!this.close && this.audioActive) {
|
if (!this.close && this.audioActive) {
|
||||||
Log.i(RecordClient.class.getSimpleName(), "开始录制文件:" + this.filename);
|
Log.i(RecordClient.class.getSimpleName(), "开始录制文件:" + this.filename);
|
||||||
this.mediaMuxer.start();
|
this.mediaMuxer.start();
|
||||||
this.notifyAll();
|
this.start = true;
|
||||||
} else if (!this.close) {
|
|
||||||
try {
|
|
||||||
this.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(RecordClient.class.getSimpleName(), "录制线程等待异常", e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (outputIndex >= 0) {
|
} else if (outputIndex >= 0) {
|
||||||
|
if(!this.start) {
|
||||||
|
// 还没开始直接丢弃数据防止通道阻塞
|
||||||
|
this.videoCodec.releaseOutputBuffer(outputIndex, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(pts == 0L) {
|
if(pts == 0L) {
|
||||||
pts = bufferInfo.presentationTimeUs / 1000;
|
pts = bufferInfo.presentationTimeUs / 1000;
|
||||||
}
|
}
|
||||||
@@ -396,6 +403,7 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.close();
|
super.close();
|
||||||
|
this.start = false;
|
||||||
Log.i(RecordClient.class.getSimpleName(), "结束录制:" + this.filepath);
|
Log.i(RecordClient.class.getSimpleName(), "结束录制:" + this.filepath);
|
||||||
if(this.javaAudioDeviceModule != null) {
|
if(this.javaAudioDeviceModule != null) {
|
||||||
this.javaAudioDeviceModule.removeMixerProcesser();
|
this.javaAudioDeviceModule.removeMixerProcesser();
|
||||||
@@ -422,7 +430,6 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
Log.i(RecordClient.class.getSimpleName(), "删除没有录制数据文件:" + this.filepath);
|
Log.i(RecordClient.class.getSimpleName(), "删除没有录制数据文件:" + this.filepath);
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
this.notifyAll();
|
|
||||||
this.mediaManager.closeClient();
|
this.mediaManager.closeClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,7 +447,7 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
* @param data PCM数据
|
* @param data PCM数据
|
||||||
*/
|
*/
|
||||||
public void onPcm(long pts, byte[] data) {
|
public void onPcm(long pts, byte[] data) {
|
||||||
if(this.close || !this.audioActive) {
|
if(this.close || !this.audioActive || !this.videoActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int index = this.audioCodec.dequeueInputBuffer(WAIT_TIME_US);
|
final int index = this.audioCodec.dequeueInputBuffer(WAIT_TIME_US);
|
||||||
@@ -454,7 +461,7 @@ public class RecordClient extends Client implements VideoSink {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFrame(VideoFrame videoFrame) {
|
public void onFrame(VideoFrame videoFrame) {
|
||||||
if (this.close || !this.videoActive) {
|
if (this.close || !this.audioActive || !this.videoActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Log.d(RecordClient.class.getSimpleName(), "视频信息:" + videoFrame.getRotatedWidth() + " - " + videoFrame.getRotatedHeight());
|
// Log.d(RecordClient.class.getSimpleName(), "视频信息:" + videoFrame.getRotatedWidth() + " - " + videoFrame.getRotatedHeight());
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import org.webrtc.VideoFrame;
|
import org.webrtc.VideoFrame;
|
||||||
|
|
||||||
@@ -88,7 +89,11 @@ public class WatermarkProcesser extends VideoProcesser {
|
|||||||
final boolean[][] matrix = new boolean[width][height];
|
final boolean[][] matrix = new boolean[width][height];
|
||||||
for (int j = 0; j < height; j++) {
|
for (int j = 0; j < height; j++) {
|
||||||
for (int i = 0; i < width; i++) {
|
for (int i = 0; i < width; i++) {
|
||||||
matrix[i][j] = bitmap.getColor(i, j).toArgb() != 0;
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
matrix[i][j] = bitmap.getColor(i, j).toArgb() != 0;
|
||||||
|
} else {
|
||||||
|
matrix[i][j] = bitmap.getPixel(i, j) != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MATRICES[source] = new WatermarkMatrix(width, height, matrix);
|
MATRICES[source] = new WatermarkMatrix(width, height, matrix);
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ module.exports = {
|
|||||||
clockRate: 90000,
|
clockRate: 90000,
|
||||||
parameters: {
|
parameters: {
|
||||||
"x-google-start-bitrate": 1000,
|
"x-google-start-bitrate": 1000,
|
||||||
|
// "x-google-min-bitrate": 800,
|
||||||
|
// "x-google-max-bitrate": 1600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,6 +84,8 @@ module.exports = {
|
|||||||
parameters: {
|
parameters: {
|
||||||
"profile-id": 2,
|
"profile-id": 2,
|
||||||
"x-google-start-bitrate": 1000,
|
"x-google-start-bitrate": 1000,
|
||||||
|
// "x-google-min-bitrate": 800,
|
||||||
|
// "x-google-max-bitrate": 1600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -93,6 +97,8 @@ module.exports = {
|
|||||||
"profile-level-id": "4d0032",
|
"profile-level-id": "4d0032",
|
||||||
"level-asymmetry-allowed": 1,
|
"level-asymmetry-allowed": 1,
|
||||||
"x-google-start-bitrate": 1000,
|
"x-google-start-bitrate": 1000,
|
||||||
|
// "x-google-min-bitrate": 800,
|
||||||
|
// "x-google-max-bitrate": 1600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -104,6 +110,8 @@ module.exports = {
|
|||||||
"profile-level-id": "42e01f",
|
"profile-level-id": "42e01f",
|
||||||
"level-asymmetry-allowed": 1,
|
"level-asymmetry-allowed": 1,
|
||||||
"x-google-start-bitrate": 1000,
|
"x-google-start-bitrate": 1000,
|
||||||
|
// "x-google-min-bitrate": 800,
|
||||||
|
// "x-google-max-bitrate": 1600,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ export default {
|
|||||||
.client{float:left;width:50vw;height:50vh;box-shadow:0 0 1px 0px rgba(0,0,0,0.4);}
|
.client{float:left;width:50vw;height:50vh;box-shadow:0 0 1px 0px rgba(0,0,0,0.4);}
|
||||||
.client audio{display:none;}
|
.client audio{display:none;}
|
||||||
.client video{width:100%;height:100%;}
|
.client video{width:100%;height:100%;}
|
||||||
.client .mic{background:linear-gradient(to top, var(--el-color-primary) 10%, transparent 0%);}
|
.client .mic{background:linear-gradient(to top, var(--el-color-primary) 100%, transparent 0%);}
|
||||||
.client .title{position:absolute;top:0;left:0;text-align:center;width:100%;}
|
.client .title{position:absolute;top:0;left:0;text-align:center;width:100%;}
|
||||||
.client .buttons{width:100%;bottom:0;left:0;text-align:center;position:absolute;padding:0.8rem 0;background:rgba(0,0,0,0.4);}
|
.client .buttons{width:100%;bottom:0;left:0;text-align:center;position:absolute;padding:0.8rem 0;background:rgba(0,0,0,0.4);}
|
||||||
.client .buttons .el-button{margin:0 6px;}
|
.client .buttons .el-button{margin:0 6px;}
|
||||||
|
|||||||
@@ -107,5 +107,5 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume), transparent 0%);}
|
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume, 100%), transparent 0%);}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -114,5 +114,5 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume), transparent 0%);}
|
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume, 100%), transparent 0%);}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -105,5 +105,5 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume), transparent 0%);}
|
.client .mic{background:linear-gradient(to top, var(--el-color-primary) var(--volume, 100%), transparent 0%);}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ class Session {
|
|||||||
// 远程终端名称
|
// 远程终端名称
|
||||||
name;
|
name;
|
||||||
// 音量
|
// 音量
|
||||||
volume = 100;
|
volume = "100%";
|
||||||
// 是否关闭
|
// 是否关闭
|
||||||
closed;
|
closed;
|
||||||
// 远程终端ID
|
// 远程终端ID
|
||||||
@@ -382,7 +382,7 @@ class RemoteClient {
|
|||||||
// 终端标识
|
// 终端标识
|
||||||
clientId;
|
clientId;
|
||||||
// 音量
|
// 音量
|
||||||
volume = 0;
|
volume = "100%";
|
||||||
// 代理对象
|
// 代理对象
|
||||||
proxy;
|
proxy;
|
||||||
// 数据消费者
|
// 数据消费者
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import lombok.Setter;
|
|||||||
* 视频编码 = 压缩
|
* 视频编码 = 压缩
|
||||||
* 8 = 颜色位数
|
* 8 = 颜色位数
|
||||||
* 3 / 2 = YUV | RGB
|
* 3 / 2 = YUV | RGB
|
||||||
* 码率(比特率):单位(kbps | kbit/s | kb/s)
|
* 码率(比特率):单位(kbps | kbit/s)
|
||||||
* 码率 = 文件字节大小 * 8 / 秒数 / 1000
|
* 码率 = 文件字节大小 * 8 / 秒数 / 1000
|
||||||
* 1Byte = 8bit
|
* 1Byte = 8bit
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -82,11 +82,13 @@ public class RtpTest {
|
|||||||
// {"header":{"v":"1.0.0","id":1215310510002009,"signal":"room::enter"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9"}}
|
// {"header":{"v":"1.0.0","id":1215310510002009,"signal":"room::enter"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9"}}
|
||||||
// {"header":{"v":"1.0.0","id":1215310510002010,"signal":"media::transport::plain"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","rtcpMux":false,"comedia":true}}
|
// {"header":{"v":"1.0.0","id":1215310510002010,"signal":"media::transport::plain"},"body":{"roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","rtcpMux":false,"comedia":true}}
|
||||||
// {"header":{"v":"1.0.0","id":1215375110006012,"signal":"media::produce"},"body":{"kind":"video","roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","transportId":"14dc9307-bf9c-4442-a9ad-ce6a97623ef4","appData":{},"rtpParameters":{"codecs":[{"mimeType":"video/vp8","clockRate":90000,"payloadType":102,"rtcpFeedback":[]}],"encodings":[{"ssrc":123123}]}}}
|
// {"header":{"v":"1.0.0","id":1215375110006012,"signal":"media::produce"},"body":{"kind":"video","roomId":"8260e615-3081-4bfc-96a8-574f4dd780d9","transportId":"14dc9307-bf9c-4442-a9ad-ce6a97623ef4","appData":{},"rtpParameters":{"codecs":[{"mimeType":"video/vp8","clockRate":90000,"payloadType":102,"rtcpFeedback":[]}],"encodings":[{"ssrc":123123}]}}}
|
||||||
|
// ffmpeg直接播放
|
||||||
|
// ffmpeg -re -i video.mp4 -c:a copy -vn -map 0:1 -f rtp rtp://localhost:6666 > taoyao.sdp
|
||||||
|
// ffmpeg -re -i video.mp4 -c:v copy -an -map 0:0 -f rtp rtp://localhost:6666 > taoyao.sdp
|
||||||
// ffplay -protocol_whitelist "file,udp,rtp" taoyao.sdp
|
// ffplay -protocol_whitelist "file,udp,rtp" taoyao.sdp
|
||||||
// ffmpeg -re -i video.mp4 -vcodec copy -map 0:0 -f rtp rtp://localhost:6666 > taoyao.sdp
|
|
||||||
// ffmpeg不支持rtcpMux
|
// ffmpeg不支持rtcpMux
|
||||||
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
// ffmpeg -re -i video.mp4 -c:v libvpx -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||||
// ffmpeg -re -i video.mp4 -vcodec vp8 -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
// ffmpeg -re -i video.mp4 -c:v vp8 -map 0:0 -f tee "[select=v:f=rtp:ssrc=123123:payload_type=102]rtp://192.168.1.110:40793?rtcpport=47218"
|
||||||
final Scanner scanner = new Scanner(System.in);
|
final Scanner scanner = new Scanner(System.in);
|
||||||
do {
|
do {
|
||||||
if(StringUtils.isEmpty(line)) {
|
if(StringUtils.isEmpty(line)) {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
/**
|
/**
|
||||||
* 创建RTP输入通道信令
|
* 创建RTP输入通道信令
|
||||||
* 注意:
|
* 注意:
|
||||||
* 3. ffmpeg不支持rtcpMux
|
* 1. ffmpeg不支持rtcpMux
|
||||||
* 2. comedia必须开启srtp功能
|
* 2. comedia必须开启srtp功能
|
||||||
* 1. 如果关闭comedia不会自动升级双向通道
|
* 3. 如果关闭comedia不会自动升级双向通道
|
||||||
*
|
*
|
||||||
* @author acgist
|
* @author acgist
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user