加入VLC改善拉流失败的问题(不行)

yt_mpremote
liuguijing 2 months ago
parent 8a68778555
commit 26f12089a2

@ -89,7 +89,7 @@ dependencies {
implementation 'dev.mobile:dadb:1.2.8'
implementation files('libs/common-release.aar')
// implementation project(path: ':dadb')
implementation 'org.videolan.android:libvlc-all:3.4.5'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

@ -2,7 +2,6 @@ package com.xypower.mpremote;
import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.media3.common.MediaItem;
import androidx.media3.common.PlaybackException;
@ -18,26 +17,25 @@ import androidx.media3.ui.PlayerView;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Toast;
import com.xypower.common.FilesUtils;
import com.xypower.mpremote.databinding.ActivityMainBinding;
import com.xypower.mpremote.databinding.ActivityStreamBinding;
import com.xypower.mpremote.utils.AdbUtils;
import com.xypower.mpremote.zlmediakit.ZLMediaKit;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
import java.util.ArrayList;
import dadb.AdbKeyPair;
import dadb.AdbShellResponse;
import dadb.Dadb;
@ -47,9 +45,9 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private static final String TAG = "STRM";
private static final int MAX_RETRIES = 5;
private ExoPlayer exoPlayer;
private ExoPlayer player;
private String mDeviceIp;
private Handler mHandler;
private Handler retryHandler = new Handler();
@NonNull
private com.xypower.mpremote.databinding.ActivityStreamBinding binding;
private String localIp;
@ -58,8 +56,18 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private int rotation;
private int netCamera;
private int vendor;
private PlayerView playerView;
private int retryCount;
private static final long INITIAL_RETRY_DELAY_MS = 1000; // 初始重试延迟
private static final long MAX_RETRY_DELAY_MS = 15000; // 最大重试延迟
private static final float RETRY_BACKOFF_MULTIPLIER = 1.5f; // 退避乘数
private long currentRetryDelay = INITIAL_RETRY_DELAY_MS;
private boolean isBuffering = false;
private long bufferingStartTime = 0;
private static final long BUFFERING_TIMEOUT_MS = 10000; // 10秒缓冲超时
private String RTMP_URL;
private SurfaceView playerView;
private MediaPlayer mediaPlayer;
private LibVLC libVLC;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -73,7 +81,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
}
private void initEvent() {
String url = "rtmp://" + mDeviceIp + "/live/0";
RTMP_URL = "rtmp://" + mDeviceIp + "/live/0";
String cmd = "am start -n com.xypower.mplive/com.xypower.mplive.MainActivity"
+ " --ei cameraId " + Integer.toString(cameraId)
@ -81,7 +89,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
+ " --ei netCamera " + Integer.toString(netCamera)
+ " --ei vendor " + Integer.toString(vendor)
+ " --ei autoStart 1"
+ " --es url \"" + url + "\"";
+ " --es url \"" + RTMP_URL + "\"";
Runnable runnable = new Runnable() {
@Override
public void run() {
@ -89,10 +97,22 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
}
};
startStreaming(cmd, runnable);
}
private void initHandler() {
mHandler = new Handler();
// mHandler = new Handler();
// 配置 VLC 参数
ArrayList<String> options = new ArrayList<>();
options.add("--aout=opensles"); // 优化音频延迟‌:ml-citation{ref="6" data="citationList"}
options.add("--audio-time-stretch");
libVLC = new LibVLC(this, options);
mediaPlayer = new MediaPlayer(libVLC);
}
private void initIntent() {
@ -111,6 +131,25 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
binding.toolbar.back.setOnClickListener(this);
binding.toolbar.refresh.setOnClickListener(this);
playerView = binding.playerView;
SurfaceHolder holder = playerView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
Surface surface = holder.getSurface();
mediaPlayer.getVLCVout().setVideoSurface(surface,holder);
mediaPlayer.getVLCVout().attachViews(); // 绑定视图‌:ml-citation{ref="8" data="citationList"}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
}
});
}
private void startStreaming(final String cmd, final Runnable runnable) {
@ -124,7 +163,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
adb = AdbManager.getAdb(mDeviceIp, AdbUtils.DEFAULT_ADB_PORT);
Log.i(TAG, "Finish connecting " + mDeviceIp);
if (adb == null) {
mHandler.postDelayed(new Runnable() {
retryHandler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(StreamActivity.this, R.string.err_dev_failed_to_connect, Toast.LENGTH_LONG).show();
@ -147,7 +186,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
if (adbShellResponse != null) {
if (adbShellResponse.getExitCode() == 0) {
try {
Thread.sleep(100);
Thread.sleep(5000);
} catch (Exception ex) {
}
runOnUiThread(runnable);
@ -201,7 +240,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
th.start();
}
private void initializePlayer() {
private void initializePlayer2() {
// // 创建重试策略
// DefaultLoadControl loadControl = new DefaultLoadControl.Builder()
// .setBufferDurationsMs(
@ -210,52 +249,154 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
// 500, // bufferForPlaybackMs
// 500 // bufferForPlaybackAfterRebufferMs
// ).build();
exoPlayer = new ExoPlayer.Builder(this).build();
playerView.setPlayer(exoPlayer);
exoPlayer.addListener(new Player.Listener() {
@Override
public void onPlayerError(PlaybackException error) {
Log.e(TAG, "播放错误: " + error.getMessage());
// player = new ExoPlayer.Builder(this).build();
// playerView.setPlayer(player);
// player.addListener(new Player.Listener() {
// @Override
// public void onPlayerError(PlaybackException error) {
// Log.e(TAG, "播放错误: " + error.getMessage());
// handlePlaybackError();
// }
//
// @Override
// public void onPlaybackStateChanged(int state) {
// if (state == Player.STATE_READY) {
// // 重置重试计数器当成功连接时
// retryCount = 0;
// }
// }
// });
// startPlayback();
}
// private void handlePlaybackError2() {
// if (retryCount < MAX_RETRIES) {
// retryCount++;
// Log.d(TAG, "准备重试 (" + retryCount + "/" + MAX_RETRIES + ")...");
// mHandler.postDelayed(() -> {
// if (player != null) {
// startPlayback();
// }
// }, 3000);
// } else {
// Log.e(TAG, "达到最大重试次数,停止尝试");
// // 这里可以添加UI提示或执行其他错误处理
// }
// }
private void startPlayback2() {
if (player == null) return;
Log.d(TAG, "开始播放,重试次数: " + retryCount);
MediaItem mediaItem = MediaItem.fromUri("rtmp://" + mDeviceIp + "/live/0");
ProgressiveMediaSource videoSource = new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory())
.createMediaSource(mediaItem);
player.setMediaSource(videoSource);
player.prepare();
player.setPlayWhenReady(true);
}
private void initializePlayer() {
// if (player == null) {
// player = new ExoPlayer.Builder(this)
// .setLoadControl(new DefaultLoadControl.Builder()
// .setBufferDurationsMs(
// 5000, // minBufferMs
// 10000, // maxBufferMs
// 500, // bufferForPlaybackMs
// 500 // bufferForPlaybackAfterRebufferMs
// ).setPrioritizeTimeOverSizeThresholds(true)
// .build())
// .build();
//
//// playerView.setPlayer(player);
//
// player.addListener(new Player.Listener() {
// @Override
// public void onPlayerError(PlaybackException error) {
// Log.e(TAG, "播放错误: " + error.getMessage());
// handlePlaybackError();
// }
//
// @Override
// public void onPlaybackStateChanged(int state) {
// if (state == Player.STATE_BUFFERING) {
// if (!isBuffering) {
// isBuffering = true;
// bufferingStartTime = System.currentTimeMillis();
// // 启动缓冲超时检查
// retryHandler.postDelayed(bufferingTimeoutRunnable, BUFFERING_TIMEOUT_MS);
// }
// } else if (state == Player.STATE_READY) {
// isBuffering = false;
// retryHandler.removeCallbacks(bufferingTimeoutRunnable);
// retryCount = 0;
// currentRetryDelay = INITIAL_RETRY_DELAY_MS;
// Log.d(TAG, "播放器准备就绪");
// }
// }
// });
// }
// startPlayback();
Media media = new Media(libVLC, Uri.parse(RTMP_URL));
mediaPlayer.setMedia(media);
mediaPlayer.play();
}
private Runnable bufferingTimeoutRunnable = new Runnable() {
@Override
public void run() {
if (isBuffering) {
Log.w(TAG, "缓冲超时,触发重连");
handlePlaybackError();
}
}
};
@Override
public void onPlaybackStateChanged(int state) {
if (state == Player.STATE_READY) {
// 重置重试计数器当成功连接时
retryCount = 0;
}
}
});
startPlayback();
private void startPlayback() {
if (player == null) return;
Log.d(TAG, "开始播放,重试次数: " + retryCount);
// MediaItem mediaItem = MediaItem.fromUri(Uri.parse(RTMP_URL));
MediaItem mediaItem = MediaItem.fromUri(RTMP_URL);
ProgressiveMediaSource videoSource = new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory())
.createMediaSource(mediaItem);
player.setMediaSource(videoSource);
// player.setMediaItem(mediaItem);
player.prepare();
player.setPlayWhenReady(true);
}
private void handlePlaybackError() {
releasePlayerInternal();
if (retryCount < MAX_RETRIES) {
retryCount++;
Log.d(TAG, "准备重试 (" + retryCount + "/" + MAX_RETRIES + ")...");
mHandler.postDelayed(() -> {
if (exoPlayer != null) {
startPlayback();
}
}, 3000);
} else {
Log.e(TAG, "达到最大重试次数,停止尝试");
// 这里可以添加UI提示或执行其他错误处理
Log.d(TAG, "准备重试 (" + retryCount + "), 延迟: " + currentRetryDelay + "ms");
retryHandler.postDelayed(() -> {
initializePlayer();
// 增加下次重试的延迟时间(使用退避算法)
currentRetryDelay = Math.min((long)(currentRetryDelay * RETRY_BACKOFF_MULTIPLIER), MAX_RETRY_DELAY_MS);
}, currentRetryDelay);
}
}
private void startPlayback() {
if (exoPlayer == null) return;
Log.d(TAG, "开始播放,重试次数: " + retryCount);
MediaItem mediaItem = MediaItem.fromUri("rtmp://" + mDeviceIp + "/live/0");
ProgressiveMediaSource videoSource = new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory())
.createMediaSource(mediaItem);
exoPlayer.setMediaSource(videoSource);
exoPlayer.prepare();
exoPlayer.setPlayWhenReady(true);
private void releasePlayerInternal() {
if (player != null) {
player.release();
player = null;
isBuffering = false;
retryHandler.removeCallbacks(bufferingTimeoutRunnable);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
@ -281,9 +422,9 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
}
private void releasePlayer() {
if (exoPlayer != null) {
exoPlayer.release();
exoPlayer = null;
if (player != null) {
player.release();
player = null;
}
}

@ -11,17 +11,24 @@
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<androidx.media3.ui.PlayerView
<SurfaceView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:resize_mode="fit"
app:show_buffering="always"
app:show_timeout="5000"
app:use_controller="false"
tools:ignore="MissingConstraints" />
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
<!-- <androidx.media3.ui.PlayerView-->
<!-- android:id="@+id/playerView"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="0dip"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/toolbar"-->
<!-- app:resize_mode="fit"-->
<!-- app:show_buffering="always"-->
<!-- app:show_timeout="5000"-->
<!-- app:use_controller="false"-->
<!-- tools:ignore="MissingConstraints" />-->
<!-- <ImageButton-->

Loading…
Cancel
Save