pedroSG94 / RootEncoder

RootEncoder for Android (rtmp-rtsp-stream-client-java) is a stream encoder to push video/audio to media servers using protocols RTMP, RTSP, SRT and UDP with all code written in Java/Kotlin
Apache License 2.0
2.58k stars 780 forks source link

i can't streaming #1500

Open yubinjin opened 5 months ago

yubinjin commented 5 months ago

i can't streaming rtsp this is my code package com.seoul.fire.usb.activity; import com.jiangdg.ausbc.camera.CameraUvcStrategy; import com.jiangdg.ausbc.camera.bean.CameraRequest; import com.pedro.rtspserver.server.ClientListener; import com.pedro.rtspserver.server.ServerClient; import com.seoul.fire.rtsp.ExampleRtspActivity; import com.seoul.fire.usb.activity.StreamingController; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.graphics.SurfaceTexture; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Bundle;

import android.os.SystemClock; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceHolder; import android.view.TextureView; import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.Toast;

import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat;

import com.google.gson.Gson; import com.herohan.uvcapp.CameraHelper; import com.herohan.uvcapp.ICameraHelper; import com.herohan.uvcapp.ImageCapture; import com.herohan.uvcapp.VideoCapture; import com.hjq.permissions.XXPermissions; import com.jiangdg.ausbc.CameraClient; import com.pedro.common.ConnectChecker; import com.pedro.encoder.input.video.CameraOpenException; import com.pedro.encoder.utils.gl.AspectRatioMode; import com.pedro.library.rtsp.RtspStream; import com.pedro.library.util.sources.audio.MicrophoneSource; import com.pedro.library.view.OrientationForced; import com.seoul.fire.MyFusedLocationProvider; import com.seoul.fire.R; import com.seoul.fire.databinding.ActivityUsbBinding; import com.seoul.fire.usb.fragment.CameraControlsDialogFragment; import com.seoul.fire.usb.fragment.DeviceListDialogFragment; import com.seoul.fire.usb.fragment.VideoFormatDialogFragment; import com.seoul.fire.usb.utils.SaveHelper; import com.serenegiant.opengl.renderer.MirrorMode; import com.serenegiant.usb.IButtonCallback; import com.serenegiant.usb.Size; import com.serenegiant.usb.USBMonitor; import com.serenegiant.utils.UriHelper;

import java.io.File; import java.text.DecimalFormat; import java.util.Timer; import java.util.TimerTask;

public class USBActivity extends AppCompatActivity implements ClientListener, ConnectChecker {

private static final String TAG = USBActivity.class.getSimpleName();
private static final boolean DEBUG = true;

private ActivityUsbBinding mBinding;

private static final int QUARTER_SECOND = 250;
private static final int HALF_SECOND = 500;
private static final int ONE_SECOND = 1000;

private static final int DEFAULT_WIDTH = 640;
private static final int DEFAULT_HEIGHT = 480;

/**
 * Camera preview width
 */
private int mPreviewWidth = DEFAULT_WIDTH;
/**
 * Camera preview height
 */
private int mPreviewHeight = DEFAULT_HEIGHT;

private int mPreviewRotation = 0;

private ICameraHelper mCameraHelper;

private UsbDevice mUsbDevice;
private final ICameraHelper.StateCallback mStateCallback = new MyCameraHelperCallback();

private long mRecordStartTime = 0;
private Timer mRecordTimer = null;
private DecimalFormat mDecimalFormat;

private boolean mIsRecording = false;
private boolean mIsCameraConnected = false;

private CameraControlsDialogFragment mControlsDialog;
private DeviceListDialogFragment mDeviceListDialog;
private VideoFormatDialogFragment mFormatDialog;
//add
//
private ImageButton button;
private ImageButton lockScreenButton;
private MyFusedLocationProvider myFusedLocationProvider;
private boolean isScreenLocked = false;
private RtspStream rtspStream;
private CameraClient cameraClient;
private StreamingController streamingController;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mBinding = ActivityUsbBinding.inflate(getLayoutInflater());
    setContentView(mBinding.getRoot());
    setSupportActionBar(mBinding.toolbar);
    //add
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

// rtspStream.startPreview(); // 이 위치에서 CameraHelper 초기화를 먼저 수행 button = findViewById(R.id.usb_start_stop); lockScreenButton= findViewById(R.id.usb_lock_screen); // button.setOnClickListener(this); myFusedLocationProvider = new MyFusedLocationProvider(this);

    checkCameraHelper();
    setListeners();

    cameraClient = CameraClient.newBuilder(this)
            .setEnableGLES(true)
            .setCameraStrategy(new CameraUvcStrategy(this))
            .setRawImage(false)
            .setCameraRequest(new CameraRequest.Builder()
                    .setFrontCamera(false)
                    .setPreviewWidth(1280)
                    .setPreviewHeight(720)
                    .create())
            .build();

// rtspStream = new RtspStream( // (Context) this, // (ConnectChecker) this, // new USBCameraSource(this,mCameraHelper), // new MicrophoneSource() // // ); // rtspStream.getStreamClient().setReTries(10);

}

public void setUpServer(CameraClient cameraClient) {
    rtspStream = new RtspStream(
            this,  // 'context' 대신 'this'를 사용하면 현재 Activity의 context를 참조할 수 있습니다.
            this,  // ClientListener 인터페이스를 구현하는 객체 (이 경우 현재의 Activity)
            new USBCameraSource(this, cameraClient),
            new MicrophoneSource()
    );

    // 설정 옵션
    rtspStream.getStreamClient().setReTries(10);
    rtspStream.getGlInterface().setAspectRatioMode(AspectRatioMode.Adjust);
    rtspStream.getGlInterface().forceOrientation(OrientationForced.LANDSCAPE);

    // 비디오 및 오디오 준비
    boolean prepared = rtspStream.prepareVideo(1920, 1080, 4000000) && rtspStream.prepareAudio(48000, false, 128000);
}

public void startStream() {
    if (rtspStream.prepareVideo(1920, 1080, 4000000) && rtspStream.prepareAudio(48000, false, 128000) && !rtspStream.isStreaming()) {
    String streamName = getStreamName(); // 스트림 이름 가져오기
    String streamURL = "myrtsp:8554/" + streamName;
    rtspStream.startStream(streamURL); // 스트림 시작
    Log.d("Streaming", "Stream started: " + streamURL);
    myFusedLocationProvider.updateServerIp("myIp");
    myFusedLocationProvider.setStreamName(streamName);
    myFusedLocationProvider.requestLocationUpdates();
    Toast.makeText(this, "서버에 연결중 입니다.", Toast.LENGTH_LONG).show();
    // 여기서 LiveData 대신 일반 변수나 다른 메커니즘을 사용해야 할 수 있습니다.
    // 예: _isStreaming = true; 또는 updateStreamingStatus(true);
} else {
    rtspStream.stopStream();
    Log.d("Streaming", "rtspStream has not been initialized or not prepared");
    myFusedLocationProvider.removeLocationUpdates();
    // LiveData 대신 사용할 방법
    // 예: _isStreaming = false; 또는 updateStreamingStatus(false);
     Toast.makeText(this, "rtspStream has not been initialized or not prepared", Toast.LENGTH_LONG).show();
}

}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
        if (!mIsCameraConnected) {
            mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            selectDevice(mUsbDevice);
        }
    }
}

@Override
protected void onStart() {
    super.onStart();
    initPreviewView();

}

@Override
protected void onStop() {
    super.onStop();
    if (mIsRecording) {
        toggleVideoRecord(false);
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    clearCameraHelper();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_control) {
        showCameraControlsDialog();
    } else if (id == R.id.action_device) {
        showDeviceListDialog();
    } else if (id == R.id.action_safely_eject) {
        safelyEject();
    } else if (id == R.id.action_settings) {
    } else if (id == R.id.action_video_format) {
        showVideoFormatDialog();
    } else if (id == R.id.action_rotate_90_CW) {
        rotateBy(90);
    } else if (id == R.id.action_rotate_90_CCW) {
        rotateBy(-90);
    } else if (id == R.id.action_flip_horizontally) {
        flipHorizontally();
    } else if (id == R.id.action_flip_vertically) {
        flipVertically();
    }

    return true;
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    if (mIsCameraConnected) {
        menu.findItem(R.id.action_control).setVisible(true);
        menu.findItem(R.id.action_safely_eject).setVisible(true);
        menu.findItem(R.id.action_video_format).setVisible(true);
        menu.findItem(R.id.action_rotate_90_CW).setVisible(true);
        menu.findItem(R.id.action_rotate_90_CCW).setVisible(true);
        menu.findItem(R.id.action_flip_horizontally).setVisible(true);
        menu.findItem(R.id.action_flip_vertically).setVisible(true);
    } else {
        menu.findItem(R.id.action_control).setVisible(false);
        menu.findItem(R.id.action_safely_eject).setVisible(false);
        menu.findItem(R.id.action_video_format).setVisible(false);
        menu.findItem(R.id.action_rotate_90_CW).setVisible(false);
        menu.findItem(R.id.action_rotate_90_CCW).setVisible(false);
        menu.findItem(R.id.action_flip_horizontally).setVisible(false);
        menu.findItem(R.id.action_flip_vertically).setVisible(false);
    }
    return super.onPrepareOptionsMenu(menu);

}

private void setListeners() {
    mBinding.fabPicture.setOnClickListener(v -> {
        XXPermissions.with(this)
                .permission(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
                .request((permissions, all) -> {
                    takePicture();
                });
    });

    mBinding.fabVideo.setOnClickListener(v -> {
        XXPermissions.with(this)
                .permission(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
                .permission(Manifest.permission.RECORD_AUDIO)
                .request((permissions, all) -> {
                    toggleVideoRecord(!mIsRecording);
                });
    });
}

private void showCameraControlsDialog() {
    if (mControlsDialog == null) {
        mControlsDialog = new CameraControlsDialogFragment(mCameraHelper);
    }
    // When DialogFragment is not showing
    if (!mControlsDialog.isAdded()) {
        mControlsDialog.show(getSupportFragmentManager(), "camera_controls");
    }
}

private void showDeviceListDialog() {
    if (mDeviceListDialog != null && mDeviceListDialog.isAdded()) {
        return;
    }

    mDeviceListDialog = new DeviceListDialogFragment(mCameraHelper, mIsCameraConnected ? mUsbDevice : null);
    mDeviceListDialog.setOnDeviceItemSelectListener(usbDevice -> {
        if (mIsCameraConnected) {
            mCameraHelper.closeCamera();
        }
        mUsbDevice = usbDevice;
        selectDevice(mUsbDevice);
    });

    mDeviceListDialog.show(getSupportFragmentManager(), "device_list");
}

private void showVideoFormatDialog() {
    if (mFormatDialog != null && mFormatDialog.isAdded()) {
        return;
    }

    mFormatDialog = new VideoFormatDialogFragment(mCameraHelper.getSupportedFormatList(), mCameraHelper.getPreviewSize());
    mFormatDialog.setOnVideoFormatSelectListener(size -> {
        if (mIsCameraConnected && !mCameraHelper.isRecording()) {
            mCameraHelper.stopPreview();
            mCameraHelper.setPreviewSize(size);
            mCameraHelper.startPreview();
            //rtspStream.startStream("rtsp://myrtsp:8554/" + getStreamName());
            resizePreviewView(size);
            // save selected preview size
            setSavedPreviewSize(size);
        }
    });

    mFormatDialog.show(getSupportFragmentManager(), "video_format");
}

private void closeAllDialogFragment() {
    if (mControlsDialog != null && mControlsDialog.isAdded()) {
        mControlsDialog.dismiss();
    }
    if (mDeviceListDialog != null && mDeviceListDialog.isAdded()) {
        mDeviceListDialog.dismiss();
    }
    if (mFormatDialog != null && mFormatDialog.isAdded()) {
        mFormatDialog.dismiss();
    }
}

private void safelyEject() {
    if (mCameraHelper != null) {
        mCameraHelper.closeCamera();
    }
}

private void rotateBy(int angle) {
    mPreviewRotation += angle;
    mPreviewRotation %= 360;
    if (mPreviewRotation < 0) {
        mPreviewRotation += 360;
    }

    if (mCameraHelper != null) {
        mCameraHelper.setPreviewConfig(
                mCameraHelper.getPreviewConfig().setRotation(mPreviewRotation));
    }
}

private void flipHorizontally() {
    if (mCameraHelper != null) {
        mCameraHelper.setPreviewConfig(
                mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_HORIZONTAL));
    }
}

private void flipVertically() {
    if (mCameraHelper != null) {
        mCameraHelper.setPreviewConfig(
                mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_VERTICAL));
    }
}

private void checkCameraHelper() {
    if (!mIsCameraConnected) {
        clearCameraHelper();
    }
    initCameraHelper();
}

private void initCameraHelper() {
    if (mCameraHelper == null) {
        mCameraHelper = new CameraHelper();
        mCameraHelper.setStateCallback(mStateCallback);

        setCustomImageCaptureConfig();
        setCustomVideoCaptureConfig();
    }
}

private void clearCameraHelper() {
    if (DEBUG) Log.v(TAG, "clearCameraHelper:");
    if (mCameraHelper != null) {
        mCameraHelper.release();
        mCameraHelper = null;
    }
}

private void initPreviewView() {
    mBinding.viewMainPreview.setAspectRatio(mPreviewWidth, mPreviewHeight);
    mBinding.viewMainPreview.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
            if (mCameraHelper != null) {
                mCameraHelper.addSurface(surface, false);
                setUpServer(cameraClient);
                startStream();
                rtspStream.startPreview( mBinding.viewMainPreview);

// startPreview( mBinding.viewMainPreview);

            }
        }

        @Override
        public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
        }

        @Override
        public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
            if (mCameraHelper != null) {
                mCameraHelper.removeSurface(surface);
            }
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

        }
    });
}

public void attachNewDevice(UsbDevice device) {
    if (mUsbDevice == null) {
        mUsbDevice = device;

        selectDevice(device);
    }
}

/**
 * In Android9+, connected to the UVC CAMERA, CAMERA permission is required
 *
 * @param device
 */
protected void selectDevice(UsbDevice device) {
    if (DEBUG) Log.v(TAG, "selectDevice:device=" + device.getDeviceName());

    XXPermissions.with(this)
            .permission(Manifest.permission.CAMERA)
            .request((permissions, all) -> {
                mIsCameraConnected = false;
                updateUIControls();

                if (mCameraHelper != null) {
                    // 通过UsbDevice对象,尝试获取设备权限
                    mCameraHelper.selectDevice(device);
                }
            });
}

@Override
public void onAuthError() {

}

@Override
public void onAuthSuccess() {

}

@Override
public void onConnectionFailed(@NonNull String reason) {
    if (rtspStream.getStreamClient().reTry(5000, reason, null)) {
        Toast.makeText(USBActivity.this, "Retry", Toast.LENGTH_SHORT)
                .show();

        myFusedLocationProvider.updateServerIp("myIp");
        //updateStreamingStatus(true);
        myFusedLocationProvider.setStreamName(getStreamName());
        myFusedLocationProvider.requestLocationUpdates();
        rtspStream.getStreamClient().reTry(5000, reason, "rtsp://myIp:8554/"+getStreamName());
    } else {

        //ScreenOrientation.INSTANCE.unlockScreen(this);

        //Log.d(TAG, "onConnectionFailed: ",null); //button.setText(R.string.start_button);
    }
}

@Override
public void onConnectionStarted(@NonNull String s) {

    Log.d("onConnectionStarted",s);
}

@Override
public void onConnectionSuccess() {
    Log.d("Streaming","Connection Successful!");
}

@Override
public void onDisconnect() {

    Log.d("Streaming","onDisconnect!");
}

@Override
public void onNewBitrate(long l) {

}

@Override
public void onClientConnected(@NonNull ServerClient serverClient) {
    Log.d("Streaming","Client Connected: ${client.name}");
}

@Override
public void onClientDisconnected(@NonNull ServerClient serverClient) {
    Log.d("Streaming","Client Disconnected: ${client.name}");

}

private class MyCameraHelperCallback implements ICameraHelper.StateCallback {
    @Override
    public void onAttach(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onAttach:device=" + device.getDeviceName());

        attachNewDevice(device);
    }

    /**
     * After obtaining USB device permissions, connect the USB camera
     */
    @Override
    public void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {
        if (DEBUG) Log.v(TAG, "onDeviceOpen:device=" + device.getDeviceName());

        mCameraHelper.openCamera(getSavedPreviewSize());

        mCameraHelper.setButtonCallback(new IButtonCallback() {
            @Override
            public void onButton(int button, int state) {
                Toast.makeText(USBActivity.this, "onButton(button=" + button + "; " +
                        "state=" + state + ")", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onCameraOpen(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onCameraOpen:device=" + device.getDeviceName());
        mCameraHelper.startPreview();

        // After connecting to the camera, you can get preview size of the camera
        Size size = mCameraHelper.getPreviewSize();
        if (size != null) {
            resizePreviewView(size);
        }

        if (mBinding.viewMainPreview.getSurfaceTexture() != null) {
            mCameraHelper.addSurface(mBinding.viewMainPreview.getSurfaceTexture(), false);
        }

        mIsCameraConnected = true;
        updateUIControls();
    }

    @Override
    public void onCameraClose(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onCameraClose:device=" + device.getDeviceName());

        if (mIsRecording) {
            toggleVideoRecord(false);
        }

        if (mCameraHelper != null && mBinding.viewMainPreview.getSurfaceTexture() != null) {
            mCameraHelper.removeSurface(mBinding.viewMainPreview.getSurfaceTexture());
        }

        mIsCameraConnected = false;
        updateUIControls();

        closeAllDialogFragment();
    }

    @Override
    public void onDeviceClose(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onDeviceClose:device=" + device.getDeviceName());
    }

    @Override
    public void onDetach(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onDetach:device=" + device.getDeviceName());

        if (device.equals(mUsbDevice)) {
            mUsbDevice = null;
        }
    }

    @Override
    public void onCancel(UsbDevice device) {
        if (DEBUG) Log.v(TAG, "onCancel:device=" + device.getDeviceName());

        if (device.equals(mUsbDevice)) {
            mUsbDevice = null;
        }
    }
}

private void resizePreviewView(Size size) {
    // Update the preview size
    mPreviewWidth = size.width;
    mPreviewHeight = size.height;
    // Set the aspect ratio of TextureView to match the aspect ratio of the camera
    mBinding.viewMainPreview.setAspectRatio(mPreviewWidth, mPreviewHeight);
}

private void updateUIControls() {
    runOnUiThread(() -> {
        if (mIsCameraConnected) {
            mBinding.viewMainPreview.setVisibility(View.VISIBLE);
            mBinding.tvConnectUSBCameraTip.setVisibility(View.GONE);

            mBinding.fabPicture.setVisibility(View.VISIBLE);
            mBinding.fabVideo.setVisibility(View.VISIBLE);

            // Update record button
            int colorId = R.color.WHITE;
            if (mIsRecording) {
                colorId = R.color.RED;
            }
            ColorStateList colorStateList = ColorStateList.valueOf(getResources().getColor(colorId));
            mBinding.fabVideo.setSupportImageTintList(colorStateList);

        } else {
            mBinding.viewMainPreview.setVisibility(View.GONE);
            mBinding.tvConnectUSBCameraTip.setVisibility(View.VISIBLE);

            mBinding.fabPicture.setVisibility(View.GONE);
            mBinding.fabVideo.setVisibility(View.GONE);

            mBinding.tvVideoRecordTime.setVisibility(View.GONE);
        }
        invalidateOptionsMenu();
    });
}

private Size getSavedPreviewSize() {
    String key = getString(R.string.saved_preview_size) + USBMonitor.getProductKey(mUsbDevice);
    String sizeStr = getPreferences(MODE_PRIVATE).getString(key, null);
    if (TextUtils.isEmpty(sizeStr)) {
        return null;
    }
    Gson gson = new Gson();
    return gson.fromJson(sizeStr, Size.class);
}

private void setSavedPreviewSize(Size size) {
    String key = getString(R.string.saved_preview_size) + USBMonitor.getProductKey(mUsbDevice);
    Gson gson = new Gson();
    String json = gson.toJson(size);
    getPreferences(MODE_PRIVATE)
            .edit()
            .putString(key, json)
            .apply();
}

private void setCustomImageCaptureConfig() {

// mCameraHelper.setImageCaptureConfig( // mCameraHelper.getImageCaptureConfig().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)); mCameraHelper.setImageCaptureConfig( mCameraHelper.getImageCaptureConfig().setJpegCompressionQuality(90)); }

public void takePicture() {
    if (mIsRecording) {
        return;
    }

    try {
        File file = new File(SaveHelper.getSavePhotoPath());
        ImageCapture.OutputFileOptions options =
                new ImageCapture.OutputFileOptions.Builder(file).build();
        mCameraHelper.takePicture(options, new ImageCapture.OnImageCaptureCallback() {
            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                Toast.makeText(USBActivity.this,
                        "save \"" + UriHelper.getPath(USBActivity.this, outputFileResults.getSavedUri()) + "\"",
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(int imageCaptureError, @NonNull String message, @Nullable Throwable cause) {
                Toast.makeText(USBActivity.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    } catch (Exception e) {
        Log.e(TAG, e.getLocalizedMessage(), e);
    }
}

public void toggleVideoRecord(boolean isRecording) {
    try {
        if (isRecording) {
            if (mIsCameraConnected && mCameraHelper != null && !mCameraHelper.isRecording()) {
                startRecord();
            }
        } else {
            if (mIsCameraConnected && mCameraHelper != null && mCameraHelper.isRecording()) {
                stopRecord();
            }

            stopRecordTimer();
        }
    } catch (Exception e) {
        Log.e(TAG, e.getLocalizedMessage(), e);
        stopRecordTimer();
    }

    mIsRecording = isRecording;

    updateUIControls();
}

private void setCustomVideoCaptureConfig() {
    mCameraHelper.setVideoCaptureConfig(
            mCameraHelper.getVideoCaptureConfig()

// .setAudioCaptureEnable(false) .setBitRate((int) (1024 1024 25 * 0.25)) .setVideoFrameRate(25) .setIFrameInterval(1)); }

private void startRecord() {
    File file = new File(SaveHelper.getSaveVideoPath());
    VideoCapture.OutputFileOptions options =
            new VideoCapture.OutputFileOptions.Builder(file).build();
    mCameraHelper.startRecording(options, new VideoCapture.OnVideoCaptureCallback() {
        @Override
        public void onStart() {
            startRecordTimer();
        }

        @Override
        public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {
            toggleVideoRecord(false);

            Toast.makeText(
                    USBActivity.this,
                    "save \"" + UriHelper.getPath(USBActivity.this, outputFileResults.getSavedUri()) + "\"",
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
            toggleVideoRecord(false);

            Toast.makeText(USBActivity.this, message, Toast.LENGTH_LONG).show();
        }
    });
}

private void stopRecord() {
    mCameraHelper.stopRecording();
}

private void startRecordTimer() {
    runOnUiThread(() -> mBinding.tvVideoRecordTime.setVisibility(View.VISIBLE));

    // Set “00:00:00” to record time TextView
    setVideoRecordTimeText(formatTime(0));

    // Start Record Timer
    mRecordStartTime = SystemClock.elapsedRealtime();
    mRecordTimer = new Timer();
    //The timer is refreshed every quarter second
    mRecordTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            long recordTime = (SystemClock.elapsedRealtime() - mRecordStartTime) / 1000;
            if (recordTime > 0) {
                setVideoRecordTimeText(formatTime(recordTime));
            }
        }
    }, QUARTER_SECOND, QUARTER_SECOND);
}

private void stopRecordTimer() {
    runOnUiThread(() -> mBinding.tvVideoRecordTime.setVisibility(View.GONE));

    // Stop Record Timer
    mRecordStartTime = 0;
    if (mRecordTimer != null) {
        mRecordTimer.cancel();
        mRecordTimer = null;
    }
    // Set “00:00:00” to record time TextView
    setVideoRecordTimeText(formatTime(0));
}

private void setVideoRecordTimeText(String timeText) {
    runOnUiThread(() -> {
        mBinding.tvVideoRecordTime.setText(timeText);
    });
}

/**
 * 将秒转化为 HH:mm:ss 的格式
 *
 * @param time 秒
 * @return
 */
private String formatTime(long time) {
    if (mDecimalFormat == null) {
        mDecimalFormat = new DecimalFormat("00");
    }
    String hh = mDecimalFormat.format(time / 3600);
    String mm = mDecimalFormat.format(time % 3600 / 60);
    String ss = mDecimalFormat.format(time % 60);
    return hh + ":" + mm + ":" + ss;
}

private String getStreamName() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
        TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
        try {
            String phoneNumber = tm.getLine1Number();
            if (phoneNumber != null && !phoneNumber.trim().isEmpty()) {
                if (phoneNumber.startsWith("+8210")) {
                    phoneNumber = phoneNumber.replace("+8210", "010");
                }
                return phoneNumber;
            }
        } catch (SecurityException e) {
            Log.e("ExampleRtspActivity", "Failed to get phone number.", e);
        }
    }
    return "mystream";  // Default stream name
}

private void updateStreamingStatus(boolean isStreaming) {
    int size = getResources().getDimensionPixelSize(R.dimen.stream_status_icon_size); // 예: 48dp

    // LayoutParams를 통한 크기 조정
    //ViewGroup.LayoutParams layoutParams = streamStatusIcon.getLayoutParams();
    //layoutParams.width = size;
    //layoutParams.height = size;
    //streamStatusIcon.setLayoutParams(layoutParams);

    if (isStreaming) {
        myFusedLocationProvider.requestLocationUpdates();
        //streamStatusIcon.setImageResource(R.drawable.streaming1);
    } else {
        //streamStatusIcon.setImageResource(R.drawable.nostreaming);
        myFusedLocationProvider.removeLocationUpdates();
    }
}

public void onLockScreenClicked(Menu menu) {
    isScreenLocked = !isScreenLocked;
    if (isScreenLocked) {
        menu.findItem(R.id.action_control).setVisible(false);
        menu.findItem(R.id.action_safely_eject).setVisible(false);
        menu.findItem(R.id.action_video_format).setVisible(false);
        menu.findItem(R.id.action_rotate_90_CW).setVisible(false);
        menu.findItem(R.id.action_rotate_90_CCW).setVisible(false);
        menu.findItem(R.id.action_flip_horizontally).setVisible(false);
        menu.findItem(R.id.action_flip_vertically).setVisible(false);
        // 화면 잠금 상태
        lockScreenButton.setImageResource(R.drawable.lock);
        //switchCameraButton.setEnabled(false);
        button.setEnabled(false); // 스트리밍 시작/중지 버튼 비활성화
        hideSystemUI();
        Toast.makeText(this, "화면이 잠겼습니다.", Toast.LENGTH_SHORT).show();
        // 기타 버튼을 비활성화하는 코드 추가
    } else {
        menu.findItem(R.id.action_control).setVisible(true);
        menu.findItem(R.id.action_safely_eject).setVisible(true);
        menu.findItem(R.id.action_video_format).setVisible(true);
        menu.findItem(R.id.action_rotate_90_CW).setVisible(true);
        menu.findItem(R.id.action_rotate_90_CCW).setVisible(true);
        menu.findItem(R.id.action_flip_horizontally).setVisible(true);
        menu.findItem(R.id.action_flip_vertically).setVisible(true);
        // 화면 잠금 해제
        lockScreenButton.setImageResource(R.drawable.unlock);
        //switchCameraButton.setEnabled(true);
        button.setEnabled(true);
        showSystemUI();// 스트리밍 시작/중지 버튼 활성화
        Toast.makeText(this, "화면이 풀렸습니다.", Toast.LENGTH_SHORT).show();
        // 기타 버튼을 활성화하는 코드 추가
    }

}

private void hideSystemUI() {
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_IMMERSIVE
                    // Set the content to appear under the system bars so that the
                    // content doesn't resize when the system bars hide and show.
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    // Hide the nav bar and status bar
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN);
}
private void showSystemUI() {
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}

} and the log repeatly can you help me?? 024-06-13 16:59:53.359 27273-27434 RtspClient com.seoul.fire I waiting for sps and pps 2024-06-13 16:59:58.354 27273-27441 RtspClient com.seoul.fire I write teardown success 2024-06-13 16:59:58.355 27273-27441 RtspClient com.seoul.fire E connection error kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@eb32c3a 2024-06-13 16:59:58.365 27273-27391 RtspClient com.seoul.fire I write teardown success 2024-06-13 16:59:58.365 27273-27442 RtspClient com.seoul.fire E connection error kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@4a81e48

pedroSG94 commented 5 months ago

Hello,

You can't stream because you are not rendering correctly the surface provided by VideoSource class (USBCameraSource). You are rendering the surface of the TextureView so you only have a preview and the stream never receive video data. You should render the surface provided inside USBCameraSource on the start method.

You can try follow this post (you can use the last library version for it): https://github.com/pedroSG94/RTSP-Server/issues/110 Or use this branch to test: https://github.com/pedroSG94/RootEncoder/tree/feature/extra-video-source In the Rotation example, you can select USB camera as video source

yubinjin commented 5 months ago

package com.seoul.fire.usb.activity

import android.content.Context import android.graphics.SurfaceTexture import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import com.jiangdg.ausbc.CameraClient import com.pedro.library.util.sources.video.VideoSource

class USBCameraSource( private val context: Context, private val cameraClient: CameraClient,

): VideoSource(), LifecycleOwner {

private var mWidth = 1280
private var mHeight = 720
init {
    Log.d("camera_str", "USBCameraSource INIT!")
}

private val lifecycleRegistry = LifecycleRegistry(this)

override fun create(width: Int, height: Int, fps: Int, rotation: Int): Boolean {
    mWidth = width
    mHeight = height
    return try {
        Log.d("camera_str", "defining Render size to $width Width and $height height")

        created = true
        true
    } catch (e : Exception){
        Log.d("camera_str","EXCEPTION ON CREATE USBCAMERASOURCE -> $e")
        false
    }

}

override fun start(surfaceTexture: SurfaceTexture) {
    this.surfaceTexture = surfaceTexture
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
    //cameraClient.openCamera(surfaceTexture,mWidth,mHeight)
}

override fun stop() {
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
    cameraClient.let {
        it.closeCamera()
        surfaceTexture = null
    }
}

override fun release() {
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}

override fun isRunning(): Boolean {
    return cameraClient.isCameraOpened()!!
}

override val lifecycle: Lifecycle
    get() = lifecycleRegistry

} is this problam??

pedroSG94 commented 5 months ago

Yes, that class is not correct and for the same reason part of the code in USBActivity is wrong too. I recommend you follow the links I shared you

yubinjin commented 5 months ago

thank you for answer!!!! so i configured to my code package com.seoul.fire.usb.activity

import android.content.Context import android.graphics.SurfaceTexture import android.util.Log import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import com.seoul.fire.usb.activity.CameraClient2 import com.pedro.library.util.sources.video.VideoSource

class USBCameraSource( private val context: Context, private val cameraClient: CameraClient2, ): VideoSource(), LifecycleOwner {

private var mWidth = 1280
private var mHeight = 720
init {
    Log.d("camera_str", "USBCameraSource INIT!")
}

private val lifecycleRegistry = LifecycleRegistry(this)

override fun start(surfaceTexture: SurfaceTexture) {
    this.surfaceTexture = surfaceTexture
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
    cameraClient.openCamera(surfaceTexture,mWidth,mHeight)
}

override fun stop() {
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
    cameraClient.let {
        it.closeCamera()
        surfaceTexture = null
    }
}

override fun release() {
    lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}

override fun create(width: Int, height: Int, fps: Int, rotation: Int): Boolean {
    mWidth = width
    mHeight = height
    return try {
        Log.d("camera_str", "defining Render size to $width Width and $height height")
        cameraClient.setRenderSize(width,height)
        created = true
        true
    } catch (e : Exception){
        Log.d("camera_str","EXCEPTION ON CREATE USBCAMERASOURCE -> $e")
        false
    }

}

override fun isRunning(): Boolean {
    return cameraClient.isCameraOpened()!!
}

override val lifecycle: Lifecycle
    get() = lifecycleRegistry

}

this but it is error FATAL EXCEPTION: camera_manager Process: com.seoul.fire, PID: 18830 java.lang.UnsatisfiedLinkError: No implementation found for long com.jiangdg.uvc.UVCCamera.nativeCreate() (tried Java_com_jiangdg_uvc_UVCCamera_nativeCreate and Java_com_jiangdg_uvc_UVCCamera_nativeCreate__) at com.jiangdg.uvc.UVCCamera.nativeCreate(Native Method) at com.jiangdg.uvc.UVCCamera.(UVCCamera.java:179) at com.jiangdg.ausbc.camera.CameraUvcStrategy.createCamera(CameraUvcStrategy.kt:132) at com.jiangdg.ausbc.camera.CameraUvcStrategy.startPreviewInternal(CameraUvcStrategy.kt:114) at com.jiangdg.ausbc.camera.ICameraStrategy.handleMessage(ICameraStrategy.kt:103) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.os.HandlerThread.run(HandlerThread.java:67) how can i do ??

pedroSG94 commented 5 months ago

Hello,

That error is related with the usb camera library. The library is not compiling correctly. The c++ Code of the library is missed