Как уменьшить задержку потоковой передачи RTP с помощью ffmpeg в Android?
Я создаю приложение, которое выполняет потоковую передачу RTP. Он использует пару каналов ParcelFileDescriptor, где MediaRecorder записывает в канал, в то время как ffmpeg получает аудио из канала и отправляет по RTP.
На моем рабочем столе, используя тот же маршрутизатор Wi-Fi, я получаю поток RTP с помощью ffplay, и он имеет задержку между 5 и 10 с.
Я попытался захватить звук с помощью ffmpeg в Android, но это невозможно. Я пытался использовать ffplay -fflags nobuffer, использовать MIC в качестве источника, изменить кодировщик и т. Д.
Мне нужна минимальная возможная задержка. Как мне это сделать?
Код:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
MediaRecorder mediaRecorder;
AudioRecord record;
MediaPlayer mediaPlayer;
ParcelFileDescriptor[] pipePair;
ParcelFileDescriptor pipeRead;
ParcelFileDescriptor pipeWrite;
Process ffmpegProc;
// Requesting permission to RECORD_AUDIO
private boolean permissionsAccepted = false;
private String [] permissions = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE
};
private static final int REQUEST_PERMISSIONS = 200;
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_PERMISSIONS:
permissionsAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionsAccepted ) finish();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSIONS);
TextView hello = (TextView) findViewById(R.id.hello);
hello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "Clicado",Toast.LENGTH_SHORT)
.show();
copiarFFMpeg();
}
});
}
private void executarFFMpeg(final String[] cmd, ParcelFileDescriptor read) {
try {
ffmpegProc = Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
(new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream inStream = ffmpegProc.getInputStream();
InputStreamReader sReader = new InputStreamReader(inStream);
BufferedReader bufferedReader = new BufferedReader(sReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
Log.d("FFMPEG",line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
})).start();
(new Thread(new Runnable() {
@Override
public void run() {
byte[] buffer = new byte[8192];
int read = 0;
OutputStream ffmpegInput = ffmpegProc.getOutputStream();
FileInputStream reader = new FileInputStream(pipeRead.getFileDescriptor());
try {
while (true) {
if (reader.available()>0) {
read = reader.read(buffer);
ffmpegInput.write(buffer, 0, read);
ffmpegInput.flush();
} else {
Thread.sleep(10);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
onDestroy();
}
}
})).start();
Log.d("FFMPEG","Executado");
}
private boolean estaExecutando(Process ffmpegProc) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return ffmpegProc.isAlive();
} else {
try {
ffmpegProc.exitValue();
return false;
} catch (Exception e) {
return true;
}
}
}
private void criarMediaRecorder() {
if (pipeWrite != null) {
try {
//ffplay.exe -protocol_whitelist rtp,file,udp ..\file.sdp
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//mediaRecorder.setAudioChannels(2);
mediaRecorder.setOutputFile(pipeWrite.getFileDescriptor());
mediaRecorder.prepare();
mediaRecorder.start();
Log.d("MREC","MediaRecorder criado");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void criarPipe() {
try {
pipePair =ParcelFileDescriptor.createPipe();
} catch (IOException e) {
Log.e("PIPE","Deu zica na criação do pipe");
e.printStackTrace();
finish();
}
pipeRead = new ParcelFileDescriptor(pipePair[0]);
pipeWrite = new ParcelFileDescriptor(pipePair[1]);
}
private void copiarFFMpeg() {
FFmpeg ffmpeg = FFmpeg.getInstance(this);
try {
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
@Override
public void onStart() {
Log.d("FFMPEG","Iniciar cópia");
}
@Override
public void onFailure() {
Log.e("FFMPEG","Cópia falhou");
}
@Override
public void onSuccess() {
Log.d("FFMPEG","Cópiado com sucesso");
criarPipe();
//mediaRecorder.start();
File ffmpegBin = new File(getFilesDir().getAbsolutePath()+"/ffmpeg");
String[] cmd = new String[] {
ffmpegBin.getAbsolutePath(),
"-re",
"-thread_queue_size","4",
"-i","pipe:",
//"-c:a","libx264",
//"-preset","veryfast",
//"-tune","zerolatency",
"-f",
"rtp",
"rtp://192.168.0.33:1234"
};
executarFFMpeg(cmd, pipeRead);
criarMediaRecorder();
}
@Override
public void onFinish() {
}
});
} catch (FFmpegNotSupportedException e) {
// Handle if FFmpeg is not supported by device
Log.e("FFMPEG", "Não sou suportado :/");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onClick(View view) {
}
}
Команда:
ffplay rtp://192.168.0.33:1234
ffplay -fflags nobuffer rtp://192.168.0.33:1234