Compare commits

...

6 Commits

Author SHA1 Message Date
pie
1287bbdb53 Fixes default values and adds explanation message for permission request
Some checks failed
Build / build (push) Has been cancelled
2026-05-14 14:27:40 +02:00
pie
707e1295c5 Fix extra signing block problem on Fdroid
Some checks failed
Build / build (push) Has been cancelled
2026-04-29 23:07:37 +02:00
pie
ca26daa3ee Added minimal metadata
Some checks failed
Build / build (push) Has been cancelled
2026-04-29 16:39:07 +02:00
pie
fd712c54b8 Renamed main project
Some checks failed
Build / build (push) Has been cancelled
2026-04-29 15:11:23 +02:00
pie
2ed0cb1a52 Changed app id
Some checks failed
Build / build (push) Has been cancelled
2026-04-28 15:34:49 +02:00
pie
1f136f9aea Terrible settings to make the amazing streampack boilerplate code usable.
Some checks failed
Build / build (push) Has been cancelled
2026-04-15 21:12:26 +02:00
57 changed files with 254 additions and 230 deletions

View File

@@ -11,8 +11,15 @@ android {
version = release(36)
}
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}
defaultConfig {
applicationId = "io.github.thibaultbee.streampack.app"
applicationId = "org.esiliati.repo.picostreaming.app"
minSdk = 24
targetSdk = 36
versionCode = 1
@@ -58,8 +65,9 @@ dependencies {
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.lifecycle.runtime.ktx)
implementation(libs.preference)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
}

View File

@@ -1,19 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- For play store -->
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<!-- Required permissions -->
android:required="false" /> <!-- Required permissions -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
@@ -21,12 +19,13 @@
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.MyStreamingApp"
android:theme="@style/Theme.PicoStreamingApp"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -38,6 +37,15 @@
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:parentActivityName=".MainActivity" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
</application>
</manifest>
</manifest>

View File

@@ -2,19 +2,23 @@ package io.github.thibaultbee.streampack.app
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.util.Log
import android.view.WindowManager.LayoutParams
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import io.github.thibaultbee.streampack.app.databinding.ActivityMainBinding
import io.github.thibaultbee.streampack.core.elements.sources.video.camera.extensions.defaultCameraId
import io.github.thibaultbee.streampack.core.streamers.lifecycle.StreamerActivityLifeCycleObserver
import io.github.thibaultbee.streampack.app.utils.PermissionsManager
import io.github.thibaultbee.streampack.app.utils.showDialog
import io.github.thibaultbee.streampack.app.utils.toast
import io.github.thibaultbee.streampack.core.elements.sources.video.camera.extensions.defaultCameraId
import io.github.thibaultbee.streampack.core.streamers.lifecycle.StreamerActivityLifeCycleObserver
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
@@ -41,7 +45,7 @@ class MainActivity : AppCompatActivity() {
// Explain why we need permissions
showDialog(
title = "Permissions denied",
message = "Explain why you need to grant $permissions permissions to stream",
message = getString(R.string.message),
positiveButtonText = R.string.accept,
onPositiveButtonClick = { onRequiredPermissionLastTime() },
negativeButtonText = R.string.denied
@@ -70,6 +74,24 @@ class MainActivity : AppCompatActivity() {
}
private fun bindProperties() {
val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{
val streamResolution = SettingsActivity.getResolution(applicationContext)
val (w, h) = streamResolution.split("|").map { it.toInt() }.let { it[0] to it[1] }
val streamBitrate = SettingsActivity.getBitrate(applicationContext)
lifecycleScope.launch {
viewModel.setVideoConfig(w, h, streamBitrate.toInt())
}
}
binding.settingsButton.setOnClickListener { view ->
val intent: Intent = Intent(this, SettingsActivity::class.java)
getContent.launch(intent)
}
binding.liveButton.setOnCheckedChangeListener { view, isChecked ->
if (view.isPressed) {
if (isChecked) {
@@ -77,9 +99,13 @@ class MainActivity : AppCompatActivity() {
* Dispatch from main thread is forced to avoid making network call on main thread
* with coroutines.
*/
// lifecycleScope.launch {
val streamUrl = SettingsActivity.getServer(applicationContext)
// }
lifecycleScope.launch {
try {
viewModel.startStream()
window.addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON)
viewModel.startStream(streamUrl)
} catch (e: Exception) {
binding.liveButton.isChecked = false
Log.e(TAG, "Failed to connect", e)
@@ -88,6 +114,7 @@ class MainActivity : AppCompatActivity() {
}
} else {
lifecycleScope.launch {
window.clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON)
viewModel.stopStream()
}
}
@@ -197,4 +224,4 @@ class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
}
}

View File

@@ -1,31 +1,42 @@
package io.github.thibaultbee.streampack.app
import android.app.AlertDialog
import android.Manifest
import android.content.DialogInterface
import android.media.AudioFormat
import android.media.MediaFormat
import android.text.InputType
import android.util.Log
import android.util.Size
import android.widget.EditText
import androidx.annotation.RequiresPermission
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import io.github.thibaultbee.streampack.app.data.rotation.RotationRepository
import io.github.thibaultbee.streampack.core.configuration.mediadescriptor.MediaDescriptor
import io.github.thibaultbee.streampack.core.elements.sources.audio.audiorecord.MicrophoneSourceFactory
import io.github.thibaultbee.streampack.core.elements.sources.video.camera.ICameraSource
import io.github.thibaultbee.streampack.core.interfaces.setCameraId
import io.github.thibaultbee.streampack.core.interfaces.startStream
import io.github.thibaultbee.streampack.core.pipelines.inputs.IVideoInput
import io.github.thibaultbee.streampack.core.streamers.single.AudioConfig
import io.github.thibaultbee.streampack.core.streamers.single.SingleStreamer
import io.github.thibaultbee.streampack.core.streamers.single.VideoConfig
import io.github.thibaultbee.streampack.core.utils.extensions.isClosedException
import io.github.thibaultbee.streampack.ext.rtmp.configuration.mediadescriptor.RtmpMediaDescriptor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
class MainViewModel(
private val rotationRepository: RotationRepository,
val streamer: SingleStreamer
val streamer: SingleStreamer,
) : ViewModel() {
private val defaultDispatcher = Dispatchers.Default
@@ -70,13 +81,13 @@ class MainViewModel(
*
* Replace with a valid URL.
*/
suspend fun startStream() {
suspend fun startStream(streamUrl: String) {
_isTryingConnectionLiveData.postValue(true)
try {
/**
* For SRT, use srt://my.server.url:9998?streamid=myStreamId&passphrase=myPassphrase
*/
streamer.startStream("rtmp://my.server.url:1935/app/streamKey")
streamer.startStream(streamUrl)
} finally {
_isTryingConnectionLiveData.postValue(false)
}
@@ -133,6 +144,27 @@ class MainViewModel(
streamer.setVideoConfig(videoConfig)
}
/**
* Sets the video configuration.
*
* You can verify the device supported configuration with [SingleStreamer.getInfo].
*/
suspend fun setVideoConfig(w: Int, h: Int,fps: Int) {
/**
* There are other parameters in the [VideoConfig] such as:
* - bitrate
* - profile
* - level
* - gopSize
* They will be initialized with an appropriate default value.
*/
val videoConfig = VideoConfig(
mimeType = MediaFormat.MIMETYPE_VIDEO_AVC, resolution = Size(w, h), fps = fps
)
streamer.setVideoConfig(videoConfig)
}
/**
* Sets the microphone as the audio source.
*/

View File

@@ -0,0 +1,42 @@
package io.github.thibaultbee.streampack.app
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//PreferenceManager.setDefaultValues(this, R.xml.root_preferences, false);
setContentView(R.layout.settings_activity)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
}
}
companion object {
fun getServer(context: Context): String =
PreferenceManager.getDefaultSharedPreferences(context).getString("pref_server", "") ?: ""
fun getResolution(context: Context): String =
PreferenceManager.getDefaultSharedPreferences(context).getString("pref_resolution", "") ?: ""
fun getBitrate(context: Context): String =
PreferenceManager.getDefaultSharedPreferences(context).getString("pref_bitrate", "") ?: ""
}
}

View File

@@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z" />
</vector>

View File

@@ -15,6 +15,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/settingsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginEnd="32dp"
android:background="@drawable/ic_menu"
android:contentDescription="Settings"
android:textColor="@color/white"
android:textOff=""
android:textOn=""
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<ToggleButton
android:id="@+id/liveButton"
android:layout_width="wrap_content"

View File

@@ -0,0 +1,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MyStreamingApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.PicoStreamingApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
@@ -13,4 +13,4 @@
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
</resources>

View File

@@ -0,0 +1,28 @@
<resources>
<string-array name="resolution_entries">
<item>480px360p</item>
<item>720px480p</item>
<item>1080px720p</item>
</string-array>
<string-array name="resolution_values">
<item>480|360</item>
<item>720|480</item>
<item>1080|720</item>
</string-array>
<array name="default_resolution">
<item>1080|720</item>
</array>
<string-array name="bitrate_entries">
<item>15 fps</item>
<item>25 fps</item>
<item>50 fps</item>
</string-array>
<string-array name="bitrate_values">
<item>15</item>
<item>25</item>
<item>50</item>
</string-array>
<array name="default_bitrate">
<item>25</item>
</array>
</resources>

View File

@@ -1,5 +1,12 @@
<resources>
<string name="app_name">My Streaming App</string>
<string name="app_name">Pico Streaming App</string>
<string name="denied">Denied</string>
<string name="accept">Accept</string>
<string name="message">Video and Audio permissions are required to stream from main camera and microphone.</string>
<string name="title_activity_settings">Settings</string>
<!-- Preference Titles -->
<string name="messages_header">Connection</string>
<string name="sync_header">Sync</string>
</resources>

View File

@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MyStreamingApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.PicoStreamingApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
@@ -13,4 +13,4 @@
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
</resources>

View File

@@ -0,0 +1,33 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="@string/messages_header">
<EditTextPreference
app:key="pref_server"
app:title="Stream URL"
app:summary="srt://host:port or rtsp://..."
app:useSimpleSummaryProvider="true"
app:defaultValue=""/>
</PreferenceCategory>
<PreferenceCategory app:title="Video">
<ListPreference
app:key="pref_resolution"
app:title="Resolution"
app:summary="Set video acquisition resolution"
app:useSimpleSummaryProvider="true"
app:entries="@array/resolution_entries"
app:entryValues="@array/resolution_values"
app:defaultValue="@array/default_resolution" />
<ListPreference
app:key="pref_bitrate"
app:title="Bitrate"
app:summary="Video bitrate"
app:useSimpleSummaryProvider="true"
app:entries="@array/bitrate_entries"
app:entryValues="@array/bitrate_values"
app:defaultValue="@array/default_bitrate"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,5 @@
Minimal RTMP streaming app heavily based on StreamPack-boilerplate.
It really just adds a bunch of options to make it flexible enough to stream from an Android phone.
Sends video to specified RTMP/RTSP url using main camera and microphone.

View File

@@ -0,0 +1 @@
Minimal RTMP streaming app.

View File

@@ -0,0 +1 @@
PicoStreamingApp

View File

@@ -10,6 +10,7 @@ kotlin = "2.3.0"
lifecycleRuntimeKtx = "2.10.0"
material = "1.13.0"
streampack = "3.1.1"
preference = "1.2.0"
[libraries]
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
@@ -24,6 +25,7 @@ streampack-core = { group = "io.github.thibaultbee.streampack", name = "streampa
streampack-ui = { group = "io.github.thibaultbee.streampack", name = "streampack-ui", version.ref = "streampack" }
streampack-rtmp = { group = "io.github.thibaultbee.streampack", name = "streampack-rtmp", version.ref = "streampack" }
streampack-srt = { group = "io.github.thibaultbee.streampack", name = "streampack-srt", version.ref = "streampack" }
preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View File

@@ -19,5 +19,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "MyStreamingApp"
rootProject.name = "PicoStreamingApp"
include(":app")