fix: 修复自编码模式分辨率切换及画面拉伸问题
- 重启编码器以支持动态分辨率切换 - 同步调整解码端 SurfaceView 尺寸以保持宽高比 - 修复 MediaCodec Image 未及时关闭导致的潜在崩溃
This commit is contained in:
@@ -19,6 +19,7 @@ import android.widget.RadioGroup;
|
||||
import android.widget.Switch;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.Gravity;
|
||||
import com.ttstd.controller.webrtc.SelfCodecDecoder;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
@@ -231,6 +232,57 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 自编码模式:按比例调整 selfCodecSurface 与触摸层尺寸。
|
||||
* MediaCodec 渲染到 SurfaceView 时会把帧拉伸填满整个 Surface 显示区域,
|
||||
* 因此必须让 Surface 的宽高比与视频一致,否则画面会被拉伸变形。
|
||||
*/
|
||||
private void adjustSelfCodecSize(int videoWidth, int videoHeight) {
|
||||
runOnUiThread(() -> {
|
||||
if (videoWidth == 0 || videoHeight == 0) return;
|
||||
|
||||
int containerWidth = controlPanel.getWidth();
|
||||
int containerHeight = controlPanel.getHeight();
|
||||
|
||||
if (containerWidth == 0 || containerHeight == 0) {
|
||||
// 容器尚未测量完成,稍后重试
|
||||
controlPanel.postDelayed(() -> adjustSelfCodecSize(videoWidth, videoHeight), 100);
|
||||
return;
|
||||
}
|
||||
|
||||
float videoAspectRatio = (float) videoWidth / videoHeight;
|
||||
float containerAspectRatio = (float) containerWidth / containerHeight;
|
||||
|
||||
int newWidth, newHeight;
|
||||
if (videoAspectRatio > containerAspectRatio) {
|
||||
// 视频更宽,以容器宽度为准
|
||||
newWidth = containerWidth;
|
||||
newHeight = (int) (containerWidth / videoAspectRatio);
|
||||
} else {
|
||||
// 视频更高,以容器高度为准
|
||||
newWidth = (int) (containerHeight * videoAspectRatio);
|
||||
newHeight = containerHeight;
|
||||
}
|
||||
|
||||
// 调整自编码渲染 Surface 的尺寸(等比居中),避免画面被拉伸变形
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) selfCodecSurface.getLayoutParams();
|
||||
lp.width = newWidth;
|
||||
lp.height = newHeight;
|
||||
lp.gravity = Gravity.CENTER;
|
||||
selfCodecSurface.setLayoutParams(lp);
|
||||
|
||||
// 同步触摸层,使其与视频区域完全重合,保证触控坐标一致
|
||||
FrameLayout.LayoutParams overlayLp = (FrameLayout.LayoutParams) touchOverlay.getLayoutParams();
|
||||
overlayLp.width = newWidth;
|
||||
overlayLp.height = newHeight;
|
||||
overlayLp.gravity = Gravity.CENTER;
|
||||
touchOverlay.setLayoutParams(overlayLp);
|
||||
|
||||
Log.d(TAG, String.format("Adjusted self-codec view size: %dx%d (video: %dx%d, container: %dx%d)",
|
||||
newWidth, newHeight, videoWidth, videoHeight, containerWidth, containerHeight));
|
||||
});
|
||||
}
|
||||
|
||||
/** 弹出鉴权输入对话框:选择免密连接、动态验证码或固定密码,输入后发起连接。 */
|
||||
private void showAuthDialog() {
|
||||
String serverUrl = etServerUrl.getText().toString().trim();
|
||||
@@ -653,6 +705,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
tvStreamMode = findViewById(R.id.tv_stream_mode);
|
||||
selfCodecSurface = findViewById(R.id.self_codec_surface);
|
||||
selfCodecDecoder = new SelfCodecDecoder();
|
||||
selfCodecDecoder.setOnResolutionUpdateListener((w, h) -> adjustSelfCodecSize(w, h));
|
||||
surfaceCallback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
@@ -688,6 +741,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
selfCodecSurface.setVisibility(View.VISIBLE);
|
||||
remoteVideoView.setVisibility(View.GONE);
|
||||
selfCodecDecoder.setEnabled(true);
|
||||
if (selfCodecDecoder.getVideoWidth() > 0) {
|
||||
adjustSelfCodecSize(selfCodecDecoder.getVideoWidth(), selfCodecDecoder.getVideoHeight());
|
||||
}
|
||||
} else {
|
||||
selfCodecSurface.setVisibility(View.GONE);
|
||||
remoteVideoView.setVisibility(View.VISIBLE);
|
||||
@@ -706,6 +762,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
selfCodecSurface.setVisibility(View.VISIBLE);
|
||||
remoteVideoView.setVisibility(View.GONE);
|
||||
selfCodecDecoder.setEnabled(true);
|
||||
if (selfCodecDecoder.getVideoWidth() > 0) {
|
||||
adjustSelfCodecSize(selfCodecDecoder.getVideoWidth(), selfCodecDecoder.getVideoHeight());
|
||||
}
|
||||
} else {
|
||||
selfCodecSurface.setVisibility(View.GONE);
|
||||
remoteVideoView.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -38,6 +38,9 @@ public class SelfCodecDecoder {
|
||||
private int videoWidth = PLACEHOLDER_W;
|
||||
private int videoHeight = PLACEHOLDER_H;
|
||||
|
||||
// 分辨率变化回调(通知 UI 调整渲染 Surface 尺寸以保持宽高比,避免画面被拉伸变形)
|
||||
private OnResolutionUpdateListener resolutionListener;
|
||||
|
||||
// 待喂入的参数集(SPS/PPS 等),以 CODEC_CONFIG 形式输入解码器
|
||||
private final ArrayDeque<byte[]> pendingConfigs = new ArrayDeque<>();
|
||||
// 待解码帧
|
||||
@@ -86,6 +89,9 @@ public class SelfCodecDecoder {
|
||||
// 分辨率变化时需重置解码器以重新 configure
|
||||
releaseDecoder();
|
||||
}
|
||||
if (resolutionListener != null) {
|
||||
resolutionListener.onResolutionUpdated(w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,4 +339,21 @@ public class SelfCodecDecoder {
|
||||
releaseDecoder();
|
||||
surface = null;
|
||||
}
|
||||
|
||||
public int getVideoWidth() {
|
||||
return videoWidth;
|
||||
}
|
||||
|
||||
public int getVideoHeight() {
|
||||
return videoHeight;
|
||||
}
|
||||
|
||||
public void setOnResolutionUpdateListener(OnResolutionUpdateListener l) {
|
||||
this.resolutionListener = l;
|
||||
}
|
||||
|
||||
/** 分辨率变化监听:当被控端上报新的视频宽高时回调,供 UI 调整渲染视图比例。 */
|
||||
public interface OnResolutionUpdateListener {
|
||||
void onResolutionUpdated(int width, int height);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user