视频拉流优化

yt_mpremote
liuguijing 2 months ago
parent 26f12089a2
commit 7c51f7894d

@ -14,34 +14,27 @@ import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.ProgressiveMediaSource; import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.ui.PlayerView; import androidx.media3.ui.PlayerView;
import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import com.xypower.mpremote.databinding.ActivityStreamBinding; import com.xypower.mpremote.databinding.ActivityStreamBinding;
import com.xypower.mpremote.utils.AdbUtils; import com.xypower.mpremote.utils.AdbUtils;
import com.xypower.mpremote.utils.AlertDialogUtils;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer; import org.videolan.libvlc.MediaPlayer;
import java.util.ArrayList;
import dadb.AdbShellResponse; import dadb.AdbShellResponse;
import dadb.Dadb; import dadb.Dadb;
public class StreamActivity extends AppCompatActivity implements View.OnClickListener { public class StreamActivity extends AppCompatActivity implements View.OnClickListener {
// private PlayerView playerView;
private static final String TAG = "STRM"; private static final String TAG = "STRM";
private static final int MAX_RETRIES = 5; private static final int MAX_RETRIES = 5;
@ -65,9 +58,8 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private long bufferingStartTime = 0; private long bufferingStartTime = 0;
private static final long BUFFERING_TIMEOUT_MS = 10000; // 10秒缓冲超时 private static final long BUFFERING_TIMEOUT_MS = 10000; // 10秒缓冲超时
private String RTMP_URL; private String RTMP_URL;
private SurfaceView playerView; private PlayerView playerView;
private MediaPlayer mediaPlayer; private AlertDialog alertDialog;
private LibVLC libVLC;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -82,37 +74,19 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private void initEvent() { private void initEvent() {
RTMP_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) + " --ei rotation " + Integer.toString(rotation) + " --ei netCamera " + Integer.toString(netCamera) + " --ei vendor " + Integer.toString(vendor) + " --ei autoStart 1" + " --es url \"" + RTMP_URL + "\"";
String cmd = "am start -n com.xypower.mplive/com.xypower.mplive.MainActivity"
+ " --ei cameraId " + Integer.toString(cameraId)
+ " --ei rotation " + Integer.toString(rotation)
+ " --ei netCamera " + Integer.toString(netCamera)
+ " --ei vendor " + Integer.toString(vendor)
+ " --ei autoStart 1"
+ " --es url \"" + RTMP_URL + "\"";
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public void run() { public void run() {
initializePlayer(); initializePlayer();
} }
}; };
alertDialog = AlertDialogUtils.show(this, "视频加载中");
startStreaming(cmd, runnable); startStreaming(cmd, runnable);
} }
private void initHandler() { 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() { private void initIntent() {
@ -131,29 +105,9 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
binding.toolbar.back.setOnClickListener(this); binding.toolbar.back.setOnClickListener(this);
binding.toolbar.refresh.setOnClickListener(this); binding.toolbar.refresh.setOnClickListener(this);
playerView = binding.playerView; 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) { private void startStreaming(final String cmd, final Runnable runnable) {
Log.d(TAG, cmd);
Thread th = new Thread(new Runnable() { Thread th = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -197,12 +151,10 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
} }
} }
}); });
th.start(); th.start();
} }
private void stopStreaming() { private void stopStreaming() {
// String cmd = "am start -n com.xypower.mplive/com.xypower.mplive.MainActivity --ei autoClose 1";
String cmd = "am force-stop com.xypower.mplive"; String cmd = "am force-stop com.xypower.mplive";
Thread th = new Thread(new Runnable() { Thread th = new Thread(new Runnable() {
@Override @Override
@ -240,107 +192,42 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
th.start(); th.start();
} }
private void initializePlayer2() { private void initializePlayer() {
// // 创建重试策略 if (player == null) {
// DefaultLoadControl loadControl = new DefaultLoadControl.Builder() player = new ExoPlayer.Builder(this).setLoadControl(new DefaultLoadControl.Builder().setBufferDurationsMs(5000, // minBufferMs
// .setBufferDurationsMs( 10000, // maxBufferMs
// 5000, // minBufferMs 500, // bufferForPlaybackMs
// 10000, // maxBufferMs 500 // bufferForPlaybackAfterRebufferMs
// 500, // bufferForPlaybackMs ).setPrioritizeTimeOverSizeThresholds(true).build()).build();
// 500 // bufferForPlaybackAfterRebufferMs playerView.setPlayer(player);
// ).build(); player.addListener(new Player.Listener() {
// player = new ExoPlayer.Builder(this).build(); @Override
// playerView.setPlayer(player); public void onPlayerError(PlaybackException error) {
// player.addListener(new Player.Listener() { Log.e(TAG, "播放错误: " + error.getMessage());
// @Override handlePlaybackError();
// 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);
} }
@Override
private void initializePlayer() { public void onPlaybackStateChanged(int state) {
// if (player == null) { if (state == Player.STATE_BUFFERING) {
// player = new ExoPlayer.Builder(this) if (!isBuffering) {
// .setLoadControl(new DefaultLoadControl.Builder() isBuffering = true;
// .setBufferDurationsMs( bufferingStartTime = System.currentTimeMillis();
// 5000, // minBufferMs // 启动缓冲超时检查
// 10000, // maxBufferMs retryHandler.postDelayed(bufferingTimeoutRunnable, BUFFERING_TIMEOUT_MS);
// 500, // bufferForPlaybackMs }
// 500 // bufferForPlaybackAfterRebufferMs } else if (state == Player.STATE_READY) {
// ).setPrioritizeTimeOverSizeThresholds(true) AlertDialogUtils.dismiss(alertDialog);
// .build()) isBuffering = false;
// .build(); retryHandler.removeCallbacks(bufferingTimeoutRunnable);
// retryCount = 0;
//// playerView.setPlayer(player); currentRetryDelay = INITIAL_RETRY_DELAY_MS;
// Log.d(TAG, "播放器准备就绪");
// player.addListener(new Player.Listener() { }
// @Override }
// public void onPlayerError(PlaybackException error) { });
// Log.e(TAG, "播放错误: " + error.getMessage()); }
// handlePlaybackError(); startPlayback();
// }
//
// @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();
} }
@ -356,13 +243,9 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private void startPlayback() { private void startPlayback() {
if (player == null) return; if (player == null) return;
Log.d(TAG, "开始播放,重试次数: " + retryCount); Log.d(TAG, "开始播放,重试次数: " + retryCount);
// MediaItem mediaItem = MediaItem.fromUri(Uri.parse(RTMP_URL));
MediaItem mediaItem = MediaItem.fromUri(RTMP_URL); MediaItem mediaItem = MediaItem.fromUri(RTMP_URL);
ProgressiveMediaSource videoSource = new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory()) ProgressiveMediaSource videoSource = new ProgressiveMediaSource.Factory(new RtmpDataSource.Factory()).createMediaSource(mediaItem);
.createMediaSource(mediaItem);
player.setMediaSource(videoSource); player.setMediaSource(videoSource);
// player.setMediaItem(mediaItem); // player.setMediaItem(mediaItem);
player.prepare(); player.prepare();
@ -371,11 +254,9 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
private void handlePlaybackError() { private void handlePlaybackError() {
releasePlayerInternal(); releasePlayerInternal();
if (retryCount < MAX_RETRIES) { if (retryCount < MAX_RETRIES) {
retryCount++; retryCount++;
Log.d(TAG, "准备重试 (" + retryCount + "), 延迟: " + currentRetryDelay + "ms"); Log.d(TAG, "准备重试 (" + retryCount + "), 延迟: " + currentRetryDelay + "ms");
retryHandler.postDelayed(() -> { retryHandler.postDelayed(() -> {
initializePlayer(); initializePlayer();
// 增加下次重试的延迟时间(使用退避算法) // 增加下次重试的延迟时间(使用退避算法)
@ -394,12 +275,11 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
AlertDialogUtils.dismiss(alertDialog);
releasePlayerInternal();
stopStreaming(); stopStreaming();
} }
@ -408,7 +288,7 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
if (Util.SDK_INT < 24) { if (Util.SDK_INT < 24) {
releasePlayer(); releasePlayerInternal();
} }
} }
@ -417,16 +297,10 @@ public class StreamActivity extends AppCompatActivity implements View.OnClickLis
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
if (Util.SDK_INT >= 24) { if (Util.SDK_INT >= 24) {
releasePlayer(); releasePlayerInternal();
} }
} }
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {

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

Loading…
Cancel
Save