Wie man eine mobile Streaming-App für Android erstellt

Wie man eine mobile Streaming-App für Android erstellt

Livestreaming von Mobilgeräten aus ermöglicht es Ihnen, mit Ihrem Publikum zu connecten, unabhängig davon, wo Sie sich gerade befinden. Entsprechende Dienste sind sehr beliebt und werden in den verschiedensten Anwendungsbereichen genutzt.

In diesem Artikel werden wir näher darauf eingehen, wie Sie Ihre eigene Anwendung für mobiles Streaming und das Betrachten von Livestreams auf Android erstellen und wie Sie diese in Gcore Streaming-Plattform integrieren können.

Wie man eine mobile Streaming-App für Android erstellt
Mobile livestreaming with Gcore Streaming Platform

Streaming-Protokolle

Streaming-Protokolle werden verwendet, um Video und Audio über öffentliche Netzwerke zu übertragen. Eines der gängigsten Streaming-Protokolle ist RTMP. Es wird von den meisten Streaming-Plattformen unterstützt. RTMP ist zuverlässig und eignet sich aufgrund der niedrigen Latenzzeit und der wiederholten Übertragung von Datenpaketen auf der Grundlage des TCP Protokolls perfekt für Livestreaming.

Um Content auf die Geräte von Nutzern zu bringen und ihn dort abzuspielen, bieten Streaming-Plattformen die beliebten und skalierbaren Broadcast-Formate HLS und DASH an. Darüber hinaus sind Android-Geräte von Haus aus mit einem MediaPlayer ausgestattet, der die HLS Wiedergabe unterstützt. Aus diesem Grund werden wir uns schwerpunktmäßig mit diesem Protokoll befassen.

Auswählen einer Bibliothek zum Erstellen eines RTMP Streams

Es gibt nur wenige Open-Source-Lösungen für RTMP Streaming über Android-Geräte, und noch weniger wirklich funktionelle Optionen. Sehen wir uns einige von ihnen genauer an.

1. rtmp-rtsp-stream-client-java

Vorteile:

  • Neue, regelmäßig aktualisierte Bibliothek
  • Mindestunterstützung für API 16
  • Unterstützung für die APIs Camera und Camera2
  • Kamerawechsel während des Streamens
  • Adaptive Bitrate
  • Aktivierung/Deaktivierung von Audio und Video während des Streamens
  • Möglichkeit zum Konfigurieren von Broadcasting-Parametern
  • Installation von OpenGL Filtern, Bildern, GIFs oder Text in Echtzeit
  • Einfach zu bedienen: alles funktioniert standardmäßig, ohne zusätzliche Anpassungen
  • Verfügbarkeit von Dokumentation und Anleitungen für die Bibliothek auf GitHub
  • Unkompliziertes Importieren von Bibliotheken über Gradle

Nachteile:

  • Keine Pausefunktion für einen Broadcast-Stream verfügbar

2. HaishinKit

Vorteile:

  • Stabile Updates
  • Unterstützt RTMP Wiedergabe
  • Verwendet die aktuelle Camera2-API
  • Unterstützt Kamerawechsel während des Streamens
  • Erlaubt das Konfigurieren von Broadcasting-Parametern

Nachteile:

  • Keine Option für eine adaptive Bitrate
  • Keine Pausefunktion für einen Broadcast-Stream verfügbar

3. LiveVideoBroadcaster

Vorteile:

  • Mindestunterstützung für API 28
  • Kamerawechsel während des Streamens
  • Adaptive Stream-Qualität (Framerate und Bitrate)
  • Aktivierung/Deaktivierung von Audio während des Streamens

Nachteile:

  • Nicht mehr aktuell: letztes Update am 28. Juli 2020
  • Verwendet veraltete Camera-API
  • Kein Importieren von Bibliotheken über Gradle möglich
  • Sehr viel komplexer als rtmp-rtsp-stream-client-java
  • Von der Bibliothek gesendete Frames enthalten Fehler (DC, AC, MV)

Zusammenfassend lässt sich sagen, dass rtmp-rtsp-stream-client-java unter allen drei Optionen die meisten Vorteile und die wenigsten Nachteile aufweist. Aus diesem Grund halten wir diese Bibliothek für die am besten geeignete Lösung.

Streaming-Implementierung über das RTMP Protokoll von einem Android-Smartphone

Die Bibliothek rtmp-rtsp-stream-client-java bietet zwei Arten von Objekten für das Streaming — RtmpCamera1 und RtmpCamera2. Ersteres verwendet die Camera-API, um den Stream von Ihrer Smartphone-Kamera zu erfassen, während Letzteres die Camera2-API verwendet.

Wir empfehlen die Verwendung von RtmpCamera2, da die Camera-API seit Android API Level 21 veraltet ist. In unserem Beispiel wird die aktuellste RtmpCamera2-Version verwendet.

Wir werden uns nun Schritt für Schritt ansehen, wie die Bibliothek rtmp-rtsp-stream-client-java für mobiles Streaming verwendet wird. Doch zunächst ein kurzer Überblick über ihre Funktionsweise.

  • Wenn die Methode aufgerufen wird, wird das Bild der Kamera erfasst.
  • Die erfasste Kamerasitzung wird an den OpenGlView-Eingang gesendet, wo sie dem Benutzer angezeigt wird.
  • Wenn die Methode aufgerufen wird, wird eine Verbindung zum Remote-Server hergestellt.
  • Die von der Kamera erfasste Sitzung wird über das RTMP Protokoll an die festgelegte rtmpUrl übertragen.

Schauen wir uns nun Schritt für Schritt an, wie man mobiles Online-Streaming einrichtet.

1. Init

Um die Bibliothek rtmp-rtsp-stream-client-java in Ihrem Projekt zu verwenden, müssen Sie Abhängigkeiten zu build.gradle hinzufügen:

allprojects {
   repositories {
       maven { url ’https://jitpack.io’ }
   }
}
dependencies {
    implementation ’com.github.pedroSG94.rtmp-rtsp-stream-client-  java:rtplibrary:2.1.7’
}

Aktuelle Version der Bibliothek

2. Berechtigungen

Geben Sie die erforderlichen Berechtigungen in der Datei AndroidManifest.xml an:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

3. Anzeigen des Kamera-Streams

Wenn Sie mit einer Smartphone-Kamera streamen, müssen Sie sehen, was der Stream zeigt. Zu diesem Zweck wird in einer entsprechenden Ansicht der Kamerastream auf dem Bildschirm angezeigt. Android verwendet SurfaceView oder TextureView. Die verwendete Bibliothek verfügt auch über eine eigene OpenGlView, die von SurfaceView abstammt.

Mit RtmpCamera1 können Sie jede dieser Ansichten verwenden, wohingegen mit RtmpCamera2 nur OpenGlView verfügbar ist. Aber von diesen Ansichten ermöglicht Ihnen nur OpenGlView, verschiedene Filter, Bilder, GIFs oder Text während des Streamens zu verwenden.

Fügen Sie OpenGlView zu „Layout Activity“ oder „Fragment“ hinzu, um den Kamerastream zu sehen:

<com.pedro.rtplibrary.view.OpenGlView
 android:id="@+id/openGlView"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:keepScreenOn="true"
 android:visibility="gone"
 app:keepAspectRatio="true"
 app:aspectRatioMode="adjust"/>
Wie man eine mobile Streaming-App für Android erstellt

4. Vorbereitungen für das Streamen

Um das Objekt RtmpCamera2 zu initialisieren, benötigen Sie das Objekt OpenGlView und die Schnittstellenimplementierung ConnectCheckerRtmp:

private val connectCheckerRtmp = object : ConnectCheckerRtmp {
    override fun onAuthErrorRtmp() {      _toastMessageId.postValue(R.string.auth_error)
    }
    override fun onAuthSuccessRtmp() {
        _toastMessageId.postValue(R.string.auth_success)
     }
     override fun onConnectionFailedRtmp(reason: String) {
         _toastMessageId.postValue(R.string.connection_failed)
         stopBroadcast()
      }
      override fun onConnectionStartedRtmp(rtmpUrl: String) {}
      override fun onConnectionSuccessRtmp() {
          _toastMessageId.postValue(R.string.connection_success)
          _streamState.postValue(StreamState.PLAY)
        }
        override fun onDisconnectRtmp() {
            _toastMessageId.postValue(R.string.disconnected)
            _streamState.postValue(StreamState.STOP)
         }
         override fun onNewBitrateRtmp(bitrate: Long) {}
}

Um die adaptive Bitrate zu verwenden, müssen Sie einige Ergänzungen an der Implementierung dieser Schnittstelle vornehmen:

//...
 private lateinit var bitrateAdapter: BitrateAdapter
 override fun onConnectionSuccessRtmp() {
      bitrateAdapter = BitrateAdapter { bitrate ->
 rtmpCamera2?.setVideoBitrateOnFly(bitrate)
      }.apply {
 setMaxBitrate(StreamParameters.maxBitrate)
      }   _toastMessageId.postValue(R.string.connection_success)
     _currentBitrate.postValue(rtmpCamera2?.bitrate)
_streamState.postValue(StreamState.PLAY)
 }
 //...
 override fun onNewBitrateRtmp(bitrate: Long) {    
bitrateAdapter.adaptBitrate(bitrate)
 _currentBitrate.postValue(bitrate.toInt())
 disableStreamingAfterTimeOut()
 }

Fügen Sie einen Callback zum Objekt OpenGlView hinzu. Die Callback-Methoden werden dazu verwendet, die Kameravorschau zu starten und zu beenden:

private val surfaceHolderCallback = object : SurfaceHolder.Callback {
 override fun surfaceCreated(holder: SurfaceHolder) {
 rtmpCamera2?.startPreview(
 StreamParameters.resolution.width,
 StreamParameters.resolution.height
 )
 }
 override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
 override fun surfaceDestroyed(holder: SurfaceHolder) {
 rtmpCamera2?.stopPreview()
 }
 }
binding.openGlView.holder.addCallback(surfaceHolderCallback)

Erstellen Sie das Objekt RtmpCamera2, das für das Streaming verwendet werden soll:

rtmpCamera2 = RtmpCamera2(openGlView, connectCheckerRtmp)

5. Starten und Beenden von Livestreams

Legen Sie die Video- und Audioparameter fest, und starten Sie den Livestream.

Livestream mit Standardparametern:

fun startBroadcast(rtmpUrl: String) {
      rtmpCamera2?.let {
             if (!it.isStreaming) {
                  if (it.prepareAudio() && it.prepareVideo()) {
 _streamState.value = StreamState.PLAY
 it.startStream(rtmpU
                  } else {
   _streamState.value = StreamState.STOP
_toastMessageId.value = R.string.error_preparing_stream
                  }
           }
     }
}

Livestream mit benutzerdefinierten Parametern:

fun startBroadcast(rtmpUrl: String) {
     val audioIsReady = rtmpCamera2.prepareAudio(
  StreamParameters.audioBitrate,
  StreamParameters.sampleRate,
   StreamParameters.isStereo,
 StreamParameters.echoCanceler,
 StreamParameters.noiseSuppressor
      )
      val videoIsReady = rtmpCamera2.prepareVideo(
 StreamParameters.resolution.width,
 StreamParameters.resolution.height,
          StreamParameters.fps,
 StreamParameters.startBitrate,
 StreamParameters.iFrameIntervalInSeconds,
 CameraHelper.getCameraOrientation(getApplication())
       )
       rtmpCamera2?.let {
               if (!it.isStreaming) {
                   if (audioIsReady && videoIsReady) {
     _streamState.value = StreamState.PLAY
  it.startStream(rtmpU
                    } else {
 _streamState.value = StreamState.STOP
 _toastMessageId.value = R.string.error_preparing_stream
                     }
              }
       }
}

Den Stream beenden:

fun stopBroadcast() {
     rtmpCamera2?.let {
            if (it.isStreaming) {
               _streamState.value = StreamState.STOP
               it.stopStream()
            }
       }
}

Integration mit Gcore Streaming-Plattform

Erstellen eines Kontos bei Gcore

Um die Streaming-Plattform in das Projekt zu integrieren, müssen Sie mit Ihrer E-Mail und Ihrem Kennwort ein kostenfreies Konto bei Gcore erstellen.

Aktivieren Sie den Dienst, indem Sie Free Live oder einen anderen geeigneten Tarif wählen.

Für die Interaktion mit Gcore Streaming-Plattform werden wir die Gcore-API verwenden. Wir werden Anfragen unter Verwendung von Retrofit in Verbindung mit RxJava ausführen. Sie können aber auch jede andere Methode zur Übermittlung von HTTP Anfragen verwenden, wenn Sie möchten.

Autorisierung

Loggen Sie sich ein, um mit der API zu arbeiten. Verwenden Sie die E-Mail und das Kennwort, die Sie bei der Registrierung eingegeben haben, um den Access Token zu erhalten, den Sie für weitere Anfragen benötigen.

class AuthRequestBody(
     @SerializedName("username") val eMail: String,
     @SerializedName("password") val password: String,
@SerializedName("one_time_password") val oneTimePassword: String = "authenticator passcode"
)
interface AuthApi {
        @POST("./auth/jwt/login")
        fun performLogin(@Body body: AuthRequestBody): Single<AuthResponse>
}
private fun auth(eMail: String, password: String) {
     val requestBody = AuthRequestBody(eMail = eMail, password = password)
      compositeDisposable.add(
 (requireActivity().application as GCoreApp).authApi
 .performLogin(requestBody)
 .subscribeOn(Schedulers.computation())
 .observeOn(AndroidSchedulers.mainThread())
   .auth(requireActivity(), requestBody)
                    .subscribe({ authResponse ->
showToast(R.string.logged_success)
     saveAuthData(requestBody, authResponse)
 routeToStreams()
                     }, {
 showToast(R.string.logged_fail)
                     })
     )
}

Die PUSH URL abrufen

Es gibt zwei Möglichkeiten, die URL zum Senden des RTMP Streams abzurufen:

Methode 1. Senden Sie die Anfrage Get all live streams, um alle Livestreams abzurufen. Als Antwort erhalten Sie Daten über alle in Ihrem Konto erstellten Streams.

Ein Beispiel für das Senden einer Anfrage:

interface StreamsApi {
 /**
 * @param page integer; Query parameter. Use it to list the paginated content
 * @param with_broadcasts integer; Query parameter.
 * Set to 1 to get details of the broadcasts associated with the stream
 */
 @GET("./vp/api/streams")
 fun getStreams(
 @Header("Authorization") accessToken: String,
 @Query("page") page: Int,
 @Query("with_broadcasts") withBroadcasts: Int = 1
 ): Single<List<StreamItemResponse>>
 //...
}
private fun loadStreamItems(page: Int = 1) {
 val accessToken = getAccessToken()
 var currentPage = page
 if (currentPage == 1) {
 streamItems.clear()
 }
 compositeDisposable.add(
 (requireActivity().application as GCoreApp).streamsApi
 .getStreams("Bearer $accessToken", page = currentPage)
 .subscribeOn(Schedulers.computation())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe({
 if (it.isNotEmpty()) {
 it.forEach { streamItemResponse ->
 streamItems.add(StreamItemModel.getInstance(streamItemResponse))
 }
 loadStreamItems(page = ++currentPage)
 } else {
 updateDataInAdapter()
 }
 }, {
 it.printStackTrace()
 })
 )
}

Methode 2. Senden Sie die Anfrage Get live stream, um einen bestimmten Livestream abzurufen. Als Antwort erhalten Sie nur Daten über den angegebenen Stream, sofern dieser existiert.

Ein Beispiel für das Senden einer Anfrage:

interface StreamsApi {
 //...
 @GET("/vp/api/streams/{stream_id}")
 fun getStreamDetailed(
 @Header("Authorization") accessToken: String,
 @Path("stream_id") streamId: Int
 ): Single<StreamDetailedResponse>
 //...
}
private fun getStreamDetailedInfo(streamId: Int) {
 val accessToken = getAccessToken()
 compositeDisposable.add(
 (requireActivity().application as GCoreApp).streamsApi
 .getStreamDetailed("Bearer $accessToken", streamId)
 .subscribeOn(Schedulers.computation())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe({ streamDetailedInfo ->
 //...
 }, {
 it.printStackTrace()
 })
 )
}

Die Antworten auf diese Anfragen enthalten eine push_url, die als URL für das Senden des RTMP Streams verwendet wird. Sobald der Stream beginnt, wird die Übertragung automatisch gestartet. Wählen Sie den gewünschten Stream in Ihrem persönlichen Konto aus. Sie können die Vorschau verwenden, bevor Sie den Stream auf Ihrer Website oder in Ihrem Player bereitstellen.

Wiedergabe eines aktiven Streams

Mit Gcore Streaming-Plattform können Sie Streams auf Ressourcen von Drittanbietern in verschiedenen Formaten, einschließlich HLS, übertragen.

In unserem Beispiel berücksichtigen wir kein simultanes Streamen und Wiedergeben auf einem Gerät. Stattdessen sollte das Streamen von einem anderen Gerät aus gestartet werden.

Um den aktiven Stream wiederzugeben, verwenden Sie den standardmäßigen Android MediaPlayer. Wenn Sie mehr Kontrolle über den Stream und die Anpassungsmöglichkeiten des Players haben möchten, empfehlen wir ExoPlayer.

On-Screen-Display

Um den Videostream auf dem Smartphone-Bildschirm anzuzeigen, ist VideoView erforderlich. Es sollte dort zu „Layout Activity“ oder „Fragment“ hinzugefügt werden, wo Sie den Stream abspielen möchten:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:background="@color/black"
 android:id="@+id/streamPlayer"
 tools:context=".screens.StreamPlayerFragment">
 <VideoView
 android:id="@+id/videoView"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_gravity="center"/>
 ...
</FrameLayout>

Wiedergabe starten

Vor der Wiedergabe sollte die hls_playlist_url des aktiven Streams in den Player eingebettet werden, während dieser initialisiert wird. Die hls_playlist_url wird in der Antwort auf die oben erwähnte Anfrage „Get livestream“ zurückgegeben.

Initialisierung des Players:

private fun initializePlayer(streamUri: String) {
 val videoView = binding.videoView
 videoView.setVideoURI(Uri.parse(streamUri))
 val mediaController = MediaController(videoView.context)
 mediaController.setMediaPlayer(videoView)
 videoView.setMediaController(mediaController)
 videoView.setOnPreparedListener {
 binding.progressBar.visibility = View.GONE
 videoView.start()
 }
 videoView.setonerrorListener(mediaPlayeronerrorListener)
}

Sobald die Initialisierung abgeschlossen ist, können Sie die Wiedergabe durch Aufruf der Methode videoView.start() starten.

Starten des Players:

private fun releasePlayer() {
 binding.videoView.stopPlayback()
}

Zusammenfassung

Anhand unserer Beispiele ist das Einrichten eines Livestreams in einer Android-Anwendung denkbar einfach und nimmt nicht viel Zeit in Anspruch. Alles, was Sie dazu benötigen, ist die Open-Source-Bibliothek rtmp-rtsp-stream-client-java und unsere Streaming-Plattform.

Gcore Streaming-API

Jeglichen im Artikel erwähnten Code finden Sie auf GitHub.

More about Streaming Platform

Wie man eine mobile Streaming-App für Android erstellt

Melden Sie sich
für unseren Newsletter an

Erhalten Sie die neuesten Branchentrends, exklusive Einblicke und Gcore-Updates
direkt in Ihren Posteingang.