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.57k stars 776 forks source link

retry server1 #1468

Closed yubinjin closed 6 months ago

yubinjin commented 6 months ago

this is my code


/*
 * Copyright (C) 2023 pedroSG94.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.aliger.usbstreaming.camera;

import android.Manifest;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

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

import com.aliger.usbstreaming.MyFusedLocationProvider;
import com.aliger.usbstreaming.R;
import com.jiangdg.ausbc.CameraClient;
import com.jiangdg.ausbc.camera.CameraUvcStrategy;
import com.jiangdg.ausbc.camera.bean.CameraRequest;
import com.pedro.common.ConnectChecker;
import com.pedro.encoder.input.video.CameraOpenException;
import com.pedro.library.rtsp.RtspCamera1;
import com.pedro.rtsp.rtsp.Protocol;

import org.jetbrains.annotations.NotNull;

import java.io.File;

/**
 * More documentation see:
 * {@link com.pedro.library.base.Camera1Base}
 * {@link RtspCamera1}
 */
public class ExampleRtspActivity extends AppCompatActivity
    implements ConnectChecker, View.OnClickListener, SurfaceHolder.Callback {

  private CameraClient cameraClient;
  private RtspCamera1 rtspCamera1;
  private ImageButton button;
  private ImageView streamStatusIcon;
  private Button bRecord;
  private TextView streamStatus;
  private String currentDateAndTime = "";
  private File folder;
  private ImageButton lockScreenButton;
  private ImageButton switchCameraButton; // 여기에 switchCameraButton 선언
  private boolean isStreaming = false;
  private MyFusedLocationProvider myFusedLocationProvider;
  //private MyWebViewClient myWebViewClient;
  private WebView webView;

  private boolean isScreenLocked = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    setContentView(R.layout.activity_example);
    folder = PathUtils.getRecordPath();
    SurfaceView surfaceView = findViewById(R.id.surfaceView);
    button = findViewById(R.id.b_start_stop);
    lockScreenButton = findViewById(R.id.b_lock_screen);
    button.setOnClickListener(this);
    myFusedLocationProvider = new MyFusedLocationProvider(this);

    //myWebViewClient = new MyWebViewClient(this, webView);
    //myWebViewClient.setupWebView();
    // switchCameraButton 초기화
    switchCameraButton = findViewById(R.id.switch_camera);
    switchCameraButton.setOnClickListener(this);
    streamStatusIcon = findViewById(R.id.stream_status_icon);
    lockScreenButton.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        onLockScreenClicked(view);
      }
    });
    ImageButton switchCamera = findViewById(R.id.switch_camera);
    switchCamera.setOnClickListener(this);
    rtspCamera1 = new RtspCamera1(surfaceView, this);
    rtspCamera1.getStreamClient().setReTries(5);
    surfaceView.getHolder().addCallback(this);
    //rtspCamera1.getStreamClient().setProtocol(Protocol.TCP);
    cameraClient = CameraClient.newBuilder(this)
            .setEnableGLES(true)
            .setCameraStrategy(new CameraUvcStrategy(this))
            .setRawImage(false)
            .setCameraRequest(new CameraRequest.Builder()
                    .setFrontCamera(false)
                    .setPreviewWidth(1280)
                    .setPreviewHeight(720)
                    .create())
            .build();

  }

  @Override
  public void onConnectionStarted(@NotNull String url) {
  }

  @Override
  public void onConnectionSuccess() {
    myFusedLocationProvider.requestLocationUpdates();
    updateStreamingStatus(true);
    Toast.makeText(ExampleRtspActivity.this, "연결에 성공하였습니다.", Toast.LENGTH_SHORT).show();

  }

  @Override
  public void onConnectionFailed(@NonNull final String reason) {
    if (rtspCamera1.getStreamClient().reTry(5000, reason, "rtsp://123.456.214:8554/"+getStreamName())) {//secondserver
      Toast.makeText(ExampleRtspActivity.this, "Retry", Toast.LENGTH_SHORT)
          .show();
      Toast.makeText(ExampleRtspActivity.this, "2번째 서버에 연결중니다.", Toast.LENGTH_SHORT)
              .show();

      //updateStreamingStatus(true);
      myFusedLocationProvider.setStreamName(getStreamName());
      myFusedLocationProvider.requestLocationUpdates();
    } else {

      Toast.makeText(ExampleRtspActivity.this, "2번서버에도 연결 실패하였습니다." , Toast.LENGTH_SHORT)
          .show();
      //updateStreamingStatus(false);
      myFusedLocationProvider.removeLocationUpdates();
      rtspCamera1.stopStream();
      //ScreenOrientation.INSTANCE.unlockScreen(this);
      //button.setText(R.string.start_button);
    }
  }
  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(View view) {
    isScreenLocked = !isScreenLocked;
    if (isScreenLocked) {
      // 화면 잠금 상태
      lockScreenButton.setImageResource(R.drawable.lock);
      switchCameraButton.setEnabled(false);
      button.setEnabled(false); // 스트리밍 시작/중지 버튼 비활성화
      hideSystemUI();
      Toast.makeText(this, "화면이 잠겼습니다.", Toast.LENGTH_SHORT).show();
      // 기타 버튼을 비활성화하는 코드 추가
    } else {
      // 화면 잠금 해제
      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);
  }
  @Override
  public void onNewBitrate(final long bitrate) {

  }

  @Override
  public void onDisconnect() {
    Toast.makeText(ExampleRtspActivity.this, "Disconnected", Toast.LENGTH_SHORT).show();
    updateStreamingStatus(false);
    myFusedLocationProvider.removeLocationUpdates();

    //String retryServerURL = "rtsp://123.456.789.214:8554/" + getStreamName();//1 server
    //attemptReconnect(retryServerURL);

  }

  @Override
  public void onAuthError() {
    Toast.makeText(ExampleRtspActivity.this, "Auth error", Toast.LENGTH_SHORT).show();
    rtspCamera1.stopStream();
    //ScreenOrientation.INSTANCE.unlockScreen(this);
    //button.setText(R.string.start_button);
    updateStreamingStatus(false);
  }

  @Override
  public void onAuthSuccess() {
    Toast.makeText(ExampleRtspActivity.this, "Auth success", Toast.LENGTH_SHORT).show();
  }

  @Override
  public void onClick(View view) {
    int id = view.getId();
    if (id == R.id.b_start_stop) {
      if (!rtspCamera1.isStreaming()) {
        myFusedLocationProvider.requestLocationUpdates();

        if (rtspCamera1.isRecording()
                || rtspCamera1.prepareAudio() && rtspCamera1.prepareVideo()) {
          //button.setText(R.string.stop_button);
          String rtspUrl = "rtsp://123.456.789.123:8554/" + getStreamName(); //first server 

          rtspCamera1.startStream(rtspUrl);
          //updateStreamingStatus(true);
          myFusedLocationProvider.setStreamName(getStreamName());
          Toast.makeText(this, "1번서버에 연결중입니다. " , Toast.LENGTH_SHORT).show();
          //Toast.makeText(this, "rtspUrl : " + rtspUrl, Toast.LENGTH_SHORT).show();
        } else {

          //Toast.makeText(this, "Error preparing stream, This device cant do it",
                  //Toast.LENGTH_SHORT).show();
        }
      } else {
        //button.setText(R.string.start_button);
        rtspCamera1.stopStream();
        myFusedLocationProvider.removeLocationUpdates();

        updateStreamingStatus(false);
      }
    } else if (id == R.id.switch_camera) {
      try {
        rtspCamera1.switchCamera();
      } catch (CameraOpenException e) {
        Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
      }
    }
  }
  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
  }

  @Override
  public void surfaceCreated(SurfaceHolder surfaceHolder) {

  }

  @Override
  public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    rtspCamera1.startPreview();
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    if (rtspCamera1.isStreaming()) {
      rtspCamera1.stopStream();
      //button.setText(getResources().getString(R.string.start_button));
    }
    //ScreenOrientation.INSTANCE.unlockScreen(this);
    rtspCamera1.stopPreview();
  }
}
```java
yubinjin commented 6 months ago

The second question is, can't I run two rtspCamera1.getStreamClient().reTry like the code above? Then, like the code above, why does it go through the if statement of rtspCamera1.getStreamClient().reTry and not the rtspCamera1.getStreamClient().reTry code of if in the else? Where does it go once rtspCamera1.getStreamClient().reTry is finished and the connection is established? Where do I go when I can't connect?

pedroSG94 commented 6 months ago

The second question is, can't I run two rtspCamera1.getStreamClient().reTry like the code above? Then, like the code above, why does it go through the if statement of rtspCamera1.getStreamClient().reTry and not the rtspCamera1.getStreamClient().reTry code of if in the else?

The code look fine. After receive connectionFailed callback, if retry method return true that means that the library is re connection to the new url. If you receive false, that means that the library can't reconnect to the server. It is because the endpoint used to connect is malformed (you are using a invalid url). In that case you must stop stream and use a valid url

Where does it go once rtspCamera1.getStreamClient().reTry is finished and the connection is established? Where do I go when I can't connect?

After call the retry, the library return again callback like if you stopStream and startStream again. So onConnectionStarted is called and then onConnectionSuccess or onConnectionFailed depend if your retry was success or failed again. This repeat until you get a success connection or you retry the number of times you set in setReTries method (in your case 5 times)

yubinjin commented 6 months ago

What I want is to stream to that IP when I press the button, and when streaming stops in the middle, I first try to retry from where I was originally streaming. And if reconnection is not possible after 5 retry attempts, move to the second rtsp address and stream. All IPs I posted above are valid. This is because any IP that replaces the external IP is written down. I need two retry statements to do what I want, but it doesn't work.

yubinjin commented 6 months ago

/*

package com.aliger.usbstreaming.camera;

import static androidx.constraintlayout.widget.Constraints.TAG;

import android.Manifest; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.telephony.TelephonyManager; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.webkit.WebView; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast;

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

import com.aliger.usbstreaming.MyFusedLocationProvider; import com.aliger.usbstreaming.R; import com.jiangdg.ausbc.CameraClient; import com.jiangdg.ausbc.camera.CameraUvcStrategy; import com.jiangdg.ausbc.camera.bean.CameraRequest; import com.pedro.common.ConnectChecker; import com.pedro.encoder.input.video.CameraOpenException; import com.pedro.library.rtsp.RtspCamera1; import com.pedro.rtsp.rtsp.Protocol;

import org.jetbrains.annotations.NotNull;

import java.io.File;

/**

yubinjin commented 6 months ago

In other words, when it works, how do you execute a second retry if the retry fails?

yubinjin commented 6 months ago

i need to your help so much plz.. help me

pedroSG94 commented 6 months ago

Since you want 5 retries in an url and then other 5 retries in other url you can use a counter:

//5 for the first url, and other 5 for the second url
genericStream.getStreamClient().setReTries(10)

  private val firstUrl = "rtsp://test:8554/live/test1"
  private val secondUrl = "rtsp://test:8554/live/test2"
  private var retries = 0

  override fun onConnectionFailed(reason: String) {
    val backupUrl = if (retries > 5) secondUrl else firstUrl
    if (genericStream.getStreamClient().reTry(5000, reason, backupUrl)) {
      retries++
    } else {
      retries = 0
      genericStream.stopStream()
    }
  }

This could be a code example

yubinjin commented 6 months ago

I could have done it like this. Thank you so much for the great answer! Thank you for always responding so kindly! have a good day! If I don’t know something, can I ask again?

pedroSG94 commented 6 months ago

Hello,

I could have done it like this. Thank you so much for the great answer! Thank you for always responding so kindly! have a good day! If I don’t know something, can I ask again?

Yes, If it is related with the library you can open other issue.

Closing this as completed