Compare commits

...

55 Commits

Author SHA1 Message Date
Schoumi 9e82b24cf5 Merge pull request 'Bump versions for v1.2 release' (#56) from release-v1.2 into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/56
2024-05-17 09:30:45 +00:00
Schoumi 8b26069bd9 Bump versions 2024-05-17 11:10:14 +02:00
Schoumi 24bcf9e026 Merge pull request 'fix camera feature needed for the app' (#55) from fix_camera_feature into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/55
2024-05-17 09:02:45 +00:00
Schoumi 767d450d75 fix camera feature needed for the app 2024-05-17 11:02:21 +02:00
Schoumi 15ec63cf18 Merge pull request 'rtmp_port_close' (#54) from rtmp_port_close into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/54
2024-05-17 08:30:21 +00:00
Schoumi 65a8b99dae Merge branch 'master' into rtmp_port_close 2024-05-17 08:29:37 +00:00
Schoumi 611ac54510 ammend FAQ.md 2024-05-17 10:28:04 +02:00
Schoumi 375a2daef8 init some kind of a FAQ 2024-05-17 10:24:21 +02:00
Schoumi 0ed019bb34 Merge pull request 'Fix Themes:' (#53) from fix-dark-theme into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/53
2024-05-17 06:39:16 +00:00
Schoumi e19a0061ae Fix Themes:
- Theme Light on Stream
- Fixed color on Stream
- Logo background removed
2024-05-17 08:37:16 +02:00
Schoumi 2280a40e6e Merge pull request 'Handle 2FA for instance user (#48)' (#52) from 2FA into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/52
2024-05-16 19:23:48 +00:00
Schoumi 693c6088d1 Handle 2FA for instance user (#48) 2024-05-16 21:22:36 +02:00
Schoumi c56f33b7ab Merge pull request 'remove idea files from tracking' (#50) from remove_idea into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/50
2024-04-17 16:16:23 +00:00
Schoumi 63afc87b36 remove idea files from tracking 2024-04-17 18:14:35 +02:00
Schoumi f55b579139 Merge pull request 'Add Replay Settings support' (#49) from replay_5_2 into master
Add Replay Settings support
Change forms-data to json format in live stream initiation

Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/49
2024-04-17 16:10:57 +00:00
Schoumi 7c4850a1ea Merge branch 'master' into replay_5_2 2024-04-17 16:10:19 +00:00
Schoumi 8b509953ca change steam options from forms-data to json 2024-04-17 18:06:35 +02:00
Schoumi 517c6a043a Merge pull request 'German and Polish translations' (#44) from translates into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/44
2023-08-15 19:23:48 +00:00
Schoumi 53f16d1ad7 remove old unusable translation => exist on weblate 2023-08-15 21:03:19 +02:00
Schoumi d93f4b5c84 Translated using Weblate (Polish)
Currently translated at 99.6% (277 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/pl/
2023-08-15 20:50:00 +02:00
Mi Klo 58f68d1797 Translated using Weblate (Polish)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/pl/
2023-08-15 20:49:43 +02:00
Mi Klo ccf0eb80db Translated using Weblate (Polish)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/pl/
2023-08-15 20:49:23 +02:00
Mi Klo cc782668c5 Added translation using Weblate (Polish) 2023-08-15 20:49:08 +02:00
Felix Tymcik 1839e91896 Translated using Weblate (German)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/de/
2023-08-15 20:48:16 +02:00
J. Lavoie cfee1af487 Translated using Weblate (German)
Currently translated at 64.0% (178 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/de/
2023-08-15 20:47:49 +02:00
Schoumi 06ab71610f Merge pull request 'rtmp_update' (#40) from rtmp_update into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/40
2022-10-04 17:52:05 +02:00
Schoumi cfffa2e418 Fix some rotation issue 2022-10-04 17:31:53 +02:00
Schoumi 3abf6c3554 Update ci to woodpecker 2022-07-14 13:54:53 +02:00
Schoumi 556bca4622 Update ci to woodpecker 2022-07-14 13:38:36 +02:00
Schoumi 82dae368e0 fix stream orientation 2022-07-14 13:33:30 +02:00
Schoumi b0dace54d7 Upgrade RTMP-RTSP from 1.8.2 (custom) to 2.1.9 (standard)
-> breaks orientation at the moment
Upgrade gradle plugin version
2022-06-16 12:34:41 +02:00
Schoumi 1709bc3688 Merge pull request 'Completed translation' (#39) from translates into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/39
2022-06-15 13:38:15 +02:00
Xosé M 2073a12bbb Translated using Weblate (Galician)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/gl/
2022-03-29 18:23:14 +02:00
Xosé M e8bc1ffc92 Translated using Weblate (Galician)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/gl/
2022-03-29 18:23:14 +02:00
Weblate 3e58b85ed7 Added translation using Weblate (Galician) 2022-03-29 18:23:14 +02:00
Ihor Hordiichuk 93557ae115 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/uk/
2022-03-29 18:05:34 +02:00
Ihor Hordiichuk 81a7401188 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/uk/
2022-03-29 18:05:34 +02:00
Ihor Hordiichuk c559539485 Added translation using Weblate (Ukrainian) 2022-03-29 18:05:34 +02:00
Jiří Podhorecký d127ebff52 Translated using Weblate (Czech)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/cs/
2022-03-29 17:23:54 +02:00
Jiří Podhorecký fefaf147d3 Translated using Weblate (Czech)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/cs/
2022-03-29 17:23:54 +02:00
Jiří Podhorecký 3548381019 Translated using Weblate (Czech)
Currently translated at 7.1% (20 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/cs/
2022-03-29 17:23:54 +02:00
Jiří Podhorecký c48dd8b207 Added translation using Weblate (Czech) 2022-03-29 17:23:54 +02:00
Balázs Meskó 295a4dcea4 Translated using Weblate (Hungarian)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/hu/
2022-03-29 16:57:59 +02:00
Balázs Meskó 67d7453886 Translated using Weblate (Hungarian)
Currently translated at 99.6% (277 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/hu/
2022-03-29 16:57:59 +02:00
Balázs Meskó 131c813710 Added translation using Weblate (Hungarian) 2022-03-29 16:57:59 +02:00
badlop 271ed841d8 Translated using Weblate (Spanish)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/es/
2022-03-29 16:14:41 +02:00
Discapacidad cinco 8b01d8557b Translated using Weblate (Spanish)
Currently translated at 100.0% (2 of 2 strings)

Translation: PeerTube Live/Store
Translate-URL: https://hosted.weblate.org/projects/peertube-live/store/es/
2022-03-29 16:14:41 +02:00
Discapacidad cinco db6fd2648b Translated using Weblate (Spanish)
Currently translated at 99.6% (277 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/es/
2022-03-29 16:14:41 +02:00
Discapacidad cinco 1d5a2c1682 Added translation using Weblate (Spanish) 2022-03-29 16:14:41 +02:00
J. Lavoie f26d9c4bf4 Translated using Weblate (French)
Currently translated at 100.0% (278 of 278 strings)

Translation: PeerTube Live/App
Translate-URL: https://hosted.weblate.org/projects/peertube-live/app/fr/
2022-03-29 15:57:37 +02:00
Schoumi 736d2aee23 Merge pull request 'Mise à jour de 'README.md'' (#31) from Poussinou/PeerTubeLive:master into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/31
2022-03-28 19:37:25 +02:00
Poussinou 8e570295d9 Mise à jour de 'README.md' 2021-12-05 14:20:59 +01:00
Schoumi 186396dda1 Merge pull request 'put back incomplete translations' (#23) from translates into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/23
2021-10-19 14:03:47 +02:00
Schoumi edef1be85f put back incomplete translations 2021-10-19 14:03:06 +02:00
Schoumi 0c63ad695d Merge pull request 'release v1.1' (#22) from release_1.1 into master
Reviewed-on: https://codeberg.org/Schoumi/PeerTubeLive/pulls/22
2021-10-19 13:29:15 +02:00
402 changed files with 22944 additions and 9881 deletions

View File

@ -1,9 +0,0 @@
---
kind: pipeline
type: exec
name: default
steps:
- name: build
commands:
- bash ./gradlew assembleDebug

7
.gitignore vendored
View File

@ -1,12 +1,7 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.idea
.DS_Store
/build
/captures

View File

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
<State>
<id>AccessibilityLintAndroid</id>
</State>
<State>
<id>Android</id>
</State>
<State>
<id>Bidirectional TextInternationalizationLintAndroid</id>
</State>
<State>
<id>Chrome OSCorrectnessLintAndroid</id>
</State>
<State>
<id>Class structureJava</id>
</State>
<State>
<id>CorrectnessLintAndroid</id>
</State>
<State>
<id>IconsUsabilityLintAndroid</id>
</State>
<State>
<id>InternationalizationLintAndroid</id>
</State>
<State>
<id>Java</id>
</State>
<State>
<id>Javadoc issuesJava</id>
</State>
<State>
<id>LintAndroid</id>
</State>
<State>
<id>LintLintAndroid</id>
</State>
<State>
<id>MessagesCorrectnessLintAndroid</id>
</State>
<State>
<id>PerformanceLintAndroid</id>
</State>
<State>
<id>SecurityLintAndroid</id>
</State>
<State>
<id>TypographyUsabilityLintAndroid</id>
</State>
<State>
<id>UsabilityLintAndroid</id>
</State>
</expanded-state>
</profile-state>
</entry>
</component>
</project>

3
.idea/.gitignore vendored
View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -1 +0,0 @@
Peertube Live

View File

@ -1,139 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="150" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="schoumi">
<words>
<w>datas</w>
<w>rtmp</w>
</words>
</dictionary>
</component>

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.2.1" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/encoder" />
<option value="$PROJECT_DIR$/rtmp" />
<option value="$PROJECT_DIR$/rtplibrary" />
<option value="$PROJECT_DIR$/rtsp" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
</component>
</project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

9
.woodpecker.yml Normal file
View File

@ -0,0 +1,9 @@
pipeline:
build:
image: eclipse-temurin:11
commands:
- bash ./gradlew assembleDebug
environment:
- ANDROID_HOME=/mnt/sdk
volumes:
- /home/woodpecker/sdk:/mnt/sdk

6
FAQ.md Normal file
View File

@ -0,0 +1,6 @@
# The stream doesn't start and display: "Your livestream ended because of a network problem"
If you're the instance administrators, check that the rtmp/rtmps port are open on your server. By default rtmp 1935, rtmps 1936. Check your production.yaml.
If you're not the adminstrators, ask them if the configuration is ok, before reporting a bug.

View File

@ -11,6 +11,13 @@ Stream your phone camera to a PeerTube instance with this Android app.
PeerTube, developped by [Framasoft](https://framasoft.org), is the libre and decentralized alternative to video platforms. Join PeerTube at [https://joinpeertube.org](https://joinpeertube.org)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/fr.mobdev.peertubelive/)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png"
alt="Get it on Google Play"
height="80">](https://play.google.com/store/apps/details?id=fr.mobdev.peertubelive)
## Licence
The app itself is licensed AGPLv3+

View File

@ -5,21 +5,15 @@ apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'
android {
compileSdkVersion 31
buildToolsVersion "31.0.0"
compileSdkVersion 34
defaultConfig {
applicationId "fr.mobdev.peertubelive"
minSdkVersion 21
targetSdkVersion 31
versionCode 2
versionName "1.1"
targetSdkVersion 34
versionCode 3
versionName "1.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables
{
useSupportLibrary true
}
}
buildTypes {
@ -29,16 +23,18 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
buildFeatures {
dataBinding true
buildConfig true
}
namespace 'fr.mobdev.peertubelive'
}
@ -46,14 +42,14 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation project(':rtplibrary')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.mobdev.peertubelive">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
@ -19,7 +20,6 @@
<activity
android:name="fr.mobdev.peertubelive.activity.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -27,8 +27,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="fr.mobdev.peertubelive.activity.StreamActivity"
android:theme="@style/AppTheme.Light" android:screenOrientation="portrait"/>
<activity android:name="fr.mobdev.peertubelive.activity.StreamActivity" android:screenOrientation="portrait"/>
<activity android:name="fr.mobdev.peertubelive.activity.CreateLiveActivity" />
</application>

View File

@ -126,7 +126,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -150,7 +150,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -166,7 +166,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -190,7 +190,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -214,7 +214,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -238,7 +238,7 @@ class CreateLiveActivity : AppCompatActivity() {
updateView(null)
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
inError = true
updateView(error)
}
@ -338,7 +338,7 @@ class CreateLiveActivity : AppCompatActivity() {
}
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
runOnUiThread {
binding.error.text = error
binding.error.visibility = View.VISIBLE

View File

@ -32,12 +32,14 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.pedro.encoder.input.video.CameraHelper
import com.pedro.rtmp.utils.ConnectCheckerRtmp
import com.pedro.rtplibrary.rtmp.RtmpCamera2
import net.ossrs.rtmp.ConnectCheckerRtmp
import fr.mobdev.peertubelive.R
import fr.mobdev.peertubelive.databinding.StreamBinding
import fr.mobdev.peertubelive.manager.InstanceManager.EXTRA_DATA
import fr.mobdev.peertubelive.objects.StreamData
import java.util.*
import kotlin.collections.ArrayList
class StreamActivity : AppCompatActivity() {
@ -50,10 +52,11 @@ class StreamActivity : AppCompatActivity() {
private var surfaceInit: Boolean = false
private var permissionGiven: Boolean = false
private var streamIsActive: Boolean = false
private var screenOrientation: Int = 0
private var screenOrientation: Int = -1
private var lastScreenOrientation: Int = 0
private var orientationCounter: Int = 0
private var rotationIsLanternEnabled: Boolean = true
private var rotationIsEnabled: Boolean = true
private var orientationTimer: Timer = Timer()
companion object {
const val BACKGROUND :Int = 1
@ -69,46 +72,11 @@ class StreamActivity : AppCompatActivity() {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding = DataBindingUtil.setContentView(this, R.layout.stream)
orientationEventListener = object: OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){
override fun onOrientationChanged(orientation: Int) {
if(orientation < 0 || !rotationIsLanternEnabled)
return
var localOrientation: Int
localOrientation = when (orientation) {
in 45..135 -> {
90
}
in 135..225 -> {
180
}
in 225..315 -> {
270
}
else -> {
0
}
}
if(localOrientation != lastScreenOrientation) {
lastScreenOrientation = localOrientation
orientationCounter = 0
} else {
orientationCounter++
}
if (lastScreenOrientation != screenOrientation && orientationCounter > 30) {
screenOrientation = lastScreenOrientation
rtmpCamera2.glInterface.setStreamRotation(screenOrientation)
if (screenOrientation == 90) {
localOrientation = 270
} else if(screenOrientation == 270) {
localOrientation = 90
}
binding.flash.rotation = localOrientation.toFloat()
binding.muteMicro.rotation = localOrientation.toFloat()
binding.switchCamera.rotation = localOrientation.toFloat()
}
handlerOrientation(orientation)
}
}
orientationEventListener.enable()
@ -144,14 +112,11 @@ class StreamActivity : AppCompatActivity() {
binding.flash.visibility = View.GONE
binding.rotation.setOnClickListener {
if (rotationIsLanternEnabled) {
rotationIsLanternEnabled = !rotationIsLanternEnabled
if (rotationIsEnabled)
binding.rotation.setImageResource(R.drawable.baseline_screen_lock_rotation_24)
}
else {
rotationIsLanternEnabled = !rotationIsLanternEnabled
else
binding.rotation.setImageResource(R.drawable.baseline_screen_rotation_24)
}
rotationIsEnabled = !rotationIsEnabled
}
binding.rotation.visibility = View.GONE
binding.surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
@ -260,6 +225,52 @@ class StreamActivity : AppCompatActivity() {
}
}
private fun handlerOrientation(orientation: Int) {
if(orientation < 0 || !rotationIsEnabled) {
return
}
var localOrientation: Int
localOrientation = when (orientation) {
in 45..135 -> {
90
}
in 135..225 -> {
180
}
in 225..315 -> {
270
}
else -> {
0
}
}
if(localOrientation != lastScreenOrientation) {
lastScreenOrientation = localOrientation
orientationTimer.cancel()
orientationTimer.purge()
orientationTimer = Timer()
orientationTimer.schedule(object : TimerTask() {
override fun run() {
if (lastScreenOrientation != screenOrientation) {
screenOrientation = lastScreenOrientation
rtmpCamera2.glInterface.setStreamRotation(screenOrientation)
if (screenOrientation == 90) {
localOrientation = 270
} else if(screenOrientation == 270) {
localOrientation = 90
}
binding.flash.rotation = localOrientation.toFloat()
binding.muteMicro.rotation = localOrientation.toFloat()
binding.switchCamera.rotation = localOrientation.toFloat()
binding.rotation.rotation = localOrientation.toFloat()
}
}
},3000)
}
}
private fun startStream() {
streamIsActive = true
binding.permissionInfo.visibility = View.GONE
@ -271,6 +282,10 @@ class StreamActivity : AppCompatActivity() {
binding.flash.visibility = View.VISIBLE
val connectChecker : ConnectCheckerRtmp = object : ConnectCheckerRtmp {
override fun onConnectionStartedRtmp(rtmpUrl: String) {
}
override fun onConnectionSuccessRtmp() {
runOnUiThread {
Toast.makeText(binding.root.context, "Connection success", Toast.LENGTH_SHORT).show();
@ -341,13 +356,19 @@ class StreamActivity : AppCompatActivity() {
width = 480
height = 360
}
null -> {
width = 1920
height = 1080
}
}
rtmpCamera2.startPreview(CameraHelper.Facing.BACK, width,height)
//start stream
if (rtmpCamera2.prepareAudio() && rtmpCamera2.prepareVideo(width,height,30, (width*height*30*0.076).toInt(),false,CameraHelper.getCameraOrientation(this))) {
if (rtmpCamera2.prepareAudio() && rtmpCamera2.prepareVideo(width,height,30, (width*height*30*0.076).toInt(),CameraHelper.getCameraOrientation(this),true)) {
println("peertubeurl "+streamData.url+"/"+streamData.key)
rtmpCamera2.startStream(streamData.url+"/"+streamData.key)
} else {
/**This device cant init encoders, this could be for 2 reasons: The encoder selected doesnt support any configuration setted or your device hasnt a H264 or AAC encoder (in this case you can see log error valid encoder not found) */

View File

@ -20,6 +20,7 @@ import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.DialogFragment
import fr.mobdev.peertubelive.R
@ -45,11 +46,15 @@ class AddInstanceDialog : DialogFragment() {
builder.setPositiveButton(R.string.connect, null)
builder.setNegativeButton(R.string.cancel) { dialog,_ -> dialog.dismiss() }
builder.setView(binding.root)
isCancelable = false
binding.errorUsername.visibility = View.GONE
binding.errorInstance.visibility = View.GONE
binding.errorPassword.visibility = View.GONE
binding.tryConnect.visibility = View.GONE
binding.tryConnectMsg.visibility = View.GONE
binding.twoFaTitle.visibility = View.GONE
binding.twoFa.visibility = View.GONE
binding.errorTwoFa.visibility = View.GONE
if (this::oAuthData.isInitialized)
{
@ -67,9 +72,11 @@ class AddInstanceDialog : DialogFragment() {
val username = binding.username.text.toString()
val password = binding.password.text.toString()
var instance = binding.instance.text.toString()
var twoFaToken = binding.twoFa.text.toString()
binding.errorUsername.visibility = View.GONE
binding.errorInstance.visibility = View.GONE
binding.errorPassword.visibility = View.GONE
binding.errorTwoFa.visibility = View.GONE
binding.error.visibility = View.GONE
var inError = false
if(username.isEmpty())
@ -100,6 +107,13 @@ class AddInstanceDialog : DialogFragment() {
inError = true
}
}
if(twoFaToken.isEmpty() && binding.twoFa.isVisible)
{
binding.errorTwoFa.visibility = View.VISIBLE
inError = true
}
if (!this::oAuthData.isInitialized && DatabaseManager.existsCredential(requireContext(),instance,username)) {
inError = true
binding.error.visibility = View.VISIBLE
@ -109,13 +123,16 @@ class AddInstanceDialog : DialogFragment() {
binding.errorUsername.visibility = View.GONE
binding.errorInstance.visibility = View.GONE
binding.errorPassword.visibility = View.GONE
binding.errorTwoFa.visibility = View.GONE
binding.error.visibility = View.GONE
binding.username.visibility = View.GONE
binding.password.visibility = View.GONE
binding.instance.visibility = View.GONE
binding.twoFa.visibility = View.GONE
binding.usernameTitle.visibility = View.GONE
binding.passwordTitle.visibility = View.GONE
binding.instanceTitle.visibility = View.GONE
binding.twoFaTitle.visibility = View.GONE
binding.tryConnect.visibility = View.VISIBLE
binding.tryConnectMsg.visibility = View.VISIBLE
@ -135,20 +152,54 @@ class AddInstanceDialog : DialogFragment() {
}
}
override fun onError(error: String?) {
override fun onError(code: String?, error: String?) {
Handler(Looper.getMainLooper()).post {
binding.error.visibility = View.VISIBLE
binding.tryConnect.visibility = View.GONE
binding.tryConnectMsg.visibility = View.GONE
binding.username.visibility = View.VISIBLE
binding.password.visibility = View.VISIBLE
binding.instance.visibility = View.VISIBLE
binding.usernameTitle.visibility = View.VISIBLE
binding.passwordTitle.visibility = View.VISIBLE
binding.instanceTitle.visibility = View.VISIBLE
binding.error.text = error
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = true
if (code.equals("missing_two_factor") || code.equals("invalid_two_factor")) {
binding.error.visibility = View.GONE
binding.tryConnect.visibility = View.GONE
binding.tryConnectMsg.visibility = View.GONE
binding.username.visibility = View.GONE
binding.password.visibility = View.GONE
binding.instance.visibility = View.GONE
binding.usernameTitle.visibility = View.GONE
binding.passwordTitle.visibility = View.GONE
binding.instanceTitle.visibility = View.GONE
binding.username.isEnabled = false
binding.password.isEnabled = false
binding.instance.isEnabled = false
binding.twoFa.setText("")
binding.twoFa.visibility = View.VISIBLE
binding.twoFaTitle.visibility = View.VISIBLE
binding.errorTwoFa.visibility = View.GONE
if (code.equals("invalid_two_factor")) {
binding.error.setText(R.string.invalid_two_fa_token)
binding.error.visibility = View.VISIBLE
}
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = true
} else {
binding.error.visibility = View.VISIBLE
binding.tryConnect.visibility = View.GONE
binding.tryConnectMsg.visibility = View.GONE
binding.username.visibility = View.VISIBLE
binding.password.visibility = View.VISIBLE
binding.instance.visibility = View.VISIBLE
binding.twoFa.visibility = View.GONE
binding.usernameTitle.visibility = View.VISIBLE
binding.passwordTitle.visibility = View.VISIBLE
binding.instanceTitle.visibility = View.VISIBLE
binding.twoFaTitle.visibility = View.GONE
binding.username.isEnabled = true
binding.password.isEnabled = true
binding.instance.isEnabled = true
binding.twoFa.setText("")
binding.error.text = error
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = true
}
}
}
@ -158,9 +209,9 @@ class AddInstanceDialog : DialogFragment() {
};
if (this::oAuthData.isInitialized) {
InstanceManager.getUserToken(requireContext(), instance, username, password, oAuthData, listener)
InstanceManager.getUserToken(requireContext(), instance, username, password, twoFaToken, oAuthData, listener)
} else {
InstanceManager.registerAccount(requireContext(), instance, username, password,listener)
InstanceManager.registerAccount(requireContext(), instance, username, password, twoFaToken,listener)
}
}
}

View File

@ -52,21 +52,22 @@ object InstanceManager {
private const val CONFIG_LIVE: String = "live"
private const val CONFIG_LIVE_ENABLED: String = "enabled"
private const val CONFIG_LIVE_SAVE_REPLAY: String = "allowReplay"
public const val INTERNAL_ERROR: String = "INTERNAL_ERROR"
fun registerAccount(context: Context, url: String, username: String, password: String, listener: InstanceListener) {
fun registerAccount(context: Context, url: String, username: String, password: String, twoFa: String, listener: InstanceListener) {
val registerUrl = url + BASE_API_ENDPOINT+ REGISTER_CLIENT_ENDPOINT
val internalListener: InstanceListener = object : InstanceListener {
override fun onSuccess(args: Bundle?) {
val oauthData: OAuthData? = args?.getParcelable(EXTRA_DATA)
oauthData?.baseUrl = url
if (oauthData != null)
getUserToken(context, url, username, password,oauthData, listener)
getUserToken(context, url, username, password, twoFa, oauthData, listener)
else
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -77,9 +78,9 @@ object InstanceManager {
oauthManager.register(context,registerUrl,internalListener)
}
fun getUserToken(context: Context, url: String, username: String, password: String, oauthData: OAuthData, listener: InstanceListener) {
fun getUserToken(context: Context, url: String, username: String, password: String, twoFa: String, oauthData: OAuthData, listener: InstanceListener) {
val userAccess = url + BASE_API_ENDPOINT + GET_USER_CLIENT_ENDPOINT
oauthManager.getUserToken(context, userAccess, username, password, oauthData, listener)
oauthManager.getUserToken(context, userAccess, username, password, twoFa, oauthData, listener)
}
private fun refreshToken(context: Context, url: String, oauthData: OAuthData, listener: InstanceListener) {
@ -91,7 +92,7 @@ object InstanceManager {
if(oauthData.expires < Calendar.getInstance().timeInMillis) {
refreshToken(context,url,oauthData,object: InstanceListener {
override fun onSuccess(args: Bundle?) {
val oauth: OAuthData? = args?.getParcelable(InstanceManager.EXTRA_DATA)
val oauth: OAuthData? = args?.getParcelable(EXTRA_DATA)
if (oauth != null) {
DatabaseManager.updateCredentials(context,oauth)
listener.onUpdateOAuthData(oauth)
@ -99,8 +100,8 @@ object InstanceManager {
}
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -119,12 +120,12 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val streamData = extractStreamData(response)
if (streamData == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
val results = Bundle()
@ -132,8 +133,8 @@ object InstanceManager {
listener.onSuccess(results)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -148,43 +149,50 @@ object InstanceManager {
// val comments: Boolean, val download: Boolean, val nsfw: Boolean, val saveReplay: Boolean
val createLiveUrl = url + BASE_API_ENDPOINT + CREATE_LIVE_ENDPOINT
val data = Bundle()
val boundary = "45fcc22"
var formData: String = prepareFormData(boundary,"channelId",streamSettings.channel.toString(),false)
formData += prepareFormData(boundary,"name",streamSettings.title,false)
formData += prepareFormData(boundary,"privacy",streamSettings.privacy.toString(),false)
val formData = JSONObject()
formData.put("channelId",streamSettings.channel)
formData.put("name",streamSettings.title)
formData.put("privacy",streamSettings.privacy)
if(streamSettings.category != null)
formData += prepareFormData(boundary,"category",streamSettings.category.toString(),false)
formData.put("category",streamSettings.category)
if(streamSettings.language != null)
formData += prepareFormData(boundary,"language",streamSettings.language.toString(),false)
formData.put("language",streamSettings.language.toString())
if(streamSettings.description != null)
formData += prepareFormData(boundary,"description",streamSettings.description.toString(),false)
formData.put("description",streamSettings.description)
if(streamSettings.licence != null)
formData += prepareFormData(boundary,"licence",streamSettings.licence.toString(),false)
formData += prepareFormData(boundary,"commentsEnabled",streamSettings.comments.toString(),false)
formData += prepareFormData(boundary,"nsfw",streamSettings.nsfw.toString(),false)
formData += prepareFormData(boundary,"downloadEnabled",streamSettings.download.toString(),streamSettings.saveReplay == null)
if (streamSettings.saveReplay != null)
formData += prepareFormData(boundary,"saveReplay",streamSettings.saveReplay.toString(),true)
data.putString(CONTENT_TYPE,"multipart/form-data; boundary=$boundary")
data.putString(CONTENT_DATA,formData)
formData.put("licence",streamSettings.licence)
formData.put("commentsEnabled",streamSettings.comments.toString())
formData.put("nsfw",streamSettings.nsfw.toString())
formData.put("downloadEnabled",streamSettings.download.toString())
if (streamSettings.saveReplay != null) {
formData.put("saveReplay",streamSettings.saveReplay.toString())
val privacyReplay = JSONObject()
privacyReplay.put("privacy",streamSettings.privacy)
formData.put("replaySettings",privacyReplay)
}
data.putString(CONTENT_TYPE,"application/json")
data.putString(CONTENT_DATA,formData.toString())
println(formData.toString())
val internalListener = object: InstanceListener {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val liveId = extractLiveId(response)
if (liveId == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
getStreamKey(context,url,oauthData,liveId,listener)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -207,8 +215,8 @@ object InstanceManager {
}
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -227,21 +235,21 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val channelList = extractChannelData(response)
if (channelList == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
args.putParcelableArrayList(EXTRA_DATA, channelList)
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -258,13 +266,13 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val categoryList = extractMapData<Int>(response,0)
if (categoryList == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
categoryList[""]=0
@ -272,8 +280,8 @@ object InstanceManager {
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -291,21 +299,21 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val privacyList = extractMapData<Int>(response,0)
if (privacyList == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
args.putSerializable(EXTRA_DATA, privacyList)
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -322,13 +330,13 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val licencesList = extractMapData<Int>(response,0)
if (licencesList == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
licencesList[""]=0
@ -336,8 +344,8 @@ object InstanceManager {
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -354,13 +362,13 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val languageList = extractMapData<String>(response,"")
if (languageList == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
languageList[""]=""
@ -368,8 +376,8 @@ object InstanceManager {
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -386,20 +394,20 @@ object InstanceManager {
override fun onSuccess(args: Bundle?) {
val response = args?.getString(EXTRA_DATA, null)
if (response == null) {
listener.onError(context.getString(R.string.unknwon_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.unknwon_error))
return
}
val configData = extractConfigData(response)
if (configData == null) {
listener.onError(context.getString(R.string.json_error))
listener.onError(INTERNAL_ERROR,context.getString(R.string.json_error))
return
}
args.putParcelable(EXTRA_DATA, configData)
listener.onSuccess(args)
}
override fun onError(error: String?) {
listener.onError(error)
override fun onError(code: String?, error: String?) {
listener.onError(code,error)
}
override fun onUpdateOAuthData(oauthData: OAuthData) {
@ -500,6 +508,7 @@ object InstanceManager {
}
}
@Suppress("UNCHECKED_CAST")
private fun <T> extractMapData(response: String, type: T): HashMap<String,T>? {
return try {
val json = JSONObject(response)
@ -517,24 +526,9 @@ object InstanceManager {
}
}
private fun prepareFormData(boundary: String, propertyName: String, property: String, lastData: Boolean): String {
val crlf = "\r\n"
var formData = "--$boundary$crlf"
formData+="Content-Disposition: form-data; name=\"$propertyName\"$crlf$crlf"
formData+="$property$crlf"
if(lastData) {
formData += "--$boundary--$crlf"
}
return formData
}
interface InstanceListener{
fun onSuccess(args: Bundle?)
fun onError(error: String?)
fun onError(code: String?, error: String?)
fun onUpdateOAuthData(oauthData: OAuthData)
}
}

View File

@ -53,12 +53,13 @@ class OAuthManager {
addMessage(message)
}
fun getUserToken(context: Context, url: String, username: String, password: String, oauthData: OAuthData, listener: InstanceManager.InstanceListener) {
fun getUserToken(context: Context, url: String, username: String, password: String, twoFa: String, oauthData: OAuthData, listener: InstanceManager.InstanceListener) {
val args = Bundle()
args.putString(URL, url)
args.putParcelable(OAUTH_DATA, oauthData)
args.putString(USERNAME, username)
args.putString(PASSWORD, password)
args.putString(TWO_FA,twoFa)
val message = Message()
message.type = Message.Message_Type.GET_USER_TOKEN
@ -119,11 +120,13 @@ class OAuthManager {
private const val URL: String = "URL"
private const val USERNAME: String = "USERNAME"
private const val PASSWORD: String = "PASSWORD"
private const val TWO_FA: String = "TWO_FA"
private const val OAUTH_DATA: String = "OAUTH_DATA"
private const val DATA: String = "DATA"
private const val EXTRA_DATA: String = "EXTRA_DATA"
private const val CONTENT_TYPE: String = "CONTENT_TYPE"
private const val CONTENT_DATA: String = "CONTENT_DATA"
fun addMessage(message: Message)
{
@ -153,7 +156,7 @@ class OAuthManager {
fun register(message: Message) {
if (!isConnectedToInternet(message.context)) {
message.listener?.onError(message.context.getString(R.string.network_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.network_error))
return
}
val url: String = message.args.getString(URL,"")
@ -170,7 +173,7 @@ class OAuthManager {
try {
inputStream = connection.inputStream
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e : Exception) {
e.printStackTrace()
@ -192,7 +195,7 @@ class OAuthManager {
message.listener?.onSuccess(result)
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR, message.context.getString(R.string.unknwon_error))
}
} else {
handleError(message,response)
@ -201,7 +204,7 @@ class OAuthManager {
fun getUserToken(message: Message) {
if (!isConnectedToInternet(message.context)) {
message.listener?.onError(message.context.getString(R.string.network_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.network_error))
return
}
val url: String = message.args.getString(URL, "")
@ -220,6 +223,10 @@ class OAuthManager {
}
var username: String = message.args.getString(USERNAME,"")
var password: String = message.args.getString(PASSWORD,"")
var twoFa: String = message.args.getString(TWO_FA,"")
if (twoFa.isNotEmpty()) {
connection.setRequestProperty("x-peertube-otp", twoFa)
}
username = URLEncoder.encode(username,"UTF-8")
password = URLEncoder.encode(password,"UTF-8")
@ -241,7 +248,7 @@ class OAuthManager {
try {
inputStream = connection.inputStream
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e : Exception) {
e.printStackTrace()
@ -270,7 +277,7 @@ class OAuthManager {
message.listener?.onSuccess(result)
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknwon_error))
}
} else {
handleError(message,response)
@ -280,7 +287,7 @@ class OAuthManager {
fun refreshToken(message: Message) {
if (!isConnectedToInternet(message.context)) {
message.listener?.onError(message.context.getString(R.string.network_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.network_error))
return
}
val url: String = message.args.getString(URL, "")
@ -313,7 +320,7 @@ class OAuthManager {
try {
inputStream = connection.inputStream
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e : Exception) {
e.printStackTrace()
@ -341,7 +348,7 @@ class OAuthManager {
message.listener?.onSuccess(result)
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR, message.context.getString(R.string.unknwon_error))
}
} else {
handleError(message,response)
@ -351,7 +358,7 @@ class OAuthManager {
fun post(message: Message) {
if (!isConnectedToInternet(message.context)) {
message.listener?.onError(message.context.getString(R.string.network_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.network_error))
return
}
val url: String = message.args.getString(URL, "")
@ -381,7 +388,7 @@ class OAuthManager {
outputStream.flush()
outputStream.close()
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e : Exception) {
e.printStackTrace()
@ -394,7 +401,7 @@ class OAuthManager {
try {
inputStream = connection.inputStream
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e: Exception) {
e.printStackTrace()
@ -410,7 +417,7 @@ class OAuthManager {
result.putString(EXTRA_DATA, response)
message.listener?.onSuccess(result)
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknwon_error))
}
} else {
handleError(message,response)
@ -420,7 +427,7 @@ class OAuthManager {
fun get(message: Message) {
if (!isConnectedToInternet(message.context)) {
message.listener?.onError(message.context.getString(R.string.network_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.network_error))
return
}
val url: String = message.args.getString(URL, "")
@ -443,7 +450,7 @@ class OAuthManager {
try {
inputStream = connection.inputStream
} catch (e: UnknownHostException) {
message.listener?.onError(message.context.getString(R.string.unknown_host))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknown_host))
return
} catch (e : Exception) {
e.printStackTrace()
@ -458,7 +465,7 @@ class OAuthManager {
result.putString(EXTRA_DATA, response)
message.listener?.onSuccess(result)
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknwon_error))
}
} else {
handleError(message,response)
@ -470,12 +477,13 @@ class OAuthManager {
try {
val rootObj = JSONObject(response)
val error = rootObj.getString("error")
message.listener?.onError(error)
val code = rootObj.getString("code")
message.listener?.onError(code,error)
} catch (e: Exception) {
message.listener?.onError(message.context.getString(R.string.json_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.json_error))
}
} else {
message.listener?.onError(message.context.getString(R.string.unknwon_error))
message.listener?.onError(InstanceManager.INTERNAL_ERROR,message.context.getString(R.string.unknwon_error))
}
}

View File

@ -52,7 +52,7 @@ class StreamSettings(
parcel.writeByte(if (nsfw) 1 else 0)
if (saveReplay!= null)
parcel.writeByte(if (saveReplay) 1 else 0)
parcel.writeInt(resolution?.ordinal!!)
parcel.writeInt(resolution.ordinal)
}
override fun describeContents(): Int {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -75,6 +75,23 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/password_error"/>
<TextView
android:id="@+id/two_fa_title"
android:text="@string/two_fa"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/two_fa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"/>
<TextView
android:id="@+id/error_two_fa"
android:textColor="#FF0000"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/two_fa_error"/>
</LinearLayout>
</layout>

View File

@ -8,7 +8,6 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:keepScreenOn="true"
>
<TextView
@ -16,7 +15,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/permission_info"
android:textColor="@color/white"
android:text="@string/permissions"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@ -41,7 +39,7 @@
android:layout_height="0dp"
android:id="@+id/surfaceView"
app:keepAspectRatio="true"
app:aspectRatioMode="adjust_rotate"
app:aspectRatioMode="adjust"
app:AAEnabled="false"
app:numFilters="1"
app:isFlipHorizontal="false"
@ -60,8 +58,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/flash"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/baseline_screen_rotation_24"
app:tint="@color/white" />
app:srcCompat="@drawable/baseline_screen_rotation_24" />
<ImageView
android:layout_marginTop="15dp"
android:id="@+id/flash"
@ -70,8 +67,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/mute_micro"
app:layout_constraintStart_toEndOf="@id/rotation"
app:srcCompat="@drawable/baseline_flash_off_24"
app:tint="@color/white" />
app:srcCompat="@drawable/baseline_flash_off_24" />
<ImageView
android:layout_marginTop="15dp"
android:id="@+id/mute_micro"
@ -80,8 +76,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/flash"
app:layout_constraintEnd_toStartOf="@id/switch_camera"
app:srcCompat="@drawable/baseline_volume_up_24"
app:tint="@color/white" />
app:srcCompat="@drawable/baseline_volume_up_24"/>
<ImageView
android:layout_marginTop="15dp"
android:id="@+id/switch_camera"
@ -90,8 +85,7 @@
app:srcCompat="@drawable/baseline_cameraswitch_24"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/mute_micro"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white" />
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/stop"

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="stream_title_error">Název by neměl být prázdný</string>
<string name="instance_error">Instance nemůže být prázdná</string>
<string name="username_error">Uživatelské jméno nesmí být prázdné</string>
<string name="password_error">Heslo nesmí být prázdné</string>
<string name="account_exist">Tento účet již existuje</string>
<string name="loading_channels">Načítání seznamu kanálů</string>
<string name="try_connect">Čekání na připojení</string>
<string name="delete_account">Odstranit účet %s spojený se serverem %s\?</string>
<string name="tags_rules">Maximálně 5 značek, každá od 2 do 30 znaků, oddělených čárkou</string>
<string name="network_error">Nenalezeno připojení k internetu</string>
<string name="app_name">PeerTube Live</string>
<string name="unknwon_error">Neznámá chyba</string>
<string name="unknown_host">Neznámý hostitel</string>
<string name="json_error">JSON chyba</string>
<string name="malformed_instance_error">Instance musí mít platnou adresu URL</string>
<string name="no_instance">Není registrován žádný účet. Přidejte účet PeerTube pomocí tlačítka \"+\" v horní liště</string>
<string name="permissions">Pro živé vysílání je nutný přístup ke kameře a mikrofonu. Kliknutím níže změníte nastavení oprávnění.</string>
<string name="back_reason">Váš živý přenos skončil po stisknutí tlačítka zpět</string>
<string name="background_reason">Váš živý přenos skončil, protože aplikace přešla na pozadí</string>
<string name="save_replay_info">Pokud tuto možnost povolíte, bude váš živý přenos ukončen, pokud překročíte kvótu videí</string>
<string name="password">Heslo</string>
<string name="delete_account_title">Smazat tento účet</string>
<string name="download_enabled">Povolit stahování</string>
<string name="nsfw">Obsahuje citlivý obsah</string>
<string name="tags">Štítky</string>
<string name="description">Popis</string>
<string name="creating">Čekání na živou tvorbu</string>
<string name="stream_resolution">Rozlišení živého přenosu</string>
<string name="entertainment">Zábava</string>
<string name="news_politics">Zprávy a Politika</string>
<string name="how_to">Jak na to</string>
<string name="education">Vzdělání</string>
<string name="activism">Aktivismus</string>
<string name="science_tech">Věda a Technika</string>
<string name="animals">Zvířata</string>
<string name="kids">Děti</string>
<string name="food">Jídlo</string>
<string name="afar">Afarština</string>
<string name="abkhazian">Abcházština</string>
<string name="afrikaans">Afrikánština</string>
<string name="akan">Akanština</string>
<string name="amharic">Amharština</string>
<string name="aragonese">Aragonština</string>
<string name="american_sign_language">Americký znakový jazyk</string>
<string name="assamese">Assámština</string>
<string name="bashkir">Baškirština</string>
<string name="bambara">Bambara</string>
<string name="belarusian">Běloruština</string>
<string name="british_sign_language">Britský znakový jazyk</string>
<string name="bislama">Bislama</string>
<string name="tibetan">Tibetština</string>
<string name="bosnian">Bosenský jazyk</string>
<string name="breton">Bretonština</string>
<string name="brazilian_sign_language">Brazilský znakový jazyk</string>
<string name="catalan">Katalánština</string>
<string name="czech">Čeština</string>
<string name="chamorro">Chamorro</string>
<string name="chechen">Čečenština</string>
<string name="chuvash">Čuvašština</string>
<string name="cornish">Cornwallština</string>
<string name="cree">Cree</string>
<string name="czech_sign_language">Český znakový jazyk</string>
<string name="welsh">Velština</string>
<string name="danish">Dánština</string>
<string name="german">Němčina</string>
<string name="dhivehi">Dhivehi</string>
<string name="danish_sign_language">Dánský znakový jazyk</string>
<string name="dzongkha">Džongkha</string>
<string name="greek">Řečtina</string>
<string name="english">Angličtina</string>
<string name="esperanto">Esperanto</string>
<string name="faroese">Faerština</string>
<string name="persian">Perština</string>
<string name="french">Francouzština</string>
<string name="western_frisian">Západofríština</string>
<string name="galician">Galicijština</string>
<string name="manx">Manx</string>
<string name="guarani">Guarani</string>
<string name="german_sign_language">Německý znakový jazyk</string>
<string name="gujarati">Gudžarátština</string>
<string name="hausa">Hausa</string>
<string name="hebrew">Hebrejština</string>
<string name="herero">Herero</string>
<string name="hindi">Hindština</string>
<string name="hiri_motu">Hiri Motu</string>
<string name="croatian">Chorvatština</string>
<string name="hungarian">Maďarština</string>
<string name="armenian">Arménština</string>
<string name="igbo">Igbo</string>
<string name="sichuan_yi">Sečuánský jazyk Yi</string>
<string name="inuktitut">Inuktitut</string>
<string name="indonesian">Indonéština</string>
<string name="inupiaq">Inupiaq</string>
<string name="icelandic">Islandština</string>
<string name="italian">Italština</string>
<string name="javanese">Javánština</string>
<string name="lojban">Lojban</string>
<string name="japanese_sign_language">Japonský znakový jazyk</string>
<string name="kannada">Kannadština</string>
<string name="georgian">Gruzínština</string>
<string name="kanuri">Kanuri</string>
<string name="kazakh">Kazaština</string>
<string name="khmer">Khmerština</string>
<string name="kikuyu">Kikuyu</string>
<string name="kirghiz">Kirgizština</string>
<string name="komi">Komi</string>
<string name="kongo">Kongo</string>
<string name="korean">Korejština</string>
<string name="kurdish">Kurdština</string>
<string name="latvian">Lotyšština</string>
<string name="limburgan">Limburština</string>
<string name="luxembourgish">Lucemburština</string>
<string name="ganda">Ganda</string>
<string name="marshallese">Maršálština</string>
<string name="malayalam">Malajalamština</string>
<string name="marathi">Maráthština</string>
<string name="macedonian">Makedonština</string>
<string name="malagasy">Malgaština</string>
<string name="maltese">Maltézština</string>
<string name="mongolian">Mongolština</string>
<string name="maori">Maorština</string>
<string name="malay_macrolanguage">Malajština (makrojazyk)</string>
<string name="navajo">Navajština</string>
<string name="south_ndebele">Jižní Ndebele</string>
<string name="ndonga">Ndonga</string>
<string name="nepali_macrolanguage">Nepálština (makrojazyk)</string>
<string name="dutch">Nizozemština</string>
<string name="norwegian_nynorsk">Norština Nynorsk</string>
<string name="norwegian_bokmål">Norština Bokmål</string>
<string name="norwegian">Norština</string>
<string name="nyanja">Nyanja</string>
<string name="ojibwa">Jazyk Odžibvejů</string>
<string name="oriya_macrolanguage">Oriya (makrojazyk)</string>
<string name="ossetian">Osetijština</string>
<string name="panjabi">Pandžábština</string>
<string name="polish">Polština</string>
<string name="portuguese">Portugalština</string>
<string name="pushto">Paštúnština</string>
<string name="quechua">Kečuánština</string>
<string name="romanian">Rumunština</string>
<string name="romansh">Romština</string>
<string name="russian_sign_language">Ruský znakový jazyk</string>
<string name="south_african_sign_language">Jihoafrický znakový jazyk</string>
<string name="sinhala">Sinhálština</string>
<string name="slovak">Slovenština</string>
<string name="southern_sotho">Jižní Sotho</string>
<string name="albanian">Albánština</string>
<string name="tahitian">Tahitština</string>
<string name="tamil">Tamilština</string>
<string name="telugu">Telugu</string>
<string name="tajik">Tádžijština</string>
<string name="tagalog">Tagalog</string>
<string name="tigrinya">Tigrinya</string>
<string name="tsonga">Tsonga</string>
<string name="bysa">Uveďte autora - Sdílejte stejně</string>
<string name="byncsa">Uveďte autora - nekomerční - sdílejte stejně</string>
<string name="privacy_private">Soukromé</string>
<string name="internal">Interní</string>
<string name="live_disabled">Tato instance má vypnutý livestream.</string>
<string name="stream_privacy">Soukromí</string>
<string name="yes">Ano</string>
<string name="add_instance">Přidat tento účet</string>
<string name="aymara">Aymarština</string>
<string name="gaming">Hry</string>
<string name="fulah">Fulah</string>
<string name="lock_reason">Váš živý přenos skončil, protože telefon byl uzamčen</string>
<string name="ask_end_stream">Chcete zastavit živý přenos\?</string>
<string name="stream_category">Kategorie</string>
<string name="stream_language">Jazyk</string>
<string name="go_live">Spustit živý přenos</string>
<string name="stream_licence">Licence</string>
<string name="username">Uživatelské jméno</string>
<string name="goto_permissions">Zobrazit nastavení</string>
<string name="stream_title">Název</string>
<string name="stop_reason">Váš živý přenos skončil</string>
<string name="network_reason">Váš živý přenos skončil kvůli problému se sítí</string>
<string name="choose_channel">Kanál</string>
<string name="advanced_settings">▶ Pokročilá nastavení</string>
<string name="advanced_settings_expand">▼ Pokročilá nastavení</string>
<string name="connection">Připojení</string>
<string name="comments_enabled">Povolit komentáře k videu</string>
<string name="films">Filmy</string>
<string name="azerbaijani">Ázerbájdžán</string>
<string name="cancel">Zrušit</string>
<string name="connect">Připojit</string>
<string name="vehicles">Auta</string>
<string name="no">Ne</string>
<string name="chinese_sign_language">Čínský znakový jazyk</string>
<string name="stream_ended">Živý přenos skončil</string>
<string name="exemple_instance">např. peertube.fr, https://peertube.fr</string>
<string name="music">Hudba</string>
<string name="travels">Cestování</string>
<string name="arabic">Arabština</string>
<string name="avaric">Avaric</string>
<string name="instance">Instance</string>
<string name="save_replay">Automatické zveřejnění přehrávání po skončení živého vysílání</string>
<string name="end_stream">Zastavit živý přenos</string>
<string name="comedy">Komedie</string>
<string name="art">Umění</string>
<string name="sports">Sporty</string>
<string name="people">Lidé</string>
<string name="corsican">Korsičtina</string>
<string name="kotava">Kotava</string>
<string name="bengali">Bengálština</string>
<string name="bulgarian">Bulharština</string>
<string name="estonian">Estonština</string>
<string name="fijian">Fidžijština</string>
<string name="french_sign_language">Francouzský znakový jazyk</string>
<string name="scottish_gaelic">Skotská gaelština</string>
<string name="ewe">Ewe</string>
<string name="haitian">Haitský jazyk</string>
<string name="serbo_croatian">Srbochorvatština</string>
<string name="basque">Baskičtina</string>
<string name="finnish">Finština</string>
<string name="irish">Irština</string>
<string name="japanese">Japonština</string>
<string name="kabyle">Kabyle</string>
<string name="kalaallisut">Kalaallisut</string>
<string name="kashmiri">Kašmírština</string>
<string name="kinyarwanda">Kinyarwanda</string>
<string name="kuanyama">Kuanyama</string>
<string name="lao">Lao</string>
<string name="sindhi">Sindhi</string>
<string name="burmese">Barmština</string>
<string name="occitan">Okcitánština</string>
<string name="somali">Somálština</string>
<string name="luba_katanga">Luba-Katanga</string>
<string name="nauru">Nauru</string>
<string name="north_ndebele">Severní Ndebele</string>
<string name="lingala">Lingala</string>
<string name="lithuanian">Litevština</string>
<string name="pakistan_sign_language">Pákistánský znakový jazyk</string>
<string name="oromo">Oromo</string>
<string name="russian">Ruština</string>
<string name="saudi_arabian_sign_language">Saúdskoarabský znakový jazyk</string>
<string name="samoan">Samojský jazyk</string>
<string name="serbian">Srbština</string>
<string name="swedish_sign_language">Švédský znakový jazyk</string>
<string name="thai">Thajština</string>
<string name="turkmen">Turkmenština</string>
<string name="twi">Twi</string>
<string name="urdu">Urdština</string>
<string name="uzbek">Uzbečtina</string>
<string name="walloon">Waloonština</string>
<string name="tatar">Tatarština</string>
<string name="turkish">Turečtina</string>
<string name="uighur">Ujgurština</string>
<string name="yiddish">Jiddiš</string>
<string name="sardinian">Sardinijština</string>
<string name="rundi">Rundi</string>
<string name="sango">Sango</string>
<string name="slovenian">Slovinština</string>
<string name="shona">Shona</string>
<string name="spanish">Španělština</string>
<string name="klingon">Klingonština</string>
<string name="vietnamese">Vietnamština</string>
<string name="wolof">Wolof</string>
<string name="xhosa">Xhosa</string>
<string name="zhuang">Čuang</string>
<string name="northern_sami">Severosamijský jazyk</string>
<string name="swati">Swati</string>
<string name="sundanese">Sundánština</string>
<string name="swahili_macrolanguage">Svahilština (makrojazyk)</string>
<string name="swedish">Švédština</string>
<string name="tonga_tonga_islands">Tonga (Ostrovy Tonga)</string>
<string name="tswana">Tswana</string>
<string name="ukrainian">Ukrajinština</string>
<string name="venda">Venda</string>
<string name="yoruba">Jorubština</string>
<string name="chinese">Čínština</string>
<string name="zulu">Zulujština</string>
<string name="by">Přiznání autorství</string>
<string name="bynd">Uveďte autora - bez odvozenin</string>
<string name="privacy_public">Veřejné</string>
<string name="unlisted">Nezaneseno do seznamu</string>
<string name="bync">Uveďte autora - Nekomerční</string>
<string name="byncnd">Uveďte autora - nekomerční - bez odvozenin</string>
<string name="public_domain">Vyhrazení veřejné domény</string>
</resources>

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="no_instance">Kein Konto registriert. Um ein Peertube-Konto hinzuzufügen, klicke auf das + in der oberen Leiste</string>
<string name="german">Deutsch</string>
<string name="arabic">Arabisch</string>
<string name="food">Essen</string>
<string name="kids">Kinder</string>
<string name="animals">Tiere</string>
<string name="science_tech">Wissenschaft und Technik</string>
<string name="activism">Aktivismus</string>
<string name="education">Bildung</string>
<string name="how_to">Tutorials</string>
<string name="news_politics">Nachrichten und Politik</string>
<string name="entertainment">Unterhaltung</string>
<string name="comedy">Humor</string>
<string name="people">Menschen</string>
<string name="gaming">Videospiele</string>
<string name="travels">Reise</string>
<string name="sports">Sport</string>
<string name="art">Kunst</string>
<string name="vehicles">Fahrzeuge</string>
<string name="films">Filme</string>
<string name="music">Musik</string>
<string name="description">Beschreibung</string>
<string name="tags">Markierungen</string>
<string name="nsfw">Enthält sensible Inhalte</string>
<string name="download_enabled">Herunterladen-Option aktivieren</string>
<string name="comments_enabled">Videokommentare aktivieren</string>
<string name="delete_account_title">Dieses Konto löschen</string>
<string name="instance">Instanz</string>
<string name="password">Passwort</string>
<string name="username">Benutzername</string>
<string name="add_instance">Dieses Konto hinzufügen</string>
<string name="advanced_settings_expand">▼ Erweiterte Einstellungen</string>
<string name="advanced_settings">▶ Erweiterte Einstellungen</string>
<string name="stream_licence">Lizenz</string>
<string name="stream_language">Sprache</string>
<string name="stream_privacy">Privatsphäre</string>
<string name="stream_category">Kategorie</string>
<string name="stream_title">Titel</string>
<string name="connect">Verbinden</string>
<string name="goto_permissions">Einstellungen anzeigen</string>
<string name="no">Nein</string>
<string name="yes">Ja</string>
<string name="cancel">Abbrechen</string>
<string name="choose_channel">Kanal</string>
<string name="stop_reason">Dein Livestream ist beendet</string>
<string name="background_reason">Dein Livestream ist beendet, weil die Anwendung in den Hintergrund gegangen ist</string>
<string name="back_reason">Dein Livestream wurde beendet, nachdem Du die Zurück-Taste gedrückt hast</string>
<string name="tags_rules">Maximal 5 Markierungen, jeweils zwischen 2 und 30 Zeichen, durch Komma getrennt</string>
<string name="delete_account">Bist du sicher, dass du das Konto %s, das mit dem Server %s verbunden ist, löschen möchtest\?</string>
<string name="permissions">Wir benötigen den Zugriff auf Kamera und Mikrofon, um live zu streamen. Zum Ändern der Berechtigungseinstellungen klicke bitte unten.</string>
<string name="try_connect">Warte auf Verbindung</string>
<string name="loading_channels">Deine Kanalliste wird geladen</string>
<string name="account_exist">Dieses Konto existiert bereits</string>
<string name="malformed_instance_error">Instanz sollte eine gültige URL sein</string>
<string name="password_error">Passwort kann nicht leer sein</string>
<string name="username_error">Benutzername kann nicht leer sein</string>
<string name="instance_error">Instanz kann nicht leer sein</string>
<string name="stream_title_error">Titel sollte nicht leer sein</string>
<string name="json_error">JSON-Fehler</string>
<string name="unknown_host">Unbekannter Host</string>
<string name="unknwon_error">Unbekannter Fehler</string>
<string name="network_error">Keine Internetverbindung gefunden</string>
<string name="app_name">Peertube Live</string>
<string name="bashkir">Baschkirisch</string>
<string name="british_sign_language">Britische Gebärdensprache</string>
<string name="bislama">Bislama</string>
<string name="tibetan">Tibetisch</string>
<string name="bosnian">Bosnisch</string>
<string name="breton">Bretonisch</string>
<string name="bulgarian">Bulgarisch</string>
<string name="brazilian_sign_language">Brasilianische Gebärdensprache</string>
<string name="chechen">Tschetschenisch</string>
<string name="chuvash">Tschuwaschisch</string>
<string name="danish">Dänisch</string>
<string name="dzongkha">Dzongkha</string>
<string name="greek">Griechisch</string>
<string name="estonian">Estnisch</string>
<string name="basque">Baskisch</string>
<string name="persian">Persisch</string>
<string name="fijian">Fidschianisch</string>
<string name="finnish">Finnisch</string>
<string name="french">Französisch</string>
<string name="western_frisian">Westfriesisch</string>
<string name="japanese_sign_language">Japanische Gebärdensprache</string>
<string name="kirghiz">Kirgisisch</string>
<string name="norwegian_nynorsk">Norwegisch (Nynorsk)</string>
<string name="norwegian_bokmål">Norwegisch (Bokmål)</string>
<string name="polish">Polnisch</string>
<string name="portuguese">Portugiesisch</string>
<string name="pushto">Paschtu</string>
<string name="quechua">Quechua</string>
<string name="spanish">Spanisch</string>
<string name="albanian">Albanisch</string>
<string name="tamil">Tamilisch</string>
<string name="uzbek">Usbekisch</string>
<string name="vietnamese">Vietnamesisch</string>
<string name="walloon">Wallonisch</string>
<string name="zhuang">Zhuang</string>
<string name="bync">Namensnennung - nicht kommerziell</string>
<string name="bysa">Namensnennung - Weitergabe unter gleichen Bedingungen</string>
<string name="byncnd">Namensnennung - Nicht kommerziell - Keine Derivate</string>
<string name="public_domain">Gemeingut Widmung</string>
<string name="byncsa">Namensnennung - Nicht kommerziell - Weitergabe unter gleichen Bedingungen</string>
<string name="privacy_public">Öffentlich</string>
<string name="unlisted">Ungelistet</string>
<string name="privacy_private">Privat</string>
<string name="internal">Intern</string>
<string name="yiddish">Jiddisch</string>
<string name="cornish">Kornisch</string>
<string name="corsican">Korsisch</string>
<string name="welsh">Walisisch</string>
<string name="latvian">Lettisch</string>
<string name="cree">Cree</string>
<string name="faroese">Färöisch</string>
<string name="english">Englisch</string>
<string name="esperanto">Esperanto</string>
<string name="ewe">Ewe</string>
<string name="scottish_gaelic">Schottisch-Gälisch</string>
<string name="irish">Irisch</string>
<string name="gujarati">Gujarati</string>
<string name="haitian">Haitianisch</string>
<string name="serbo_croatian">Serbo-kroatisch</string>
<string name="hungarian">Ungarisch</string>
<string name="armenian">Armenisch</string>
<string name="icelandic">Isländisch</string>
<string name="javanese">Javanisch</string>
<string name="kabyle">Kabylisch</string>
<string name="georgian">Georgisch</string>
<string name="korean">Koreanisch</string>
<string name="norwegian">Norwegisch</string>
<string name="panjabi">Panjabi</string>
<string name="romansh">Rätoromanisch</string>
<string name="romanian">Rumänisch</string>
<string name="russian">Russisch</string>
<string name="sinhala">Singhalesisch</string>
<string name="slovak">Slowakisch</string>
<string name="northern_sami">Nordsamisch</string>
<string name="serbian">Serbisch</string>
<string name="tigrinya">Tigrinya</string>
<string name="turkish">Türkisch</string>
<string name="uighur">Uigurisch</string>
<string name="ukrainian">Ukrainisch</string>
<string name="save_replay_info">Wenn Sie diese Option aktivieren, wird Ihre Live-Sendung beendet, wenn Sie Ihr Videokontingent überschreiten</string>
<string name="lock_reason">Dein Livestream wurde beendet, weil das Telefon gesperrt wurde</string>
<string name="zulu">Zulu</string>
<string name="yoruba">Yoruba</string>
<string name="bengali">Bengalisch</string>
<string name="german_sign_language">Deutsche Gebärdensprache</string>
<string name="japanese">Japanisch</string>
<string name="lithuanian">Litauisch</string>
<string name="luxembourgish">Luxemburgisch</string>
<string name="dutch">Niederländisch</string>
<string name="by">Namensnennung</string>
<string name="bynd">Namensnennung - Keine Derivate</string>
<string name="chinese">Chinesisch</string>
<string name="american_sign_language">Amerikanische Gebärdensprache</string>
<string name="azerbaijani">Aserbaidschanisch</string>
<string name="bambara">Bambara</string>
<string name="belarusian">Weißrussisch</string>
<string name="czech_sign_language">Tschechische Gebärdensprache</string>
<string name="chinese_sign_language">Chinesische Gebärdensprache</string>
<string name="french_sign_language">Französische Gebärdensprache</string>
<string name="hebrew">Hebräisch</string>
<string name="croatian">Kroatisch</string>
<string name="italian">Italienisch</string>
<string name="lojban">Lojban</string>
<string name="pakistan_sign_language">Pakistanische Gebärdensprache</string>
<string name="tahitian">Tahitianisch</string>
<string name="tajik">Tadschikisch</string>
<string name="galician">Galizisch</string>
<string name="manx">Manx</string>
<string name="hausa">Hausa</string>
<string name="slovenian">Slowenisch</string>
<string name="samoan">Samoanisch</string>
<string name="swedish">Schwedisch</string>
<string name="russian_sign_language">Russische Gebärdensprache</string>
<string name="swedish_sign_language">Schwedische Gebärdensprache</string>
<string name="urdu">Urdu</string>
<string name="connection">Verbindung</string>
<string name="stream_ended">Livestream beendet</string>
<string name="creating">Warte auf die Erstellung des Livestreams</string>
<string name="stream_resolution">Auflösung des Livestreams</string>
<string name="exemple_instance">z.B. peertube.fr, https://peertube.fr</string>
<string name="save_replay">Livestream automatisch veröffentlichen sobald dieser beendet wurde</string>
<string name="end_stream">Livestream beenden</string>
<string name="abkhazian">Abchasisch</string>
<string name="afrikaans">Afrikaans</string>
<string name="afar">Afar</string>
<string name="amharic">Amharisch</string>
<string name="aragonese">Aragonesisch</string>
<string name="akan">Akan</string>
<string name="assamese">Assamesisch</string>
<string name="avaric">Awarisch</string>
<string name="kotava">Kotava</string>
<string name="aymara">Aymara</string>
<string name="catalan">Katalanisch</string>
<string name="czech">Tschechisch</string>
<string name="chamorro">Chamorro</string>
<string name="dhivehi">Dhivehi</string>
<string name="danish_sign_language">Dänische Zeichensprache</string>
<string name="fulah">Fulfulde</string>
<string name="guarani">Guaraní</string>
<string name="herero">Herero</string>
<string name="hindi">Hindi</string>
<string name="hiri_motu">Hiri Motu</string>
<string name="igbo">Igbo</string>
<string name="sichuan_yi">Sichuan Yi</string>
<string name="inuktitut">Inuktitut</string>
<string name="indonesian">Indonesisch</string>
<string name="inupiaq">Inupiaq</string>
<string name="kannada">Kannada</string>
<string name="kashmiri">Kaschmiri</string>
<string name="kazakh">Kasachisch</string>
<string name="kalaallisut">Kalaallisut</string>
<string name="kanuri">Kanuri</string>
<string name="khmer">Khmer</string>
<string name="kikuyu">Kikuyu</string>
<string name="kinyarwanda">Ruandisch</string>
<string name="kongo">Kongo</string>
<string name="komi">Komi</string>
<string name="kuanyama">Kwanyama</string>
<string name="kurdish">Kurdisch</string>
<string name="lao">Lao</string>
<string name="limburgan">Limburgisch</string>
<string name="lingala">Lingala</string>
<string name="luba_katanga">Luba-Katanga</string>
<string name="ganda">Ganda</string>
<string name="marshallese">Marshallesisch</string>
<string name="malayalam">Malayalam</string>
<string name="marathi">Marathi</string>
<string name="macedonian">Mazedonisch</string>
<string name="maltese">Maltesisch</string>
<string name="mongolian">Mongolisch</string>
<string name="malagasy">Madegassisch</string>
<string name="maori">Maori</string>
<string name="malay_macrolanguage">Malaiisch (Makrosprache)</string>
<string name="burmese">Birmesisch</string>
<string name="nauru">Nauruisch</string>
<string name="navajo">Navaho</string>
<string name="south_ndebele">Süd-Ndebele</string>
<string name="north_ndebele">Nord-Ndebele</string>
<string name="ndonga">Ndonga</string>
<string name="nepali_macrolanguage">Nepali (Makrosprache)</string>
<string name="occitan">Okzitanisch</string>
<string name="nyanja">Nyanja</string>
<string name="oromo">Oromo</string>
<string name="ossetian">Ossetisch</string>
<string name="oriya_macrolanguage">Odia (Makrosprache)</string>
<string name="ojibwa">Ojibwe</string>
<string name="saudi_arabian_sign_language">Saudi-Arabische Gebärdensprache</string>
<string name="south_african_sign_language">Südafrikanische Gebärdensprache</string>
<string name="sango">Sango</string>
<string name="rundi">Kirundi</string>
<string name="somali">Somali</string>
<string name="southern_sotho">Sesotho</string>
<string name="sindhi">Sindhi</string>
<string name="shona">Shona</string>
<string name="sardinian">Sardisch</string>
<string name="sundanese">Sundanesisch</string>
<string name="swati">Swati</string>
<string name="swahili_macrolanguage">Swahili (Makrosprache)</string>
<string name="tatar">Tatarisch</string>
<string name="telugu">Telugu</string>
<string name="tagalog">Tagalog</string>
<string name="klingon">Klingonisch</string>
<string name="thai">Thai</string>
<string name="turkmen">Turkmenisch</string>
<string name="tsonga">Tsonga</string>
<string name="tswana">Tswana</string>
<string name="tonga_tonga_islands">Tonga (Tonga-Inseln)</string>
<string name="twi">Twi</string>
<string name="wolof">Wolof</string>
<string name="xhosa">Xhosa</string>
<string name="venda">Venda</string>
<string name="ask_end_stream">Willst du den Livestream beenden\?</string>
<string name="live_disabled">Auf dieser Instanz sind Live-Streams deaktiviert</string>
<string name="go_live">Starte Livestream</string>
<string name="network_reason">Ihr Livestream wurde beendet, aufgrund eines Netzwerkproblems</string>
</resources>

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="json_error">Error JSON</string>
<string name="username_error">El nombre de usuario no puede estar vacío</string>
<string name="account_exist">Esta cuenta ya existe</string>
<string name="no_instance">No hay cuenta registrada. Agregue una cuenta de PeerTube usando el \'+\' en la barra superior</string>
<string name="loading_channels">Cargando tu lista de canales</string>
<string name="try_connect">Esperando la conexión</string>
<string name="save_replay_info">Si habilita esta opción, su transmisión en vivo se cancelará si excede su cuota de video</string>
<string name="back_reason">Tu transmisión en vivo terminó después de presionar el botón Atrás</string>
<string name="stop_reason">Tu transmisión en vivo terminó</string>
<string name="ask_end_stream">¿Quieres detenerla detener la transmisión en vivo\?</string>
<string name="network_reason">Tu transmisión en vivo terminó debido a un problema de red</string>
<string name="choose_channel">Canal</string>
<string name="go_live">Iniciar transmisión en vivo</string>
<string name="yes"></string>
<string name="goto_permissions">Ver configuraciones</string>
<string name="connect">Conectar</string>
<string name="stream_category">Categoría</string>
<string name="stream_privacy">Privacidad</string>
<string name="stream_language">Idioma</string>
<string name="advanced_settings_expand">▼ Ajustes avanzados</string>
<string name="add_instance">Agregar esta cuenta</string>
<string name="username">Nombre de usuario</string>
<string name="instance">Instancia</string>
<string name="delete_account_title">Eliminar esta cuenta</string>
<string name="nsfw">Contiene contenido sensible</string>
<string name="tags">Etiquetas</string>
<string name="description">Descripción</string>
<string name="end_stream">Detener transmisión en vivo</string>
<string name="exemple_instance">e.g. peertube.fr, https://peertube.fr</string>
<string name="music">Música</string>
<string name="films">Peliculas</string>
<string name="art">Arte</string>
<string name="how_to">Cómo hacer</string>
<string name="education">Educación</string>
<string name="science_tech">Ciencias y Tecnología</string>
<string name="food">Comida</string>
<string name="afar">Lejos</string>
<string name="akan">Voluntad</string>
<string name="amharic">Amárico</string>
<string name="arabic">Arábica</string>
<string name="aragonese">aragonés</string>
<string name="american_sign_language">lenguaje de signos americano</string>
<string name="kotava">Kotava</string>
<string name="aymara">Aymara</string>
<string name="bambara">Bambara</string>
<string name="belarusian">Bielorruso</string>
<string name="bengali">bengalí</string>
<string name="british_sign_language">Lenguaje de señas británico</string>
<string name="tibetan">Tibetano</string>
<string name="bulgarian">búlgaro</string>
<string name="catalan">Catalán</string>
<string name="chamorro">Chamorro</string>
<string name="chuvash">Chuvash</string>
<string name="cornish">Córnico</string>
<string name="corsican">Corso</string>
<string name="esperanto">Esperanto</string>
<string name="ewe">Oveja</string>
<string name="faroese">Feroés</string>
<string name="french">Francés</string>
<string name="scottish_gaelic">Gaélico escocés</string>
<string name="manx">lengua de la isla de Man</string>
<string name="german_sign_language">Lengua de señas alemana</string>
<string name="gujarati">Gujarati</string>
<string name="haitian">Haitiano</string>
<string name="hausa">Hausa</string>
<string name="herero">Herero</string>
<string name="hiri_motu">Hiri Motu</string>
<string name="croatian">Croata</string>
<string name="armenian">Armenio</string>
<string name="inuktitut">Inuktitut</string>
<string name="inupiaq">Inupiaq</string>
<string name="javanese">javanés</string>
<string name="lojban">Lojban</string>
<string name="japanese_sign_language">Lengua de señas japonesa</string>
<string name="kannada">Canarés</string>
<string name="kashmiri">Cachemira</string>
<string name="kanuri">Kanuri</string>
<string name="khmer">Jemer</string>
<string name="kikuyu">Kikuyu</string>
<string name="kinyarwanda">Kinyarwanda</string>
<string name="kongo">Congo</string>
<string name="kuanyama">Animales</string>
<string name="kurdish">kurdo</string>
<string name="lao">Lao</string>
<string name="limburgan">Limburgan</string>
<string name="lithuanian">Lituano</string>
<string name="luxembourgish">Luxemburgués</string>
<string name="luba_katanga">Luba-Katanga</string>
<string name="ganda">Ganda</string>
<string name="marshallese">Marshalés</string>
<string name="malayalam">Malayalam</string>
<string name="malagasy">Madagascarí</string>
<string name="maori">Maori</string>
<string name="nauru">Nauru</string>
<string name="south_ndebele">Ndebele del sur</string>
<string name="north_ndebele">Ndebele del norte</string>
<string name="norwegian">Noruego</string>
<string name="nyanja">Nyanja</string>
<string name="occitan">Occitano</string>
<string name="oromo">Oromo</string>
<string name="ossetian">Osetio</string>
<string name="panjabi">Panjabi</string>
<string name="pakistan_sign_language">Lengua de señas pakistaní</string>
<string name="pushto">Pushto</string>
<string name="quechua">Quechua</string>
<string name="romanian">Rumano</string>
<string name="russian">Ruso</string>
<string name="sango">Sango</string>
<string name="sinhala">Cingalés</string>
<string name="slovak">Eslovaco</string>
<string name="slovenian">Esloveno</string>
<string name="shona">Shona</string>
<string name="spanish">Español</string>
<string name="sardinian">Sardo</string>
<string name="serbian">Serbio</string>
<string name="swati">Swati</string>
<string name="sundanese">Sundanés</string>
<string name="tahitian">Tahitiano</string>
<string name="tamil">Tamil</string>
<string name="tajik">Tayiko</string>
<string name="tagalog">Tagalo</string>
<string name="thai">Tailandés</string>
<string name="tigrinya">Tigrinya</string>
<string name="klingon">Klingon</string>
<string name="tswana">Tswana</string>
<string name="turkish">Turco</string>
<string name="ukrainian">Ucranio</string>
<string name="urdu">Urdu</string>
<string name="uzbek">Uzbeko</string>
<string name="venda">Venda</string>
<string name="walloon">Valonia</string>
<string name="xhosa">Xhosa</string>
<string name="zhuang">Zhuang</string>
<string name="zulu">Zulú</string>
<string name="bynd">Atribución: sin derivados</string>
<string name="bync">Atribución - No comercial</string>
<string name="public_domain">Dedicación de dominio público</string>
<string name="unlisted">No listado</string>
<string name="unknown_host">Host desconocido</string>
<string name="stream_title_error">El título no debe estar vacío</string>
<string name="network_error">No se encontró conexión a Internet</string>
<string name="unknwon_error">Error desconocido</string>
<string name="instance_error">La instancia no puede estar vacía</string>
<string name="malformed_instance_error">La instancia debe tener una URL válida</string>
<string name="delete_account">¿Eliminar la cuenta %s asociada con el servidor %s \?</string>
<string name="password_error">La contraseña no puede estar vacía</string>
<string name="permissions">Se necesita acceso a la cámara y al micrófono para transmitir en vivo. Haga clic a continuación para cambiar la configuración de permisos.</string>
<string name="tags_rules">Máximo 5 etiquetas, cada una de entre 2 y 30 caracteres, separadas por comas</string>
<string name="background_reason">Tu transmisión en vivo finalizó porque la aplicación pasó a segundo plano</string>
<string name="lock_reason">Tu transmisión en vivo terminó porque el teléfono estaba bloqueado</string>
<string name="live_disabled">Esta instancia ha inhabilitado la transmisión en vivo.</string>
<string name="no">No</string>
<string name="cancel">Cancelar</string>
<string name="stream_title">Título</string>
<string name="stream_licence">Licencia</string>
<string name="password">Contraseña</string>
<string name="save_replay">Publica automáticamente una repetición cuando finaliza tu transmisión en vivo</string>
<string name="activism">Activismo</string>
<string name="afrikaans">africaans</string>
<string name="brazilian_sign_language">Lenguaje de señas brasileño</string>
<string name="czech">Checo</string>
<string name="advanced_settings">▶ Ajustes avanzados</string>
<string name="chechen">Checheno</string>
<string name="danish">Danés</string>
<string name="german">Alemán</string>
<string name="fulah">Fulah</string>
<string name="hindi">Hindi</string>
<string name="connection">Conexión</string>
<string name="download_enabled">Habilitar descarga</string>
<string name="comments_enabled">Habilitar comentarios de video</string>
<string name="creating">Espera la creación en vivo</string>
<string name="sports">Deportes</string>
<string name="people">Gente</string>
<string name="entertainment">Entretenimiento</string>
<string name="stream_ended">La transmisión en vivo terminó</string>
<string name="stream_resolution">Resolución de transmisión en vivo</string>
<string name="vehicles">Vehículos</string>
<string name="travels">Viajes</string>
<string name="gaming">Juegos</string>
<string name="comedy">Comedia</string>
<string name="news_politics">Noticias y política</string>
<string name="animals">Animales</string>
<string name="kids">Niños</string>
<string name="assamese">Asamés</string>
<string name="avaric">Avarico</string>
<string name="bashkir">Bashkir</string>
<string name="bislama">Bislama</string>
<string name="breton">Bretón</string>
<string name="cree">Cree</string>
<string name="czech_sign_language">Lengua de señas checa</string>
<string name="chinese_sign_language">Lenguaje de señas chino</string>
<string name="welsh">Galés</string>
<string name="dhivehi">Dhivehi</string>
<string name="greek">Griego</string>
<string name="estonian">Estonio</string>
<string name="basque">Vasco</string>
<string name="persian">Persa</string>
<string name="fijian">Fiyiano</string>
<string name="french_sign_language">Lengua de señas francesa</string>
<string name="galician">Gallego</string>
<string name="abkhazian">Abjasio</string>
<string name="azerbaijani">Azerbaiyano</string>
<string name="bosnian">bosnio</string>
<string name="danish_sign_language">Lengua de señas danesa</string>
<string name="dzongkha">Dzongkha</string>
<string name="english">Inglés</string>
<string name="western_frisian">Frisón occidental</string>
<string name="guarani">Guaraní</string>
<string name="serbo_croatian">Serbocroata</string>
<string name="finnish">Finlandés</string>
<string name="irish">Irlandesa</string>
<string name="hungarian">Húngaro</string>
<string name="igbo">Igbo</string>
<string name="indonesian">Indonesio</string>
<string name="japanese">Japonés</string>
<string name="kabyle">Kabyle</string>
<string name="kalaallisut">Kalaallisut</string>
<string name="georgian">Georgiano</string>
<string name="kazakh">Kazajo</string>
<string name="kirghiz">Kirguís</string>
<string name="korean">Coreano</string>
<string name="latvian">Letón</string>
<string name="hebrew">Hebreo</string>
<string name="sichuan_yi">Sichuan Yi</string>
<string name="icelandic">Islandés</string>
<string name="komi">Komi</string>
<string name="lingala">Lingala</string>
<string name="maltese">Maltese</string>
<string name="italian">Italiano</string>
<string name="ojibwa">Ojibwa</string>
<string name="portuguese">Portugués</string>
<string name="marathi">Marathi</string>
<string name="macedonian">Macedónio</string>
<string name="mongolian">Mongol</string>
<string name="malay_macrolanguage">Malayo (macrolenguaje)</string>
<string name="southern_sotho">Sur de Sotho</string>
<string name="telugu">Telugu</string>
<string name="tsonga">Tsonga</string>
<string name="wolof">Wolof</string>
<string name="yiddish">Yídish</string>
<string name="byncnd">Atribución - No comercial - Sin derivados</string>
<string name="app_name">PeerTube Live</string>
<string name="burmese">Birmano</string>
<string name="navajo">Navajo</string>
<string name="ndonga">Ndonga</string>
<string name="nepali_macrolanguage">Nepalí (macrolenguaje)</string>
<string name="dutch">Holandés</string>
<string name="oriya_macrolanguage">Oriya (macrolenguaje)</string>
<string name="polish">Polaco</string>
<string name="russian_sign_language">Lengua de señas rusa</string>
<string name="saudi_arabian_sign_language">Lengua de señas saudita</string>
<string name="sindhi">Sindhi</string>
<string name="norwegian_nynorsk">Noruego Nynorsk</string>
<string name="norwegian_bokmål">Noruego bokmål</string>
<string name="rundi">Rundi</string>
<string name="swahili_macrolanguage">Swahili (macrolenguaje)</string>
<string name="tatar">Tártaro</string>
<string name="vietnamese">Vietnamita</string>
<string name="byncsa">Atribución - No comercial - Compartir por igual</string>
<string name="privacy_private">Privado</string>
<string name="romansh">Romanche</string>
<string name="south_african_sign_language">Lenguaje de señas sudafricano</string>
<string name="samoan">Samoano</string>
<string name="northern_sami">Sami del norte</string>
<string name="somali">Somalí</string>
<string name="albanian">Albanés</string>
<string name="swedish">Sueco</string>
<string name="swedish_sign_language">Lenguaje de señas sueco</string>
<string name="tonga_tonga_islands">Tonga (Islas Tonga)</string>
<string name="turkmen">Turcomano</string>
<string name="twi">Twi</string>
<string name="uighur">Uigur</string>
<string name="yoruba">Yoruba</string>
<string name="chinese">Chino</string>
<string name="by">Atribución</string>
<string name="bysa">Atribución - Compartir igual</string>
<string name="privacy_public">Público</string>
<string name="internal">Interno</string>
</resources>

View File

@ -10,6 +10,8 @@
<string name="instance_error">L\'instance ne peut pas être vide</string>
<string name="username_error">Le nom d\'utilisateur ne peut pas être vide</string>
<string name="password_error">Le mot de passe ne peut pas être vide</string>
<string name="two_fa_error">Le jeton d\'authentification à deux facteurs ne peut pas être vide</string>
<string name="invalid_two_fa_token">Le jeton d\'authentification à deux facteurs n\'est pas valide</string>
<string name="malformed_instance_error">L\'instance doit avoir une URL valide</string>
<string name="account_exist">Ce compte existe déjà</string>
<!-- messages -->
@ -48,6 +50,7 @@
<string name="username">Nom d\'utilisateur</string>
<string name="password">Mot de passe</string>
<string name="instance">Instance</string>
<string name="two_fa">Jeton d\'authentification à deux facteurs</string>
<string name="delete_account_title">Supprimer ce compte</string>
<string name="comments_enabled">Activer les commentaires</string>
<string name="download_enabled">Activer le téléchargement</string>
@ -166,7 +169,7 @@
<string name="kabyle">Kabyle</string>
<string name="kalaallisut">Groenlandais</string>
<string name="kannada">Kannada</string>
<string name="kashmiri">Kashmiri</string>
<string name="kashmiri">Cachemire</string>
<string name="georgian">Géorgien</string>
<string name="kanuri">Kanouri</string>
<string name="kazakh">Kazakh</string>

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="go_live">Iniciar emisión en directo</string>
<string name="connect">Conectar</string>
<string name="advanced_settings">▶ Axustes avanzados</string>
<string name="advanced_settings_expand">▼ Axustes avanzados</string>
<string name="connection">Conexión</string>
<string name="comedy">Comedia</string>
<string name="amharic">Amharic</string>
<string name="aragonese">Aragonés</string>
<string name="stream_title_error">O título non pode quedar baleiro</string>
<string name="instance_error">Hai que indicar unha instancia</string>
<string name="username_error">Debes escribir un nome de usuaria</string>
<string name="password_error">Hai que escribir o contrasinal</string>
<string name="username">Nome de usuaria</string>
<string name="guarani">Guaraní</string>
<string name="german_sign_language">Idioma de signos alemán</string>
<string name="indonesian">Indonesio</string>
<string name="inupiaq">Inupiaq</string>
<string name="limburgan">Limburgan</string>
<string name="ukrainian">Ucraniano</string>
<string name="urdu">Urdu</string>
<string name="uzbek">Uzbek</string>
<string name="app_name">PeerTube Live</string>
<string name="unknwon_error">Erro descoñecido</string>
<string name="network_error">Non se detecta conexión a internet</string>
<string name="unknown_host">Servidor descoñecido</string>
<string name="json_error">Erro JSON</string>
<string name="loading_channels">Cargando a túa lista de canles</string>
<string name="account_exist">Esta conta xa existe</string>
<string name="malformed_instance_error">Escribe un URL válido para a instancia</string>
<string name="no_instance">Sen conta rexistrada. Engade unha conta PeerTube usando o \'+\' na barra superior</string>
<string name="try_connect">Agardando pola conexión</string>
<string name="permissions">Precisa acceso á cámara e micrófono para retransmitir. Preme embaixo para conceder permiso.</string>
<string name="delete_account">Eliminar a conta %s asociada ao servidor %s\?</string>
<string name="tags_rules">Máximo 5 etiquetas, de entre 2 e 30 caracteres, separadas por vírgulas</string>
<string name="back_reason">A emisión en directo rematou após premer o botón atrás</string>
<string name="background_reason">A emisión en directo rematou porque a app pasou ao segundo plano</string>
<string name="stop_reason">Rematou a emisión en directo</string>
<string name="ask_end_stream">Queres deter a emisión en directo\?</string>
<string name="entertainment">Entretemento</string>
<string name="how_to">Instruccións</string>
<string name="education">Educación</string>
<string name="bashkir">Bashkir</string>
<string name="brazilian_sign_language">Idioma de signos brasileiro</string>
<string name="faroese">Faroese</string>
<string name="galician">Galego</string>
<string name="save_replay_info">Se activas esta opción, o teu directo rematará se excede a túa cota de vídeo</string>
<string name="lock_reason">A emisión en directo rematou porque se bloqueou o teléfono</string>
<string name="network_reason">A emisión en directo rematou porque houbo problemas coa rede</string>
<string name="live_disabled">A instancia desactivou a emisión en directo.</string>
<string name="stream_title">Título</string>
<string name="stream_privacy">Privacidade</string>
<string name="choose_channel">Canle</string>
<string name="cancel">Cancelar</string>
<string name="yes">Si</string>
<string name="no">Non</string>
<string name="stream_category">Categoría</string>
<string name="stream_language">Idioma</string>
<string name="stream_licence">Licenza</string>
<string name="goto_permissions">Ver axustes</string>
<string name="gaming">Xogos</string>
<string name="afar">Afar</string>
<string name="danish_sign_language">Idioma de signos danés</string>
<string name="add_instance">Engadir esta conta</string>
<string name="password">Contrasinal</string>
<string name="stream_resolution">Resolución da emisión en directo</string>
<string name="end_stream">Deter emisión en directo</string>
<string name="art">Arte</string>
<string name="sports">Deporte</string>
<string name="travels">Viaxes</string>
<string name="people">Persoas</string>
<string name="news_politics">Novas e política</string>
<string name="activism">Activismo</string>
<string name="instance">Instancia</string>
<string name="delete_account_title">Eliminar esta conta</string>
<string name="comments_enabled">Activar comentarios ao vídeo</string>
<string name="download_enabled">Permitir descarga</string>
<string name="nsfw">Contén contido sensible</string>
<string name="tags">Etiquetas</string>
<string name="description">Descrición</string>
<string name="save_replay">Publicar automáticamente unha repetición ao rematar o directo</string>
<string name="stream_ended">Rematou a emisión en directo</string>
<string name="creating">Agardar pola creación do directo</string>
<string name="exemple_instance">ex. peertube.fr, https://peertube.fr</string>
<string name="music">Música</string>
<string name="films">Películas</string>
<string name="akan">Akan</string>
<string name="american_sign_language">Idioma de signos americano</string>
<string name="breton">Bretón</string>
<string name="pushto">Pushto</string>
<string name="vehicles">Vehículos</string>
<string name="science_tech">Ciencia e Tecnoloxía</string>
<string name="animals">Animais</string>
<string name="kids">Rapazada</string>
<string name="food">Comida</string>
<string name="abkhazian">Abkhazian</string>
<string name="afrikaans">Afrikaans</string>
<string name="arabic">Árabe</string>
<string name="assamese">Assamese</string>
<string name="avaric">Avaric</string>
<string name="aymara">Aymara</string>
<string name="bislama">Bislama</string>
<string name="bulgarian">Búlgaro</string>
<string name="chamorro">Chamorro</string>
<string name="kotava">Kotava</string>
<string name="bambara">Bambara</string>
<string name="belarusian">Belarusian</string>
<string name="british_sign_language">Idioma de signos británico</string>
<string name="azerbaijani">Azerbaijani</string>
<string name="bengali">Bengali</string>
<string name="tibetan">Tibetan</string>
<string name="bosnian">Bosnian</string>
<string name="welsh">Galés</string>
<string name="estonian">Estonio</string>
<string name="catalan">Catalán</string>
<string name="czech">Checo</string>
<string name="chechen">Checheno</string>
<string name="chuvash">Chuvash</string>
<string name="cornish">Cornish</string>
<string name="corsican">Corso</string>
<string name="cree">Cree</string>
<string name="danish">Danés</string>
<string name="czech_sign_language">Idioma de signos checo</string>
<string name="chinese_sign_language">Idioma de signos chinés</string>
<string name="dhivehi">Dhivehi</string>
<string name="german">Alemán</string>
<string name="persian">Persa</string>
<string name="finnish">Finlandés</string>
<string name="western_frisian">Frisio occidental</string>
<string name="french_sign_language">Idioma de signos francés</string>
<string name="fulah">Fulah</string>
<string name="dzongkha">Dzongkha</string>
<string name="greek">Grego</string>
<string name="basque">Euskera</string>
<string name="ewe">Ewe</string>
<string name="fijian">Fijian</string>
<string name="french">Francés</string>
<string name="english">Inglés</string>
<string name="esperanto">Esperanto</string>
<string name="kikuyu">Kikuyu</string>
<string name="scottish_gaelic">Gaélico escocés</string>
<string name="croatian">Croata</string>
<string name="irish">Irlandés</string>
<string name="manx">Manx</string>
<string name="gujarati">Gujarati</string>
<string name="haitian">Haitiano</string>
<string name="hausa">Hausa</string>
<string name="serbo_croatian">Serbo-Croata</string>
<string name="hebrew">Hebreo</string>
<string name="hindi">Hindi</string>
<string name="hiri_motu">Hiri Motu</string>
<string name="herero">Herero</string>
<string name="kinyarwanda">Kinyarwanda</string>
<string name="burmese">Burmese</string>
<string name="ojibwa">Ojibwa</string>
<string name="hungarian">Húngaro</string>
<string name="armenian">Armenio</string>
<string name="kalaallisut">Kalaallisut</string>
<string name="kazakh">Kazakh</string>
<string name="lingala">Lingala</string>
<string name="ganda">Ganda</string>
<string name="macedonian">Macedonian</string>
<string name="navajo">Navajo</string>
<string name="nepali_macrolanguage">Nepali (macrolanguage)</string>
<string name="igbo">Igbo</string>
<string name="sichuan_yi">Sichuan Yi</string>
<string name="icelandic">Islandés</string>
<string name="javanese">Javanese</string>
<string name="inuktitut">Inuktitut</string>
<string name="japanese">Xaponés</string>
<string name="italian">Italiano</string>
<string name="kabyle">Kabyle</string>
<string name="khmer">Khmer</string>
<string name="kirghiz">Kirghiz</string>
<string name="lojban">Lojban</string>
<string name="japanese_sign_language">Idioma de signos xaponés</string>
<string name="kannada">Kannada</string>
<string name="kashmiri">Kashmiri</string>
<string name="georgian">Georgian</string>
<string name="kanuri">Kanuri</string>
<string name="kongo">Kongo</string>
<string name="komi">Komi</string>
<string name="korean">Coreano</string>
<string name="kurdish">Curdo</string>
<string name="latvian">Letón</string>
<string name="luba_katanga">Luba-Katanga</string>
<string name="malayalam">Malayalam</string>
<string name="maltese">Maltés</string>
<string name="kuanyama">Kuanyama</string>
<string name="lao">Lao</string>
<string name="lithuanian">Lituano</string>
<string name="luxembourgish">Luxemburgués</string>
<string name="marshallese">Mashallese</string>
<string name="marathi">Marathi</string>
<string name="malagasy">Malagsy</string>
<string name="maori">Maori</string>
<string name="mongolian">Mongolian</string>
<string name="malay_macrolanguage">Malay (macrolanguage)</string>
<string name="nauru">Nauru</string>
<string name="ndonga">Ndonga</string>
<string name="norwegian_nynorsk">Noruegués Nynorsk</string>
<string name="norwegian_bokmål">Noruegués Bokmål</string>
<string name="norwegian">Noruegués</string>
<string name="occitan">Occitano</string>
<string name="ossetian">Ossetian</string>
<string name="dutch">Neerlandés</string>
<string name="sinhala">Sinhala</string>
<string name="thai">Thai</string>
<string name="bynd">Atribución - Sen derivados</string>
<string name="nyanja">Nyanja</string>
<string name="oriya_macrolanguage">Oriya (macrolanguage)</string>
<string name="slovak">Eslovaco</string>
<string name="slovenian">Esloveno</string>
<string name="oromo">Oromo</string>
<string name="south_african_sign_language">Idioma de singos de África do sur</string>
<string name="swedish_sign_language">Idioma de signos sueco</string>
<string name="panjabi">Panjabi</string>
<string name="polish">Polaco</string>
<string name="portuguese">Portugués</string>
<string name="quechua">Quechua</string>
<string name="romansh">Romansh</string>
<string name="russian_sign_language">Idioma de signos ruso</string>
<string name="rundi">Rundi</string>
<string name="pakistan_sign_language">Idioma de signos de Paquistán</string>
<string name="romanian">Rumanés</string>
<string name="russian">Ruso</string>
<string name="sango">Sango</string>
<string name="saudi_arabian_sign_language">Idioma de signos de Arabia Saudita</string>
<string name="northern_sami">Sami do Norte</string>
<string name="shona">Shona</string>
<string name="spanish">Español</string>
<string name="samoan">Samoano</string>
<string name="sindhi">Sindhi</string>
<string name="somali">Somalí</string>
<string name="southern_sotho">Sotho do Sur</string>
<string name="swahili_macrolanguage">Swahili (macrolanguage)</string>
<string name="tamil">Tamil</string>
<string name="turkmen">Turkmen</string>
<string name="turkish">Turco</string>
<string name="twi">Twi</string>
<string name="vietnamese">Vietnamita</string>
<string name="by">Atribución</string>
<string name="byncnd">Atribución - Non Comercial - Sen Derivados</string>
<string name="public_domain">Dedicado ao Dominio Público</string>
<string name="privacy_public">Público</string>
<string name="internal">Interno</string>
<string name="albanian">Albanés</string>
<string name="sardinian">Sardo</string>
<string name="serbian">Serbio</string>
<string name="swati">Swati</string>
<string name="sundanese">Sundanese</string>
<string name="tahitian">Tahitian</string>
<string name="telugu">Telugu</string>
<string name="tajik">Tajik</string>
<string name="tagalog">Tagalog</string>
<string name="tigrinya">Tigrinya</string>
<string name="klingon">Klingon</string>
<string name="tonga_tonga_islands">Tonga (Illas Tonga)</string>
<string name="swedish">Sueco</string>
<string name="tatar">Tatar</string>
<string name="venda">Venda</string>
<string name="walloon">Walloon</string>
<string name="bysa">Atribución - Compartir Igual</string>
<string name="tswana">Tswana</string>
<string name="tsonga">Tsonga</string>
<string name="uighur">Uighur</string>
<string name="wolof">Wolof</string>
<string name="xhosa">Xhosa</string>
<string name="yiddish">Yiddish</string>
<string name="yoruba">Yoruba</string>
<string name="zhuang">Zhuang</string>
<string name="chinese">Chinés</string>
<string name="bync">Atribución - Non Comercial</string>
<string name="zulu">Zulu</string>
<string name="byncsa">Atribución - Non Comercial - Compartir Igual</string>
<string name="unlisted">Fóra de listaxes</string>
<string name="privacy_private">Privado</string>
<string name="south_ndebele">Ndebele do sur</string>
<string name="north_ndebele">Ndebele do norte</string>
</resources>

View File

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="username_error">A felhasználónév nem lehet üres</string>
<string name="instance_error">A példány nem lehet üres</string>
<string name="loading_channels">Saját csatornalista betöltése</string>
<string name="unknwon_error">Ismeretlen hiba</string>
<string name="unknown_host">Ismeretlen kiszolgáló</string>
<string name="json_error">JSON hiba</string>
<string name="stream_title_error">A cím nem lehet üres</string>
<string name="password_error">A jelszó nem lehet üres</string>
<string name="malformed_instance_error">A példánynak érvényes URL-nek kell lennie</string>
<string name="account_exist">Ez a fiók már létezik</string>
<string name="no_instance">Nincs fiók regisztrálva. Adjon hozzá egy PeerTube-fiókot a felső sávban lévő „+” gombbal</string>
<string name="try_connect">Várakozás a kapcsolatra</string>
<string name="permissions">Az elő adáshoz kamera- és mikrofon-hozzáférési engedély szükséges. Kattintson lent az engedélybeállítások módosításához.</string>
<string name="tags_rules">Legfeljebb 5 címke, mindegyik 2 és 30 karakter közti hosszúsággal, vesszőkkel elválasztva</string>
<string name="save_replay_info">Ha engedélyezi ezt a beállítást, akkor az elő adás megszakad, ha túllépi a videókvótáját</string>
<string name="network_reason">Az elő adása hálózati probléma miatt befejeződött</string>
<string name="live_disabled">Ez a példány letiltotta az élő adásokat.</string>
<string name="choose_channel">Csatorna</string>
<string name="go_live">Élő adás indítása</string>
<string name="stream_licence">Licenc</string>
<string name="advanced_settings">▶ Speciális beállítások</string>
<string name="add_instance">Fiók hozzáadása</string>
<string name="music">Zene</string>
<string name="save_replay">Felvétel automatikus közzététele, ha az élő adás véget ér</string>
<string name="stream_ended">Az élő adás véget ért</string>
<string name="end_stream">Élő adás leállítása</string>
<string name="creating">Várakozás az élő adás létrehozására</string>
<string name="stream_resolution">Élő adás felbontása</string>
<string name="exemple_instance">például peertube.fr, https://peertube.fr</string>
<string name="films">Filmek</string>
<string name="vehicles">Járművek</string>
<string name="art">Művészet</string>
<string name="sports">Sport</string>
<string name="travels">Utazás</string>
<string name="gaming">Játék</string>
<string name="people">Emberek</string>
<string name="comedy">Humor</string>
<string name="entertainment">Szórakoztatás</string>
<string name="news_politics">Hírek és politika</string>
<string name="how_to">Hogyanok</string>
<string name="education">Oktatás</string>
<string name="activism">Aktivizmus</string>
<string name="science_tech">Tudomány és technológia</string>
<string name="animals">Állatok</string>
<string name="kids">Gyerekek</string>
<string name="food">Ételek</string>
<string name="afar">Afar</string>
<string name="abkhazian">Abház</string>
<string name="afrikaans">Afrikaans</string>
<string name="akan">Akan</string>
<string name="amharic">Amhara</string>
<string name="arabic">Arab</string>
<string name="aragonese">Aragóniai</string>
<string name="american_sign_language">Amerikai jelnyelv</string>
<string name="assamese">Asszámi</string>
<string name="avaric">Kaukázusi avar</string>
<string name="kotava">Kotava</string>
<string name="aymara">Ajmara</string>
<string name="azerbaijani">Azeri</string>
<string name="bashkir">Baskír</string>
<string name="bambara">Bambara</string>
<string name="belarusian">Belarusz</string>
<string name="bengali">Bengáli</string>
<string name="british_sign_language">Brit jelnyelv</string>
<string name="bislama">Biszlama</string>
<string name="tibetan">Tibeti</string>
<string name="bosnian">Bosnyák</string>
<string name="breton">Breton</string>
<string name="bulgarian">Bolgár</string>
<string name="brazilian_sign_language">Brazil jelnyelv</string>
<string name="catalan">Katalán</string>
<string name="czech">Cseh</string>
<string name="chechen">Csecsen</string>
<string name="chuvash">Csuvas</string>
<string name="cornish">Korni</string>
<string name="corsican">Korzikai</string>
<string name="czech_sign_language">Cseh jelnyelv</string>
<string name="chinese_sign_language">Kínai jelnyelv</string>
<string name="welsh">Walesi</string>
<string name="cree">Krí</string>
<string name="chamorro">Csamoró</string>
<string name="danish">Dán</string>
<string name="german">Német</string>
<string name="danish_sign_language">Dán jelnyelv</string>
<string name="greek">Görög</string>
<string name="english">Angol</string>
<string name="esperanto">Eszperantó</string>
<string name="estonian">Észt</string>
<string name="basque">Baszk</string>
<string name="ewe">Ewe</string>
<string name="faroese">Feröeri</string>
<string name="persian">Perzsa</string>
<string name="finnish">Finn</string>
<string name="french">Francia</string>
<string name="western_frisian">Nyugati fríz</string>
<string name="fijian">Fidzsi</string>
<string name="french_sign_language">Francia jelnyelv</string>
<string name="dhivehi">Divehi</string>
<string name="dzongkha">Dzsonga</string>
<string name="fulah">Ful</string>
<string name="scottish_gaelic">Skót gael</string>
<string name="irish">Ír</string>
<string name="indonesian">Indonéz</string>
<string name="inupiaq">Inupiak</string>
<string name="komi">Komi</string>
<string name="kalaallisut">Grönlandi</string>
<string name="kikuyu">Kikuju</string>
<string name="limburgan">Limburgi</string>
<string name="ojibwa">Odzsibva</string>
<string name="pushto">Pastu</string>
<string name="app_name">PeerTube Live</string>
<string name="network_error">Nincs internetkapcsolat</string>
<string name="zulu">Zulu</string>
<string name="by">Nevezd meg!</string>
<string name="bysa">Nevezd meg! Így add tovább!</string>
<string name="bynd">Nevezd meg! Ne változtasd!</string>
<string name="bync">Nevezd meg! Ne add el!</string>
<string name="byncsa">Nevezd meg! Ne add el! Így add tovább!</string>
<string name="byncnd">Nevezd meg! Ne add el! Ne változtasd!</string>
<string name="public_domain">Közkincsnek jelölt</string>
<string name="privacy_public">Nyilvános</string>
<string name="unlisted">Nem felsorolt</string>
<string name="privacy_private">Privát</string>
<string name="internal">Belső</string>
<string name="ukrainian">Ukrán</string>
<string name="urdu">Urdu</string>
<string name="uzbek">Üzbég</string>
<string name="venda">Venda</string>
<string name="vietnamese">Vietnámi</string>
<string name="walloon">Vallon</string>
<string name="wolof">Volof</string>
<string name="xhosa">Xhosza</string>
<string name="yiddish">Jiddis</string>
<string name="delete_account">Törli a(z) %s fiókot, amely a(z) %s kiszolgálóhoz kapcsolódik\?</string>
<string name="lock_reason">Az elő adása befejeződött, mert a telefon zárolva lett</string>
<string name="background_reason">Az élő adása befejeződött, mert az alkalmazás a háttérbe került</string>
<string name="ask_end_stream">Leállítja az élő adást\?</string>
<string name="back_reason">Az elő adása befejeződött, miután megnyomta a vissza gombot</string>
<string name="stop_reason">Az élő adása befejeződött</string>
<string name="stream_privacy">Adatvédelem</string>
<string name="cancel">Mégse</string>
<string name="yes">Igen</string>
<string name="no">Nem</string>
<string name="goto_permissions">Beállítások megtekintése</string>
<string name="instance">Példány</string>
<string name="tags">Címkék</string>
<string name="description">Leírás</string>
<string name="connect">Kapcsolódás</string>
<string name="stream_title">Cím</string>
<string name="stream_category">Kategória</string>
<string name="connection">Kapcsolat</string>
<string name="stream_language">Nyelv</string>
<string name="username">Felhasználónév</string>
<string name="advanced_settings_expand">▼ Speciális beállítások</string>
<string name="nsfw">Érzékeny tartalmat tartalmaz</string>
<string name="password">Jelszó</string>
<string name="delete_account_title">Példány törlése</string>
<string name="comments_enabled">Videó hozzászólásainak engedélyezése</string>
<string name="download_enabled">Letöltés engedélyezése</string>
<string name="galician">Galiciai</string>
<string name="manx">Manx</string>
<string name="guarani">Guarani</string>
<string name="german_sign_language">Német jelnyelv</string>
<string name="gujarati">Gudzsaráti</string>
<string name="haitian">Haiti</string>
<string name="hebrew">Héber</string>
<string name="herero">Herero</string>
<string name="hindi">Hindi</string>
<string name="hiri_motu">Hiri motu</string>
<string name="croatian">Horvát</string>
<string name="hungarian">Magyar</string>
<string name="armenian">Örmény</string>
<string name="hausa">Hausza</string>
<string name="serbo_croatian">Szerbhorvát</string>
<string name="igbo">Igbó</string>
<string name="sichuan_yi">Szecsuani ji</string>
<string name="inuktitut">Inuktitut</string>
<string name="icelandic">Izlandi</string>
<string name="italian">Olasz</string>
<string name="javanese">Jávai</string>
<string name="lojban">Lojban</string>
<string name="japanese">Japán</string>
<string name="kannada">Kannada</string>
<string name="kashmiri">Kasmíri</string>
<string name="japanese_sign_language">Japán jelnyelv</string>
<string name="georgian">Grúz</string>
<string name="kanuri">Kanuri</string>
<string name="kazakh">Kazak</string>
<string name="khmer">Khmer</string>
<string name="kinyarwanda">Kinyarvanda</string>
<string name="kirghiz">Kirgiz</string>
<string name="kabyle">Kabil</string>
<string name="kongo">Kongo</string>
<string name="korean">Koreai</string>
<string name="kurdish">Kurd</string>
<string name="lao">Lao</string>
<string name="latvian">Lett</string>
<string name="lingala">Lingala</string>
<string name="lithuanian">Litván</string>
<string name="luxembourgish">Luxemburgi</string>
<string name="luba_katanga">Luba-katanga</string>
<string name="ganda">Ganda</string>
<string name="marshallese">Marshall-szigeteki</string>
<string name="malayalam">Malajálam</string>
<string name="marathi">Maráthi</string>
<string name="macedonian">Macedón</string>
<string name="malagasy">Malgas</string>
<string name="maltese">Máltai</string>
<string name="mongolian">Mongol</string>
<string name="south_ndebele">Déli ndebele</string>
<string name="north_ndebele">Északi ndebele</string>
<string name="maori">Maori</string>
<string name="malay_macrolanguage">Maláj (makronyelv)</string>
<string name="burmese">Burmai</string>
<string name="nauru">Naurui</string>
<string name="navajo">Navahó</string>
<string name="ndonga">Ndonga</string>
<string name="nepali_macrolanguage">Nepáli (makronyelv)</string>
<string name="dutch">Holland</string>
<string name="norwegian_nynorsk">Norvég nynorsk</string>
<string name="norwegian_bokmål">Norvég bokmål</string>
<string name="norwegian">Norvég</string>
<string name="occitan">Okcitán</string>
<string name="oriya_macrolanguage">Orija (makronyelv)</string>
<string name="oromo">Oromó</string>
<string name="nyanja">Nyandzsa</string>
<string name="ossetian">Oszét</string>
<string name="panjabi">Pandzsábi</string>
<string name="pakistan_sign_language">Pakisztáni jelnyelv</string>
<string name="polish">Lengyel</string>
<string name="portuguese">Portugál</string>
<string name="quechua">Kecsua</string>
<string name="romansh">Romans</string>
<string name="romanian">Román</string>
<string name="russian_sign_language">Orosz jelnyelv</string>
<string name="rundi">Rundi</string>
<string name="russian">Orosz</string>
<string name="sango">Szangó</string>
<string name="south_african_sign_language">Dél-afrikai jelnyelv</string>
<string name="sinhala">Szingaléz</string>
<string name="slovak">Szlovák</string>
<string name="slovenian">Szlovén</string>
<string name="saudi_arabian_sign_language">Szaúd-arábiai jelnyelv</string>
<string name="northern_sami">Északi számi</string>
<string name="samoan">Szamoai</string>
<string name="shona">Sona</string>
<string name="sindhi">Szindhi</string>
<string name="somali">Szomáli</string>
<string name="southern_sotho">Déli szoto</string>
<string name="spanish">Spanyol</string>
<string name="albanian">Albán</string>
<string name="sardinian">Szardíniai</string>
<string name="swahili_macrolanguage">Szuahéli (makronyelv)</string>
<string name="swedish">Svéd</string>
<string name="swedish_sign_language">Svéd jelnyelv</string>
<string name="serbian">Szerb</string>
<string name="swati">Szvázi</string>
<string name="sundanese">Szundanéz</string>
<string name="tahitian">Tahiti</string>
<string name="tamil">Tamil</string>
<string name="tatar">Tatár</string>
<string name="telugu">Telugu</string>
<string name="tajik">Tádzsik</string>
<string name="tonga_tonga_islands">Tonga (Tonga-szigetek)</string>
<string name="tswana">Csvana</string>
<string name="tsonga">Conga</string>
<string name="turkmen">Türkmén</string>
<string name="turkish">Török</string>
<string name="twi">Tvi</string>
<string name="tagalog">Tagalog</string>
<string name="thai">Thai</string>
<string name="tigrinya">Tigrinya</string>
<string name="klingon">Klingon</string>
<string name="uighur">Ujgur</string>
<string name="yoruba">Joruba</string>
<string name="zhuang">Csuang</string>
<string name="chinese">Kínai</string>
</resources>

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PeerTube Live</string>
<string name="network_error">Nie znaleziono połączenia z Internetem</string>
<string name="unknwon_error">Nieznany błąd</string>
<string name="unknown_host">Nieznany Host</string>
<string name="json_error">Błąd JSON</string>
<string name="stream_title_error">Tytuł nie może być pusty</string>
<string name="instance_error">Instancja nie może być pusta</string>
<string name="live_disabled">Ta instancja ma wyłączone transmisje na żywo.</string>
<string name="stream_ended">Transmisja na żywo zakończona</string>
<string name="malformed_instance_error">Instancja musi mieć poprawny adres URL</string>
<string name="account_exist">To konto już istnieje</string>
<string name="loading_channels">Pobieranie listy kanałów</string>
<string name="try_connect">Oczekiwanie na połączenie</string>
<string name="ask_end_stream">Czy chcesz zatrzymać transmisję\?</string>
<string name="stop_reason">Twoja transmisja na żywo zakończyła się</string>
<string name="back_reason">Twoja transmisja na żywo zakończyła się po wciśnięciu przycisku wstecz</string>
<string name="choose_channel">Kanał</string>
<string name="go_live">Rozpocznij transmisję na żywo</string>
<string name="goto_permissions">Przejdź do ustawień</string>
<string name="connect">Połącz</string>
<string name="stream_title">Tytuł</string>
<string name="stream_category">Kategoria</string>
<string name="stream_privacy">Prywatność</string>
<string name="stream_language">Język</string>
<string name="stream_licence">Licencja</string>
<string name="advanced_settings">▶ Ustawienia zaawansowane</string>
<string name="advanced_settings_expand">▼ Ustawienia zaawansowane</string>
<string name="add_instance">Dodaj to konto</string>
<string name="connection">Połączenie</string>
<string name="username">Nazwa użytkownika</string>
<string name="password">Hasło</string>
<string name="instance">Instancja</string>
<string name="delete_account_title">Usuń to konto</string>
<string name="download_enabled">Zezwól na pobieranie</string>
<string name="nsfw">Zawiera treści wrażliwe</string>
<string name="tags">Tagi</string>
<string name="description">Opis</string>
<string name="save_replay">Automatycznie publikuj powtórkę, gdy transmisja zakończy się</string>
<string name="end_stream">Zatrzymaj transmisję na żywo</string>
<string name="creating">Czekaj na utworzenie transmisji</string>
<string name="stream_resolution">Jakość transmisji na żywo</string>
<string name="music">Muzyka</string>
<string name="films">Filmy</string>
<string name="vehicles">Pojazdy</string>
<string name="art">Sztuka</string>
<string name="sports">Sport</string>
<string name="travels">Podróże</string>
<string name="gaming">Gry</string>
<string name="people">Ludzie</string>
<string name="comedy">Komedia</string>
<string name="entertainment">Rozrywka</string>
<string name="news_politics">Wiadomości i polityka</string>
<string name="how_to">Poradniki</string>
<string name="education">Edukacja</string>
<string name="activism">Aktywizm</string>
<string name="science_tech">Nauka i technologia</string>
<string name="food">Jedzenie</string>
<string name="afar">Afar</string>
<string name="abkhazian">Abchazki</string>
<string name="afrikaans">Afrikaans</string>
<string name="amharic">Amharski</string>
<string name="aragonese">Aragoński</string>
<string name="assamese">Assamski</string>
<string name="avaric">Avaric</string>
<string name="kotava">Kotava</string>
<string name="aymara">Aymara</string>
<string name="azerbaijani">Azerski</string>
<string name="bashkir">Bashkir</string>
<string name="bambara">Bambara</string>
<string name="bengali">Bengali</string>
<string name="british_sign_language">Brytyjski język migowy</string>
<string name="tibetan">Tybetański</string>
<string name="catalan">Kataloński</string>
<string name="czech">Czeski</string>
<string name="chamorro">Chamorro</string>
<string name="chechen">Czeczeński</string>
<string name="chuvash">Czuwasz</string>
<string name="cornish">Kornwalijski</string>
<string name="corsican">Korsykański</string>
<string name="german">Niemiecki</string>
<string name="dhivehi">Dhivehi</string>
<string name="danish_sign_language">Duński język migowy</string>
<string name="dzongkha">Dzongkha</string>
<string name="greek">Grecki</string>
<string name="english">Angielski</string>
<string name="estonian">Estoński</string>
<string name="basque">Baskijski</string>
<string name="ewe">Ewe</string>
<string name="faroese">Farerski</string>
<string name="persian">Perski</string>
<string name="fijian">Fidżi</string>
<string name="finnish">Fiński</string>
<string name="french">Francuski</string>
<string name="western_frisian">Zachodniofryzyjski</string>
<string name="scottish_gaelic">Szkocki Gaelic</string>
<string name="irish">Irlandzki</string>
<string name="manx">Manx</string>
<string name="guarani">Guarani</string>
<string name="german_sign_language">Niemiecki język migowy</string>
<string name="haitian">Haitian</string>
<string name="hausa">Hausa</string>
<string name="serbo_croatian">Sebsko-CHorwacki</string>
<string name="hebrew">Hebrajski</string>
<string name="hindi">Hindi</string>
<string name="hiri_motu">Hiri Motu</string>
<string name="croatian">Chorwacki</string>
<string name="igbo">Igbo</string>
<string name="sichuan_yi">Sichuan Yi</string>
<string name="inupiaq">Inupiaq</string>
<string name="icelandic">Islandzki</string>
<string name="italian">Włoski</string>
<string name="lojban">Lojban</string>
<string name="japanese">japoński</string>
<string name="japanese_sign_language">Japoński język migowy</string>
<string name="kabyle">Kabyle</string>
<string name="kalaallisut">Kalaallisut</string>
<string name="kannada">Kannada</string>
<string name="kashmiri">Kashmiri</string>
<string name="georgian">Gruziński</string>
<string name="kanuri">Kanuri</string>
<string name="kazakh">Kazakh</string>
<string name="kirghiz">Kirghiz</string>
<string name="komi">Komi</string>
<string name="kongo">Kongo</string>
<string name="kuanyama">Kuanyama</string>
<string name="lingala">Lingala</string>
<string name="latvian">Łotewski</string>
<string name="luxembourgish">Luksemburski</string>
<string name="luba_katanga">Luba-Katanga</string>
<string name="ganda">Ganda</string>
<string name="marshallese">Marshall</string>
<string name="malayalam">Malayalam</string>
<string name="marathi">Marathi</string>
<string name="macedonian">Macedoński</string>
<string name="maltese">Maltański</string>
<string name="south_ndebele">Południowe Ndebele</string>
<string name="north_ndebele">Północne Ndebele</string>
<string name="ndonga">Ndonga</string>
<string name="nepali_macrolanguage">Nepalski</string>
<string name="norwegian">Norweski</string>
<string name="romanian">Rumuński</string>
<string name="sango">Sango</string>
<string name="saudi_arabian_sign_language">Język migowy Arabii Saudyjskiej</string>
<string name="south_african_sign_language">Południowoafrykański język migowy</string>
<string name="northern_sami">Północni Lapończycy</string>
<string name="samoan">Samoan</string>
<string name="southern_sotho">Południowe Sotho</string>
<string name="spanish">Hiszpański</string>
<string name="albanian">Albański</string>
<string name="sardinian">Sardyński</string>
<string name="serbian">Serbski</string>
<string name="sundanese">Sundajski</string>
<string name="tahitian">Tahitański</string>
<string name="tamil">Tamilski</string>
<string name="tatar">Tatarski</string>
<string name="telugu">Telugu</string>
<string name="thai">Tajski</string>
<string name="tigrinya">Tigrinya</string>
<string name="tsonga">Tsonga</string>
<string name="turkmen">Turmeński</string>
<string name="yoruba">Yoruba</string>
<string name="zhuang">Zhuang</string>
<string name="chinese">Chiński</string>
<string name="bynd">Uznanie autorstwa - Bez utworów zależnych</string>
<string name="bync">Uznanie autorstwa - Użycie niekomercyjne</string>
<string name="byncnd">Uznanie autorstwa - Użycie niekomercyjne - Bez utworów zależnych</string>
<string name="public_domain">Domena Publiczna</string>
<string name="privacy_public">Publiczne</string>
<string name="privacy_private">Prywatne</string>
<string name="unlisted">Niepubliczne</string>
<string name="internal">Wewnętrzne</string>
<string name="username_error">Nazwa użytkownika nie może być pusta</string>
<string name="password_error">Hasło nie może być puste</string>
<string name="no_instance">Brak zarejestrowanego konta. Dodaj konto PeerTube używając \'+\' na górnym pasku</string>
<string name="permissions">Do transmisji na żywo potrzebny jest dostęp do kamery i mikrofonu. Kliknij poniżej, aby zmienić ustawienia uprawnień.</string>
<string name="delete_account">Usunąć konto %s powiązane z serwerem %s\?</string>
<string name="tags_rules">Maksymalnie 5 tagów, każdy o długości od 2 do 30 znaków, oddzielone przecinkiem</string>
<string name="cancel">Anuluj</string>
<string name="yes">Tak</string>
<string name="background_reason">Twoja transmisja na żywo zakończyła się, ponieważ aplikacja przeszła do pracy w tle</string>
<string name="lock_reason">Twoja transmisja na żywo zakończyła się, ponieważ telefon został zablokowany</string>
<string name="network_reason">Twoja transmisja na żywo zakończyła się z powodu problemów z siecią</string>
<string name="no">Nie</string>
<string name="comments_enabled">Włącz komentarze do filmu</string>
<string name="galician">Galicyjski</string>
<string name="gujarati">Gujarati</string>
<string name="exemple_instance">np. peertube.fr, https://peertube.fr</string>
<string name="hungarian">Węgierski</string>
<string name="akan">Akan</string>
<string name="bosnian">Bośniacki</string>
<string name="bulgarian">Bułgarski</string>
<string name="belarusian">Białoruski</string>
<string name="animals">Zwierzęta</string>
<string name="kids">Dzieci</string>
<string name="arabic">Arabski</string>
<string name="american_sign_language">Amerykański język migowy</string>
<string name="bislama">Bislama</string>
<string name="breton">Bretoński</string>
<string name="brazilian_sign_language">Brazylijski język migowy</string>
<string name="danish">Duński</string>
<string name="esperanto">Esperanto</string>
<string name="herero">Herero</string>
<string name="lithuanian">Litewski</string>
<string name="maori">Maori</string>
<string name="panjabi">Pendjabi</string>
<string name="polish">Polski</string>
<string name="portuguese">Portugalski</string>
<string name="rundi">Rundi</string>
<string name="cree">Cree</string>
<string name="czech_sign_language">Czeski język migowy</string>
<string name="chinese_sign_language">Chiński język migowy</string>
<string name="welsh">Walijski</string>
<string name="french_sign_language">Francuski język migowy</string>
<string name="fulah">Fulah</string>
<string name="armenian">Armeński</string>
<string name="inuktitut">Inuktitut</string>
<string name="indonesian">Indonesian</string>
<string name="javanese">Javanese</string>
<string name="limburgan">Limburgan</string>
<string name="malagasy">Malgaski</string>
<string name="norwegian_nynorsk">Norweski Nynorsk</string>
<string name="nyanja">Nyanja</string>
<string name="kikuyu">Kikuyu</string>
<string name="khmer">Khmer</string>
<string name="kinyarwanda">Rwanda</string>
<string name="korean">Koreański</string>
<string name="kurdish">Kurdyjski</string>
<string name="lao">Lao</string>
<string name="oriya_macrolanguage">Oriya</string>
<string name="malay_macrolanguage">Malajski</string>
<string name="nauru">Nauru</string>
<string name="navajo">Navaho</string>
<string name="mongolian">Mongolski</string>
<string name="burmese">Birmański</string>
<string name="dutch">Holenderski</string>
<string name="norwegian_bokmål">Norweski Bokmål</string>
<string name="occitan">Occitan</string>
<string name="oromo">Oromo</string>
<string name="ojibwa">Ojibwa</string>
<string name="ossetian">Ossetian</string>
<string name="pakistan_sign_language">Język migowy Pakistanu</string>
<string name="pushto">Pachto</string>
<string name="romansh">Romański</string>
<string name="russian_sign_language">Rosyjski język migowy</string>
<string name="russian">Rosyjski</string>
<string name="slovenian">Słoweński</string>
<string name="sindhi">Sindhi</string>
<string name="quechua">Quechua</string>
<string name="sinhala">Sinhala</string>
<string name="slovak">Słowacki</string>
<string name="shona">Shona</string>
<string name="somali">Somalijski</string>
<string name="swati">Swati</string>
<string name="tagalog">Tagalog</string>
<string name="swahili_macrolanguage">Swahili</string>
<string name="swedish">Szwedzki</string>
<string name="swedish_sign_language">Szwedzki język migowy</string>
<string name="tajik">Tajik</string>
<string name="klingon">Klingon</string>
<string name="tswana">Tswana</string>
<string name="xhosa">Xhosa</string>
<string name="yiddish">Yiddish</string>
<string name="bysa">Uznanie autorstwa - Na tych samych zasadach</string>
<string name="by">Uznanie autorstwa</string>
<string name="byncsa">Uznanie autorstwa - Użycie niekomercyjne - Na tych samych warunkach</string>
<string name="tonga_tonga_islands">Tonga</string>
<string name="twi">Twi</string>
<string name="ukrainian">Ukraiński</string>
<string name="walloon">Walloon</string>
<string name="turkish">Turecki</string>
<string name="uighur">Ujgurski</string>
<string name="urdu">Urdu</string>
<string name="uzbek">Uzbecki</string>
<string name="venda">Venda</string>
<string name="vietnamese">Wietnamski</string>
<string name="wolof">Wolof</string>
<string name="zulu">Zulu</string>
<string name="save_replay_info">Jeśli włączysz tę opcję, twoja transmisja zostanie przerwana jeśli przekroczysz limit miejsca na filmy</string>
</resources>

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PeerTube Live</string>
<string name="account_exist">Цей обліковий запис уже існує</string>
<string name="network_error">Відсутнє інтернет-з\'єднання</string>
<string name="unknwon_error">Невідома помилка</string>
<string name="unknown_host">Невідомий хост</string>
<string name="json_error">Помилка JSON</string>
<string name="stream_title_error">Назва не повинна бути порожньою</string>
<string name="username_error">Ім\'я користувача не може бути порожнім</string>
<string name="instance_error">Сервер не може бути порожнім</string>
<string name="password_error">Пароль не може бути порожнім</string>
<string name="no_instance">Немає зареєстрованого облікового запису. Додайте обліковий запис PeerTube за допомогою кнопки «+» у верхній панелі</string>
<string name="loading_channels">Завантаження списку каналів</string>
<string name="delete_account">Видалити обліковий запис %s, пов\'язаний із сервером %s\?</string>
<string name="tags_rules">Максимум 5 міток, кожна довжиною від 2 до 30 символів, відокремлених комами</string>
<string name="save_replay_info">Якщо ввімкнете цю опцію, трансляція буде припинена, якщо ви перевищите квоту відео</string>
<string name="background_reason">Пряма трансляція завершилася, оскільки застосунок переведено у фоновий режим</string>
<string name="ask_end_stream">Бажаєте зупинити трансляцію\?</string>
<string name="network_reason">Пряма трансляція завершилася через проблему мережі</string>
<string name="malformed_instance_error">Сервер повинен мати дійсну URL-адресу</string>
<string name="go_live">Почати пряму трансляцію</string>
<string name="yes">Так</string>
<string name="stream_title">Назва</string>
<string name="advanced_settings">▶ Розширені налаштування</string>
<string name="advanced_settings_expand">▼ Розширені налаштування</string>
<string name="add_instance">Додати цей обліковий запис</string>
<string name="connection">З\'єднання</string>
<string name="username">Ім’я користувача</string>
<string name="password">Пароль</string>
<string name="delete_account_title">Видалити цей обліковий запис</string>
<string name="comments_enabled">Увімкнути коментарі до відео</string>
<string name="nsfw">Містить делікатний вміст</string>
<string name="tags">Мітки</string>
<string name="description">Опис</string>
<string name="save_replay">Автопублікування повтору після завершення трансляції</string>
<string name="stream_ended">Пряма трансляція завершилася</string>
<string name="end_stream">Зупинити пряму трансляцію</string>
<string name="creating">Дочекайтеся створення трансляції</string>
<string name="stream_resolution">Роздільність трансляції</string>
<string name="exemple_instance">наприклад, peertube.fr, https://peertube.fr</string>
<string name="music">Музика</string>
<string name="films">Фільми</string>
<string name="vehicles">Транспорт</string>
<string name="art">Мистецтво</string>
<string name="sports">Спорт</string>
<string name="travels">Подорожі</string>
<string name="entertainment">Розваги</string>
<string name="news_politics">Новини й політика</string>
<string name="activism">Активізм</string>
<string name="science_tech">Наука й технології</string>
<string name="animals">Тварини</string>
<string name="kids">Діти</string>
<string name="food">Їжа</string>
<string name="afar">Афарська</string>
<string name="abkhazian">Абхазька</string>
<string name="afrikaans">Африкаанс</string>
<string name="akan">Акан</string>
<string name="amharic">Амхарська</string>
<string name="arabic">Арабська</string>
<string name="instance">Сервер</string>
<string name="live_disabled">Цей сервер вимкнув прямі трансляції.</string>
<string name="aragonese">Арагонська</string>
<string name="american_sign_language">Американська жестова мова</string>
<string name="kotava">Котава</string>
<string name="aymara">Аймарська</string>
<string name="azerbaijani">Азербайджанська</string>
<string name="bashkir">Башкирська</string>
<string name="bambara">Бамбара</string>
<string name="belarusian">Білоруська</string>
<string name="british_sign_language">Британська жестова мова</string>
<string name="bislama">Біслама</string>
<string name="tibetan">Тибетська</string>
<string name="bosnian">Боснійська</string>
<string name="breton">Бретонська</string>
<string name="catalan">Каталонська</string>
<string name="czech">Чеська</string>
<string name="chamorro">Чаморро</string>
<string name="chechen">Чеченська</string>
<string name="chuvash">Чуваська</string>
<string name="cornish">Корнська</string>
<string name="corsican">Корсиканська</string>
<string name="cree">Крі</string>
<string name="czech_sign_language">Чеська жестова мова</string>
<string name="chinese_sign_language">Китайська жестова мова</string>
<string name="welsh">Валлійська</string>
<string name="danish">Данська</string>
<string name="german">Німецька</string>
<string name="dhivehi">Мальдівська</string>
<string name="danish_sign_language">Данська жестова мова</string>
<string name="dzongkha">Дзонґ-ке</string>
<string name="greek">Грецька</string>
<string name="esperanto">Есперанто</string>
<string name="ewe">Еве</string>
<string name="faroese">Фарерська</string>
<string name="persian">Перська</string>
<string name="fijian">Фіджійська</string>
<string name="finnish">Фінська</string>
<string name="french">Французька</string>
<string name="french_sign_language">Французька жестова мова</string>
<string name="fulah">Фульфульде</string>
<string name="scottish_gaelic">Шотландська гельська</string>
<string name="irish">Ірландська</string>
<string name="manx">Менська</string>
<string name="german_sign_language">Німецька жестова мова</string>
<string name="hausa">Гауса</string>
<string name="serbo_croatian">Сербохорватська</string>
<string name="herero">Гереро</string>
<string name="hindi">Гінді</string>
<string name="hiri_motu">Гірі-моту</string>
<string name="croatian">Хорватська</string>
<string name="armenian">Вірменська</string>
<string name="igbo">Ігбо</string>
<string name="sichuan_yi">Носу</string>
<string name="inuktitut">Інуктитут</string>
<string name="inupiaq">Інупіак</string>
<string name="icelandic">Ісландська</string>
<string name="italian">Італійська</string>
<string name="javanese">Яванська</string>
<string name="lojban">Ложбан</string>
<string name="japanese">Японська</string>
<string name="japanese_sign_language">Японська жестова мова</string>
<string name="kabyle">Кабільська</string>
<string name="kalaallisut">Гренландська</string>
<string name="kannada">Каннада</string>
<string name="kashmiri">Кашмірська</string>
<string name="georgian">Грузинська</string>
<string name="kanuri">Канурі</string>
<string name="kazakh">Казахська</string>
<string name="khmer">Кхмерська</string>
<string name="kikuyu">Кікую</string>
<string name="kinyarwanda">Руандійська</string>
<string name="kirghiz">Киргизька</string>
<string name="komi">Комі</string>
<string name="kongo">Конґо</string>
<string name="korean">Корейська</string>
<string name="kuanyama">Кваньяма</string>
<string name="kurdish">Курдська</string>
<string name="lao">Лаоська</string>
<string name="latvian">Латиська</string>
<string name="limburgan">Лімбурзька</string>
<string name="lingala">Лінґала</string>
<string name="lithuanian">Литовська</string>
<string name="luba_katanga">Луба-Катанґа</string>
<string name="ganda">Гандійська</string>
<string name="marshallese">Маршальська</string>
<string name="malayalam">Малаялам</string>
<string name="macedonian">Македонська</string>
<string name="malagasy">Малагасійська</string>
<string name="maltese">Мальтійська</string>
<string name="maori">Маорійська</string>
<string name="malay_macrolanguage">Малайська (макромова)</string>
<string name="burmese">Бірманська</string>
<string name="nauru">Науру</string>
<string name="navajo">Навахо</string>
<string name="north_ndebele">Північна Ндебеле</string>
<string name="ndonga">Ндонга</string>
<string name="nepali_macrolanguage">Непальська (макромова)</string>
<string name="norwegian_nynorsk">Норвезька Нюношк</string>
<string name="norwegian_bokmål">Норвезька Букмол</string>
<string name="nyanja">Ньянджа</string>
<string name="occitan">Окситанська</string>
<string name="ojibwa">Оджибвемовінська</string>
<string name="oriya_macrolanguage">Орія (макромова)</string>
<string name="oromo">Оромо</string>
<string name="ossetian">Осетинська</string>
<string name="panjabi">Панджабі</string>
<string name="pakistan_sign_language">Пакистанська жестова мова</string>
<string name="pushto">Пушту</string>
<string name="quechua">Кечуа</string>
<string name="romansh">Ретороманська</string>
<string name="romanian">Румунська</string>
<string name="rundi">Рунді</string>
<string name="russian">Російська</string>
<string name="sango">Санґо</string>
<string name="saudi_arabian_sign_language">Жестова мова Саудівської Аравії</string>
<string name="south_african_sign_language">Південноафриканська жестова мова</string>
<string name="sinhala">Сингальська</string>
<string name="slovak">Словацька</string>
<string name="northern_sami">Північносаамська</string>
<string name="samoan">Самоанська</string>
<string name="shona">Шона</string>
<string name="sindhi">Синдхі</string>
<string name="somali">Сомалійська</string>
<string name="southern_sotho">Південне Сото</string>
<string name="sardinian">Сардинська</string>
<string name="serbian">Сербська</string>
<string name="swati">Сваті</string>
<string name="sundanese">Сунданська</string>
<string name="swahili_macrolanguage">Суахілі (макромова)</string>
<string name="telugu">Телугу</string>
<string name="tajik">Таджицька</string>
<string name="tagalog">Тагальська</string>
<string name="thai">Тайська</string>
<string name="tigrinya">Тигринья</string>
<string name="klingon">Клінгонська</string>
<string name="tonga_tonga_islands">Тонга (Острови Тонга)</string>
<string name="tsonga">Тсонга</string>
<string name="turkmen">Туркменська</string>
<string name="twi">Тві</string>
<string name="uighur">Уйгурська</string>
<string name="ukrainian">Українська</string>
<string name="urdu">Урду</string>
<string name="uzbek">Узбецька</string>
<string name="venda">Венда</string>
<string name="vietnamese">В\'єтнамська</string>
<string name="walloon">Валлонська</string>
<string name="wolof">Волоф</string>
<string name="xhosa">Коса</string>
<string name="yiddish">Їдиш</string>
<string name="yoruba">Йоруба</string>
<string name="zhuang">Чжуанська</string>
<string name="chinese">Китайська</string>
<string name="zulu">Зулуська</string>
<string name="by">Атрибуція</string>
<string name="bysa">Атрибуція - Спільний доступ</string>
<string name="bynd">Атрибуція - Без похідних</string>
<string name="bync">Атрибуція - Некомерційна</string>
<string name="byncsa">Атрибуція - Некомерційна - На тих же умовах</string>
<string name="byncnd">Атрибуція - Некомерційна - Без похідних</string>
<string name="public_domain">Призначено для громадського надбання</string>
<string name="privacy_public">Загальнодоступний</string>
<string name="unlisted">Не входить до списку</string>
<string name="goto_permissions">Переглянути налаштування</string>
<string name="back_reason">Пряму трансляцію завершено натисканням кнопки «Назад»</string>
<string name="lock_reason">Пряма трансляція завершилася, оскільки телефон заблоковано</string>
<string name="try_connect">Очікування з\'єднання</string>
<string name="permissions">Для прямої трансляції потрібен доступ до камери та мікрофона. Натисніть унизу, щоб змінити налаштування дозволів.</string>
<string name="stop_reason">Ваша пряма трансляція завершилася</string>
<string name="choose_channel">Канал</string>
<string name="no">Ні</string>
<string name="cancel">Скасувати</string>
<string name="connect">Під\'єднатися</string>
<string name="stream_category">Категорія</string>
<string name="stream_privacy">Приватність</string>
<string name="stream_language">Мова</string>
<string name="download_enabled">Увімкнути завантаження</string>
<string name="stream_licence">Ліцензія</string>
<string name="gaming">Ігри</string>
<string name="comedy">Комедія</string>
<string name="how_to">Поради</string>
<string name="education">Освіта</string>
<string name="people">Люди</string>
<string name="assamese">Ассамська</string>
<string name="avaric">Аварська</string>
<string name="bulgarian">Болгарська</string>
<string name="brazilian_sign_language">Бразильська жестова мова</string>
<string name="bengali">Бенгальська</string>
<string name="swedish">Шведська</string>
<string name="swedish_sign_language">Шведська жестова мова</string>
<string name="tamil">Тамільська</string>
<string name="tatar">Татарська</string>
<string name="tahitian">Таїтянська</string>
<string name="tswana">Тсвана</string>
<string name="estonian">Естонська</string>
<string name="guarani">Гуарані</string>
<string name="gujarati">Гуджараті</string>
<string name="galician">Галісійська</string>
<string name="english">Англійська</string>
<string name="basque">Баскська</string>
<string name="western_frisian">Західнофризька</string>
<string name="mongolian">Монгольська</string>
<string name="albanian">Албанська</string>
<string name="hebrew">Іврит</string>
<string name="haitian">Гаїтянська</string>
<string name="hungarian">Угорська</string>
<string name="indonesian">Індонезійська</string>
<string name="luxembourgish">Люксембурзька</string>
<string name="marathi">Маратхі</string>
<string name="russian_sign_language">Російська жестова мова</string>
<string name="south_ndebele">Південна Ндебеле</string>
<string name="dutch">Нідерландська</string>
<string name="norwegian">Норвезька</string>
<string name="polish">Польська</string>
<string name="portuguese">Португальська</string>
<string name="spanish">іспанська</string>
<string name="slovenian">Словенська</string>
<string name="turkish">Турецька</string>
<string name="privacy_private">Приватний</string>
<string name="internal">Внутрішній</string>
</resources>

View File

@ -10,6 +10,8 @@
<string name="instance_error">Instance cannot be empty</string>
<string name="username_error">Username cannot be empty</string>
<string name="password_error">Password cannot be empty</string>
<string name="two_fa_error">Two-factor authentication token cannot be empty</string>
<string name="invalid_two_fa_token">Two-factor authentication token is not valid</string>
<string name="malformed_instance_error">The instance must have a valid URL</string>
<string name="account_exist">This account already exist</string>
<!-- messages -->
@ -48,6 +50,7 @@
<string name="username">Username</string>
<string name="password">Password</string>
<string name="instance">Instance</string>
<string name="two_fa">Two-factor Authentication Token</string>
<string name="delete_account_title">Delete this account</string>
<string name="comments_enabled">Enable video comments</string>
<string name="download_enabled">Enable download</string>

View File

@ -1,14 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.9.22'
ext.rtsp_version_name = '2.1.9'
ext.rtsp_version_code = 219
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
classpath 'com.android.tools.build:gradle:8.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5'
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -18,11 +20,11 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
tasks.register('clean', Delete) {
delete rootProject.buildDir
}

View File

@ -1,11 +1,13 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
android {
compileSdkVersion 30
compileSdkVersion 34
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
targetSdkVersion 34
}
buildTypes {
release {
@ -13,8 +15,15 @@ android {
consumerProguardFiles 'proguard-rules.pro'
}
}
namespace 'com.pedro.encoder'
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
api 'androidx.annotation:annotation:1.2.0'
api 'androidx.annotation:annotation:1.8.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

View File

@ -1 +1 @@
<manifest package="com.pedro.encoder" />
<manifest />

View File

@ -1,37 +1,145 @@
/*
* Copyright (C) 2021 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.pedro.encoder;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.pedro.encoder.utils.CodecUtil;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Created by pedro on 18/09/19.
*/
public abstract class BaseEncoder implements EncoderCallback {
private static final String TAG = "BaseEncoder";
private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
protected String TAG = "BaseEncoder";
private final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
private HandlerThread handlerThread;
protected BlockingQueue<Frame> queue = new ArrayBlockingQueue<>(80);
protected MediaCodec codec;
protected long presentTimeUs;
protected static long presentTimeUs;
protected volatile boolean running = false;
protected boolean isBufferMode = true;
protected CodecUtil.Force force = CodecUtil.Force.FIRST_COMPATIBLE_FOUND;
private MediaCodec.Callback callback;
private long oldTimeStamp = 0L;
protected boolean shouldReset = true;
public void restart() {
start(false);
initCodec();
}
public void start() {
if (presentTimeUs == 0) {
presentTimeUs = System.nanoTime() / 1000;
}
start(true);
initCodec();
}
private void initCodec() {
handlerThread = new HandlerThread(TAG);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
createAsyncCallback();
codec.setCallback(callback, handler);
codec.start();
} else {
codec.start();
handler.post(new Runnable() {
@Override
public void run() {
while (running) {
try {
getDataFromEncoder();
} catch (IllegalStateException e) {
Log.i(TAG, "Encoding error", e);
reloadCodec();
}
}
}
});
}
running = true;
}
public abstract void reset();
public abstract void start(boolean resetTs);
protected abstract void stopImp();
protected void fixTimeStamp(MediaCodec.BufferInfo info) {
if (oldTimeStamp > info.presentationTimeUs) {
info.presentationTimeUs = oldTimeStamp;
} else {
oldTimeStamp = info.presentationTimeUs;
}
}
private void reloadCodec() {
//Sometimes encoder crash, we will try recover it. Reset encoder a time if crash
if (shouldReset) {
Log.e(TAG, "Encoder crashed, trying to recover it");
reset();
}
}
public void stop() {
stop(true);
}
public void stop(boolean resetTs) {
if (resetTs) {
presentTimeUs = 0;
}
running = false;
stopImp();
if (handlerThread != null) {
if (handlerThread.getLooper() != null) {
if (handlerThread.getLooper().getThread() != null) {
handlerThread.getLooper().getThread().interrupt();
}
handlerThread.getLooper().quit();
}
handlerThread.quit();
if (codec != null) {
try {
codec.flush();
} catch (IllegalStateException ignored) { }
}
//wait for thread to die for 500ms.
try {
handlerThread.getLooper().getThread().join(500);
} catch (Exception ignored) { }
}
queue.clear();
queue = new ArrayBlockingQueue<>(80);
try {
codec.stop();
codec.release();
@ -39,18 +147,19 @@ public abstract class BaseEncoder implements EncoderCallback {
} catch (IllegalStateException | NullPointerException e) {
codec = null;
}
oldTimeStamp = 0L;
}
protected abstract MediaCodecInfo chooseEncoder(String mime);
protected void getDataFromEncoder(Frame frame) throws IllegalStateException {
protected void getDataFromEncoder() throws IllegalStateException {
if (isBufferMode) {
int inBufferIndex = codec.dequeueInputBuffer(0);
if (inBufferIndex >= 0) {
inputAvailable(codec, inBufferIndex, frame);
inputAvailable(codec, inBufferIndex);
}
}
for (; running; ) {
while (running) {
int outBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
if (outBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat mediaFormat = codec.getOutputFormat();
@ -65,16 +174,22 @@ public abstract class BaseEncoder implements EncoderCallback {
protected abstract Frame getInputFrame() throws InterruptedException;
protected abstract long calculatePts(Frame frame, long presentTimeUs);
private void processInput(@NonNull ByteBuffer byteBuffer, @NonNull MediaCodec mediaCodec,
int inBufferIndex, Frame frame) throws IllegalStateException {
int inBufferIndex) throws IllegalStateException {
try {
if (frame == null) frame = getInputFrame();
Frame frame = getInputFrame();
while (frame == null) frame = getInputFrame();
byteBuffer.clear();
byteBuffer.put(frame.getBuffer(), frame.getOffset(), frame.getSize());
long pts = System.nanoTime() / 1000 - presentTimeUs;
mediaCodec.queueInputBuffer(inBufferIndex, 0, frame.getSize(), pts, 0);
int size = Math.max(0, Math.min(frame.getSize(), byteBuffer.remaining()) - frame.getOffset());
byteBuffer.put(frame.getBuffer(), frame.getOffset(), size);
long pts = calculatePts(frame, presentTimeUs);
mediaCodec.queueInputBuffer(inBufferIndex, 0, size, pts, 0);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (NullPointerException | IndexOutOfBoundsException e) {
Log.i(TAG, "Encoding error", e);
}
}
@ -100,7 +215,7 @@ public abstract class BaseEncoder implements EncoderCallback {
}
@Override
public void inputAvailable(@NonNull MediaCodec mediaCodec, int inBufferIndex, Frame frame)
public void inputAvailable(@NonNull MediaCodec mediaCodec, int inBufferIndex)
throws IllegalStateException {
ByteBuffer byteBuffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -108,7 +223,7 @@ public abstract class BaseEncoder implements EncoderCallback {
} else {
byteBuffer = mediaCodec.getInputBuffers()[inBufferIndex];
}
processInput(byteBuffer, mediaCodec, inBufferIndex, frame);
processInput(byteBuffer, mediaCodec, inBufferIndex);
}
@Override
@ -122,4 +237,41 @@ public abstract class BaseEncoder implements EncoderCallback {
}
processOutput(byteBuffer, mediaCodec, outBufferIndex, bufferInfo);
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void createAsyncCallback() {
callback = new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int inBufferIndex) {
try {
inputAvailable(mediaCodec, inBufferIndex);
} catch (IllegalStateException e) {
Log.i(TAG, "Encoding error", e);
reloadCodec();
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int outBufferIndex,
@NonNull MediaCodec.BufferInfo bufferInfo) {
try {
outputAvailable(mediaCodec, outBufferIndex, bufferInfo);
} catch (IllegalStateException e) {
Log.i(TAG, "Encoding error", e);
reloadCodec();
}
}
@Override
public void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
Log.e(TAG, "Error", e);
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec mediaCodec,
@NonNull MediaFormat mediaFormat) {
formatChanged(mediaCodec, mediaFormat);
}
};
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder;
import android.media.MediaCodec;
@ -8,7 +24,7 @@ import androidx.annotation.NonNull;
* Created by pedro on 18/09/19.
*/
public interface EncoderCallback {
void inputAvailable(@NonNull MediaCodec mediaCodec, int inBufferIndex, Frame frame)
void inputAvailable(@NonNull MediaCodec mediaCodec, int inBufferIndex)
throws IllegalStateException;
void outputAvailable(@NonNull MediaCodec mediaCodec, int outBufferIndex,

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder;
import android.graphics.ImageFormat;

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2021 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.pedro.encoder;
public interface GetFrame {
Frame getInputFrame();
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.audio;
import android.media.MediaCodec;
@ -7,11 +23,10 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.pedro.encoder.BaseEncoder;
import com.pedro.encoder.Frame;
import com.pedro.encoder.GetFrame;
import com.pedro.encoder.input.audio.GetMicrophoneData;
import com.pedro.encoder.utils.CodecUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,15 +37,18 @@ import java.util.List;
public class AudioEncoder extends BaseEncoder implements GetMicrophoneData {
private static final String TAG = "AudioEncoder";
private GetAacData getAacData;
private final GetAacData getAacData;
private int bitRate = 64 * 1024; //in kbps
private int sampleRate = 32000; //in hz
private int maxInputSize = 0;
private boolean isStereo = true;
private GetFrame getFrame;
private long bytesRead = 0;
private boolean tsModeBuffer = false;
public AudioEncoder(GetAacData getAacData) {
this.getAacData = getAacData;
TAG = "AudioEncoder";
}
/**
@ -38,31 +56,19 @@ public class AudioEncoder extends BaseEncoder implements GetMicrophoneData {
*/
public boolean prepareAudioEncoder(int bitRate, int sampleRate, boolean isStereo,
int maxInputSize) {
this.bitRate = bitRate;
this.sampleRate = sampleRate;
this.maxInputSize = maxInputSize;
this.isStereo = isStereo;
isBufferMode = true;
try {
List<MediaCodecInfo> encoders = new ArrayList<>();
if (force == CodecUtil.Force.HARDWARE) {
encoders = CodecUtil.getAllHardwareEncoders(CodecUtil.AAC_MIME);
} else if (force == CodecUtil.Force.SOFTWARE) {
encoders = CodecUtil.getAllSoftwareEncoders(CodecUtil.AAC_MIME);
}
if (force == CodecUtil.Force.FIRST_COMPATIBLE_FOUND) {
MediaCodecInfo encoder = chooseEncoder(CodecUtil.AAC_MIME);
if (encoder != null) {
codec = MediaCodec.createByCodecName(encoder.getName());
} else {
Log.e(TAG, "Valid encoder not found");
return false;
}
MediaCodecInfo encoder = chooseEncoder(CodecUtil.AAC_MIME);
if (encoder != null) {
Log.i(TAG, "Encoder selected " + encoder.getName());
codec = MediaCodec.createByCodecName(encoder.getName());
} else {
if (encoders.isEmpty()) {
Log.e(TAG, "Valid encoder not found");
return false;
} else {
codec = MediaCodec.createByCodecName(encoders.get(0).getName());
}
Log.e(TAG, "Valid encoder not found");
return false;
}
int channelCount = (isStereo) ? 2 : 1;
@ -76,41 +82,65 @@ public class AudioEncoder extends BaseEncoder implements GetMicrophoneData {
running = false;
Log.i(TAG, "prepared");
return true;
} catch (IOException | IllegalStateException e) {
} catch (Exception e) {
Log.e(TAG, "Create AudioEncoder failed.", e);
this.stop();
return false;
}
}
public void setGetFrame(GetFrame getFrame) {
this.getFrame = getFrame;
}
/**
* Prepare encoder with default parameters
*/
public boolean prepareAudioEncoder() {
return prepareAudioEncoder(bitRate, sampleRate, isStereo, 0);
return prepareAudioEncoder(bitRate, sampleRate, isStereo, maxInputSize);
}
@Override
public void start(boolean resetTs) {
presentTimeUs = System.nanoTime() / 1000;
codec.start();
running = true;
shouldReset = resetTs;
Log.i(TAG, "started");
}
@Override
protected void stopImp() {
bytesRead = 0;
Log.i(TAG, "stopped");
}
@Override
public void reset() {
stop(false);
prepareAudioEncoder(bitRate, sampleRate, isStereo, maxInputSize);
restart();
}
@Override
protected Frame getInputFrame() throws InterruptedException {
return null;
return getFrame != null ? getFrame.getInputFrame() : queue.take();
}
@Override
protected long calculatePts(Frame frame, long presentTimeUs) {
long pts;
if (tsModeBuffer) {
int channels = isStereo ? 2 : 1;
pts = 1000000 * bytesRead / 2 / channels / sampleRate;
bytesRead += frame.getSize();
} else {
pts = System.nanoTime() / 1000 - presentTimeUs;
}
return pts;
}
@Override
protected void checkBuffer(@NonNull ByteBuffer byteBuffer,
@NonNull MediaCodec.BufferInfo bufferInfo) {
fixTimeStamp(bufferInfo);
}
@Override
@ -123,39 +153,45 @@ public class AudioEncoder extends BaseEncoder implements GetMicrophoneData {
* Set custom PCM data.
* Use it after prepareAudioEncoder(int sampleRate, int channel).
* Used too with microphone.
*
*/
@Override
public void inputPCMData(Frame frame) {
if (running) {
try {
getDataFromEncoder(frame);
} catch (IllegalStateException e) {
Log.i(TAG, "Encoding error", e);
}
} else {
if (running && !queue.offer(frame)) {
Log.i(TAG, "frame discarded");
}
}
@Override
protected MediaCodecInfo chooseEncoder(String mime) {
List<MediaCodecInfo> mediaCodecInfoList = CodecUtil.getAllEncoders(mime);
for (MediaCodecInfo mediaCodecInfo : mediaCodecInfoList) {
String name = mediaCodecInfo.getName().toLowerCase();
if (!name.contains("omx.google")) return mediaCodecInfo;
}
if (mediaCodecInfoList.size() > 0) {
return mediaCodecInfoList.get(0);
List<MediaCodecInfo> mediaCodecInfoList;
if (force == CodecUtil.Force.HARDWARE) {
mediaCodecInfoList = CodecUtil.getAllHardwareEncoders(CodecUtil.AAC_MIME);
} else if (force == CodecUtil.Force.SOFTWARE) {
mediaCodecInfoList = CodecUtil.getAllSoftwareEncoders(CodecUtil.AAC_MIME);
} else {
return null;
//Priority: hardware > software
mediaCodecInfoList = CodecUtil.getAllEncoders(CodecUtil.AAC_MIME, true);
}
Log.i(TAG, mediaCodecInfoList.size() + " encoders found");
if (mediaCodecInfoList.isEmpty()) return null;
else return mediaCodecInfoList.get(0);
}
public void setSampleRate(int sampleRate) {
this.sampleRate = sampleRate;
}
public boolean isTsModeBuffer() {
return tsModeBuffer;
}
public void setTsModeBuffer(boolean tsModeBuffer) {
if (!isRunning()) {
this.tsModeBuffer = tsModeBuffer;
}
}
@Override
public void formatChanged(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat) {
getAacData.onAudioFormat(mediaFormat);

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.audio;
import android.media.MediaCodec;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
import android.media.audiofx.AcousticEchoCanceler;
@ -13,7 +29,7 @@ public class AudioPostProcessEffect {
private final String TAG = "AudioPostProcessEffect";
private int microphoneId;
private final int microphoneId;
private AcousticEchoCanceler acousticEchoCanceler = null;
private AutomaticGainControl automaticGainControl = null;
private NoiseSuppressor noiseSuppressor = null;
@ -25,11 +41,13 @@ public class AudioPostProcessEffect {
public void enableAutoGainControl() {
if (AutomaticGainControl.isAvailable() && automaticGainControl == null) {
automaticGainControl = AutomaticGainControl.create(microphoneId);
automaticGainControl.setEnabled(true);
Log.i(TAG, "AutoGainControl enabled");
} else {
Log.e(TAG, "This device don't support AutoGainControl");
if (automaticGainControl != null) {
automaticGainControl.setEnabled(true);
Log.i(TAG, "AutoGainControl enabled");
return;
}
}
Log.e(TAG, "This device doesn't implement AutoGainControl");
}
public void releaseAutoGainControl() {
@ -43,11 +61,13 @@ public class AudioPostProcessEffect {
public void enableEchoCanceler() {
if (AcousticEchoCanceler.isAvailable() && acousticEchoCanceler == null) {
acousticEchoCanceler = AcousticEchoCanceler.create(microphoneId);
acousticEchoCanceler.setEnabled(true);
Log.i(TAG, "EchoCanceler enabled");
} else {
Log.e(TAG, "This device don't support EchoCanceler");
if (acousticEchoCanceler != null) {
acousticEchoCanceler.setEnabled(true);
Log.i(TAG, "EchoCanceler enabled");
return;
}
}
Log.e(TAG, "This device doesn't implement EchoCanceler");
}
public void releaseEchoCanceler() {
@ -61,11 +81,13 @@ public class AudioPostProcessEffect {
public void enableNoiseSuppressor() {
if (NoiseSuppressor.isAvailable() && noiseSuppressor == null) {
noiseSuppressor = NoiseSuppressor.create(microphoneId);
noiseSuppressor.setEnabled(true);
Log.i(TAG, "NoiseSuppressor enabled");
} else {
Log.e(TAG, "This device don't support NoiseSuppressor");
if (noiseSuppressor != null) {
noiseSuppressor.setEnabled(true);
Log.i(TAG, "NoiseSuppressor enabled");
return;
}
}
Log.e(TAG, "This device doesn't implement NoiseSuppressor");
}
public void releaseNoiseSuppressor() {
@ -75,4 +97,10 @@ public class AudioPostProcessEffect {
noiseSuppressor = null;
}
}
public void release() {
releaseAutoGainControl();
releaseEchoCanceler();
releaseNoiseSuppressor();
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
public abstract class CustomAudioEffect {

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
import com.pedro.encoder.Frame;

View File

@ -1,5 +1,22 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
import android.annotation.SuppressLint;
import android.media.AudioFormat;
import android.media.AudioPlaybackCaptureConfiguration;
import android.media.AudioRecord;
@ -16,28 +33,29 @@ import java.nio.ByteBuffer;
* Created by pedro on 19/01/17.
*/
@SuppressLint("MissingPermission")
public class MicrophoneManager {
private final String TAG = "MicrophoneManager";
private static final int BUFFER_SIZE = 4096;
private int BUFFER_SIZE = 0;
protected AudioRecord audioRecord;
private GetMicrophoneData getMicrophoneData;
private ByteBuffer pcmBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
private byte[] pcmBufferMuted = new byte[BUFFER_SIZE];
private final GetMicrophoneData getMicrophoneData;
protected byte[] pcmBuffer = new byte[BUFFER_SIZE];
protected byte[] pcmBufferMuted = new byte[BUFFER_SIZE];
protected boolean running = false;
private boolean created = false;
//default parameters for microphone
private int sampleRate = 32000; //hz
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int channel = AudioFormat.CHANNEL_IN_STEREO;
private boolean muted = false;
protected boolean muted = false;
private AudioPostProcessEffect audioPostProcessEffect;
HandlerThread handlerThread;
private CustomAudioEffect customAudioEffect = new NoAudioEffect();
protected HandlerThread handlerThread;
protected CustomAudioEffect customAudioEffect = new NoAudioEffect();
public MicrophoneManager(GetMicrophoneData getMicrophoneData) {
this.getMicrophoneData = getMicrophoneData;
getPcmBufferSize();
}
public void setCustomAudioEffect(CustomAudioEffect customAudioEffect) {
@ -55,61 +73,86 @@ public class MicrophoneManager {
/**
* Create audio record with params and default audio source
*/
public void createMicrophone(int sampleRate, boolean isStereo, boolean echoCanceler,
public boolean createMicrophone(int sampleRate, boolean isStereo, boolean echoCanceler,
boolean noiseSuppressor) {
createMicrophone(MediaRecorder.AudioSource.DEFAULT, sampleRate, isStereo, echoCanceler, noiseSuppressor);
return createMicrophone(MediaRecorder.AudioSource.DEFAULT, sampleRate, isStereo, echoCanceler,
noiseSuppressor);
}
/**
* Create audio record with params and selected audio source
* @param audioSource - the recording source. See {@link MediaRecorder.AudioSource} for the recording source definitions.
*
* @param audioSource - the recording source. See {@link MediaRecorder.AudioSource} for the
* recording source definitions.
*/
public void createMicrophone(int audioSource, int sampleRate, boolean isStereo, boolean echoCanceler,
boolean noiseSuppressor) {
this.sampleRate = sampleRate;
if (!isStereo) channel = AudioFormat.CHANNEL_IN_MONO;
audioRecord =
new AudioRecord(audioSource, sampleRate, channel, audioFormat,
getPcmBufferSize());
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
if (echoCanceler) audioPostProcessEffect.enableEchoCanceler();
if (noiseSuppressor) audioPostProcessEffect.enableNoiseSuppressor();
String chl = (isStereo) ? "Stereo" : "Mono";
Log.i(TAG, "Microphone created, " + sampleRate + "hz, " + chl);
created = true;
public boolean createMicrophone(int audioSource, int sampleRate, boolean isStereo,
boolean echoCanceler, boolean noiseSuppressor) {
try {
this.sampleRate = sampleRate;
channel = isStereo ? AudioFormat.CHANNEL_IN_STEREO : AudioFormat.CHANNEL_IN_MONO;
audioRecord = new AudioRecord(audioSource, sampleRate, channel, audioFormat, getMaxInputSize() * 5);
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
if (echoCanceler) audioPostProcessEffect.enableEchoCanceler();
if (noiseSuppressor) audioPostProcessEffect.enableNoiseSuppressor();
String chl = (isStereo) ? "Stereo" : "Mono";
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
throw new IllegalArgumentException("Some parameters specified is not valid");
}
Log.i(TAG, "Microphone created, " + sampleRate + "hz, " + chl);
created = true;
} catch (IllegalArgumentException e) {
Log.e(TAG, "create microphone error", e);
}
return created;
}
/**
* Create audio record with params and AudioPlaybackCaptureConfig used for capturing internal audio
* Notice that you should granted {@link android.Manifest.permission#RECORD_AUDIO} before calling this!
*
* @param config - AudioPlaybackCaptureConfiguration received from {@link android.media.projection.MediaProjection}
* Create audio record with params and AudioPlaybackCaptureConfig used for capturing internal
* audio
* Notice that you should granted {@link android.Manifest.permission#RECORD_AUDIO} before calling
* this!
*
* @param config - AudioPlaybackCaptureConfiguration received from {@link
* android.media.projection.MediaProjection}
* @see AudioPlaybackCaptureConfiguration.Builder#Builder(MediaProjection)
* @see "https://developer.android.com/guide/topics/media/playback-capture"
* @see "https://medium.com/@debuggingisfun/android-10-audio-capture-77dd8e9070f9"
*/
public void createInternalMicrophone(AudioPlaybackCaptureConfiguration config, int sampleRate, boolean isStereo) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
this.sampleRate = sampleRate;
if (!isStereo) channel = AudioFormat.CHANNEL_IN_MONO;
audioRecord = new AudioRecord.Builder()
.setAudioPlaybackCaptureConfig(config)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channel)
.build())
.setBufferSizeInBytes(getPcmBufferSize())
.build();
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
String chl = (isStereo) ? "Stereo" : "Mono";
Log.i(TAG, "Internal microphone created, " + sampleRate + "hz, " + chl);
created = true;
} else createMicrophone(sampleRate, isStereo, false, false);
public boolean createInternalMicrophone(AudioPlaybackCaptureConfiguration config, int sampleRate,
boolean isStereo, boolean echoCanceler, boolean noiseSuppressor) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
this.sampleRate = sampleRate;
channel = isStereo ? AudioFormat.CHANNEL_IN_STEREO : AudioFormat.CHANNEL_IN_MONO;
audioRecord = new AudioRecord.Builder().setAudioPlaybackCaptureConfig(config)
.setAudioFormat(new AudioFormat.Builder().setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channel)
.build())
.setBufferSizeInBytes(getMaxInputSize() * 5)
.build();
audioPostProcessEffect = new AudioPostProcessEffect(audioRecord.getAudioSessionId());
if (echoCanceler) audioPostProcessEffect.enableEchoCanceler();
if (noiseSuppressor) audioPostProcessEffect.enableNoiseSuppressor();
String chl = (isStereo) ? "Stereo" : "Mono";
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
throw new IllegalArgumentException("Some parameters specified is not valid");
}
Log.i(TAG, "Internal microphone created, " + sampleRate + "hz, " + chl);
created = true;
} else {
return createMicrophone(sampleRate, isStereo, echoCanceler, noiseSuppressor);
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "create microphone error", e);
}
return created;
}
public boolean createInternalMicrophone(AudioPlaybackCaptureConfiguration config, int sampleRate,
boolean isStereo) {
return createInternalMicrophone(config, sampleRate, isStereo, false, false);
}
/**
* Start record and get data
@ -126,8 +169,6 @@ public class MicrophoneManager {
Frame frame = read();
if (frame != null) {
getMicrophoneData.inputPCMData(frame);
} else {
running = false;
}
}
}
@ -160,14 +201,10 @@ public class MicrophoneManager {
/**
* @return Object with size and PCM buffer data
*/
private Frame read() {
pcmBuffer.rewind();
int size = audioRecord.read(pcmBuffer, pcmBuffer.remaining());
if (size <= 0) {
return null;
}
return new Frame(muted ? pcmBufferMuted : customAudioEffect.process(pcmBuffer.array()),
muted ? 0 : pcmBuffer.arrayOffset(), size);
protected Frame read() {
int size = audioRecord.read(pcmBuffer, 0, pcmBuffer.length);
if (size < 0) return null;
return new Frame(muted ? pcmBufferMuted : customAudioEffect.process(pcmBuffer), 0, size);
}
/**
@ -190,8 +227,7 @@ public class MicrophoneManager {
audioRecord = null;
}
if (audioPostProcessEffect != null) {
audioPostProcessEffect.releaseEchoCanceler();
audioPostProcessEffect.releaseNoiseSuppressor();
audioPostProcessEffect.release();
}
Log.i(TAG, "Microphone stopped");
}
@ -199,10 +235,10 @@ public class MicrophoneManager {
/**
* Get PCM buffer size
*/
private int getPcmBufferSize() {
int pcmBufSize =
AudioRecord.getMinBufferSize(sampleRate, channel, AudioFormat.ENCODING_PCM_16BIT);
return pcmBufSize * 5;
private void getPcmBufferSize() {
BUFFER_SIZE = AudioRecord.getMinBufferSize(sampleRate, channel, audioFormat);
pcmBuffer = new byte[BUFFER_SIZE];
pcmBufferMuted = new byte[BUFFER_SIZE];
}
public int getMaxInputSize() {

View File

@ -1,15 +1,33 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
import android.os.HandlerThread;
import android.util.Log;
import com.pedro.encoder.Frame;
import com.pedro.encoder.GetFrame;
import java.nio.ByteBuffer;
/**
* Similar to MicrophoneManager but samples are not read automatically.
* The owner must manually call read(...) as often as samples are needed.
*/
public class MicrophoneManagerManual extends MicrophoneManager {
public class MicrophoneManagerManual extends MicrophoneManager implements GetFrame {
private final String TAG = "MicMM";
@ -54,4 +72,13 @@ public class MicrophoneManagerManual extends MicrophoneManager {
handlerThread = new HandlerThread("nothing");
super.stop();
}
public GetFrame getGetFrame() {
return this;
}
@Override
public Frame getInputFrame() {
return read();
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
public enum MicrophoneMode {
SYNC, ASYNC, BUFFER
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.audio;
public class NoAudioEffect extends CustomAudioEffect {

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
import android.media.MediaCodec;
@ -6,160 +22,110 @@ import android.media.MediaFormat;
import android.util.Log;
import com.pedro.encoder.Frame;
import com.pedro.encoder.input.audio.GetMicrophoneData;
import com.pedro.encoder.utils.CodecUtil;
import com.pedro.encoder.utils.PCMUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Created by pedro on 20/06/17.
*/
public class AudioDecoder {
private final String TAG = "AudioDecoder";
public class AudioDecoder extends BaseDecoder {
private AudioDecoderInterface audioDecoderInterface;
private LoopFileInterface loopFileInterface;
private MediaExtractor audioExtractor;
private MediaCodec audioDecoder;
private MediaCodec.BufferInfo audioInfo = new MediaCodec.BufferInfo();
private boolean decoding;
private Thread thread;
private GetMicrophoneData getMicrophoneData;
private MediaFormat audioFormat;
private String mime = "";
private int sampleRate;
private boolean isStereo;
private int channels = 1;
private int size = 2048;
private byte[] pcmBuffer = new byte[size];
private byte[] pcmBufferMuted = new byte[11];
private static boolean loopMode = false;
private boolean muted = false;
private long duration;
private volatile long seekTime = 0;
private volatile long startMs = 0;
public AudioDecoder(GetMicrophoneData getMicrophoneData,
AudioDecoderInterface audioDecoderInterface, LoopFileInterface loopFileInterface) {
super(loopFileInterface);
TAG = "AudioDecoder";
this.getMicrophoneData = getMicrophoneData;
this.audioDecoderInterface = audioDecoderInterface;
this.loopFileInterface = loopFileInterface;
}
public boolean initExtractor(String filePath) throws IOException {
decoding = false;
audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(filePath);
@Override
protected boolean extract(MediaExtractor audioExtractor) {
size = 2048;
running = false;
for (int i = 0; i < audioExtractor.getTrackCount() && !mime.startsWith("audio/"); i++) {
audioFormat = audioExtractor.getTrackFormat(i);
mime = audioFormat.getString(MediaFormat.KEY_MIME);
mediaFormat = audioExtractor.getTrackFormat(i);
mime = mediaFormat.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio/")) {
audioExtractor.selectTrack(i);
} else {
audioFormat = null;
mediaFormat = null;
}
}
if (audioFormat != null) {
channels = audioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
if (mediaFormat != null) {
channels = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
isStereo = channels >= 2;
sampleRate = audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
duration = audioFormat.getLong(MediaFormat.KEY_DURATION);
if (channels >= 2) {
pcmBuffer = new byte[2048 * channels];
}
sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
fixBuffer();
return true;
//audio decoder not supported
} else {
mime = "";
audioFormat = null;
mediaFormat = null;
return false;
}
}
private void fixBuffer() {
if (channels >= 2) {
size *= channels;
}
pcmBuffer = new byte[size];
}
public boolean prepareAudio() {
try {
audioDecoder = MediaCodec.createDecoderByType(mime);
audioDecoder.configure(audioFormat, null, null, 0);
return true;
} catch (IOException e) {
Log.e(TAG, "Prepare decoder error:", e);
return false;
}
return prepare(null);
}
public void start() {
decoding = true;
audioDecoder.start();
thread = new Thread(new Runnable() {
@Override
public void run() {
try {
decodeAudio();
} catch (IllegalStateException e) {
Log.i(TAG, "Decoding error", e);
}
}
});
thread.start();
public void reset() {
resetCodec(null);
}
public void stop() {
decoding = false;
seekTime = 0;
if (thread != null) {
thread.interrupt();
try {
thread.join(100);
} catch (InterruptedException e) {
thread.interrupt();
}
thread = null;
}
try {
audioDecoder.stop();
audioDecoder.release();
audioDecoder = null;
} catch (IllegalStateException | NullPointerException e) {
audioDecoder = null;
}
if (audioExtractor != null) {
audioExtractor.release();
audioExtractor = null;
}
}
private void decodeAudio() throws IllegalStateException {
ByteBuffer[] inputBuffers = audioDecoder.getInputBuffers();
ByteBuffer[] outputBuffers = audioDecoder.getOutputBuffers();
@Override
protected void decode() {
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
startMs = System.currentTimeMillis();
while (decoding) {
int inIndex = audioDecoder.dequeueInputBuffer(10000);
while (running) {
int inIndex = codec.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = audioExtractor.readSampleData(buffer, 0);
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
audioDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
audioDecoder.queueInputBuffer(inIndex, 0, sampleSize, audioExtractor.getSampleTime(), 0);
audioExtractor.advance();
codec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
}
int outIndex = audioDecoder.dequeueOutputBuffer(audioInfo, 10000);
int outIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
outputBuffers = audioDecoder.getOutputBuffers();
outputBuffers = codec.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
default:
//needed for fix decode speed
while (audioExtractor.getSampleTime() / 1000
> System.currentTimeMillis() - startMs + seekTime) {
long extractorTs = extractor.getSampleTime() / 1000;
long currentTs = System.currentTimeMillis() - startMs + seekTime;
if (extractorTs > currentTs) {
try {
Thread.sleep(10);
long sleepTime = extractorTs - currentTs;
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
if (thread != null) thread.interrupt();
Thread.currentThread().interrupt();
return;
}
}
@ -167,13 +133,13 @@ public class AudioDecoder {
//This buffer is PCM data
if (muted) {
outBuffer.get(pcmBufferMuted, 0,
outBuffer.remaining() <= pcmBufferMuted.length ? outBuffer.remaining()
: pcmBufferMuted.length);
Math.min(outBuffer.remaining(), pcmBufferMuted.length));
getMicrophoneData.inputPCMData(new Frame(pcmBufferMuted, 0, pcmBufferMuted.length));
} else {
outBuffer.get(pcmBuffer, 0,
outBuffer.remaining() <= pcmBuffer.length ? outBuffer.remaining()
: pcmBuffer.length);
if (pcmBuffer.length < outBuffer.remaining()) {
pcmBuffer = new byte[outBuffer.remaining()];
}
outBuffer.get(pcmBuffer, 0, Math.min(outBuffer.remaining(), pcmBuffer.length));
if (channels > 2) { //downgrade to stereo
byte[] bufferStereo = PCMUtil.pcmToStereo(pcmBuffer, channels);
getMicrophoneData.inputPCMData(new Frame(bufferStereo, 0, bufferStereo.length));
@ -181,42 +147,54 @@ public class AudioDecoder {
getMicrophoneData.inputPCMData(new Frame(pcmBuffer, 0, pcmBuffer.length));
}
}
audioDecoder.releaseOutputBuffer(outIndex, false);
codec.releaseOutputBuffer(outIndex, false);
break;
}
}
// All decoded frames have been rendered, we can stop playing now
if ((audioInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
seekTime = 0;
Log.i(TAG, "end of file out");
if (loopMode) {
loopFileInterface.onReset(false);
} else {
audioDecoderInterface.onAudioDecoderFinished();
}
// All decoded frames have been rendered, we can stop playing now
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
seekTime = 0;
Log.i(TAG, "end of file out");
running = false;
if (loopMode) {
loopFileInterface.onReset(false);
} else {
audioDecoderInterface.onAudioDecoderFinished();
}
}
}
}
public double getTime() {
if (decoding) {
return audioExtractor.getSampleTime() / 10E5;
/**
* This method should be called after prepare.
* Get max output size to set max input size in encoder.
*/
public int getOutsize() {
if (!(mime.equals(CodecUtil.AAC_MIME) || mime.equals(CodecUtil.OPUS_MIME) || mime.equals(
CodecUtil.VORBIS_MIME))) {
Log.i(TAG, "fixing input size");
try {
if (running) {
return codec.getOutputBuffers()[0].remaining();
} else {
if (codec != null) {
codec.start();
int outSize = codec.getOutputBuffers()[0].remaining();
stopDecoder();
if (prepare(null)) return outSize;
}
return 0;
}
} catch (Exception e) {
return 0;
}
} else {
Log.i(TAG, "default input size");
return 0;
}
}
public void moveTo(double time) {
audioExtractor.seekTo((long) (time * 10E5), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
seekTime = audioExtractor.getSampleTime() / 1000;
startMs = System.currentTimeMillis();
}
public void setLoopMode(boolean loopMode) {
this.loopMode = loopMode;
}
public void mute() {
muted = true;
}
@ -236,8 +214,4 @@ public class AudioDecoder {
public boolean isStereo() {
return isStereo;
}
public double getDuration() {
return duration / 10E5;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
/**

View File

@ -0,0 +1,216 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import androidx.annotation.RequiresApi;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Map;
public abstract class BaseDecoder {
protected static String TAG = "BaseDecoder";
protected LoopFileInterface loopFileInterface;
protected MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
protected MediaExtractor extractor;
protected MediaCodec codec;
protected volatile boolean running = false;
protected MediaFormat mediaFormat;
private HandlerThread handlerThread;
protected String mime = "";
protected boolean loopMode = false;
protected volatile long seekTime = 0;
protected volatile long startMs = 0;
protected long duration;
public BaseDecoder(LoopFileInterface loopFileInterface) {
this.loopFileInterface = loopFileInterface;
}
public boolean initExtractor(String filePath) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(filePath);
return extract(extractor);
}
public boolean initExtractor(FileDescriptor fileDescriptor) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(fileDescriptor);
return extract(extractor);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean initExtractor(AssetFileDescriptor assetFileDescriptor) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(assetFileDescriptor);
return extract(extractor);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public boolean initExtractor(MediaDataSource mediaDataSource) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(mediaDataSource);
return extract(extractor);
}
public boolean initExtractor(String filePath, Map<String, String> headers) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(filePath, headers);
return extract(extractor);
}
public boolean initExtractor(FileDescriptor fileDescriptor, long offset, long length)
throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(fileDescriptor, offset, length);
return extract(extractor);
}
public boolean initExtractor(Context context, Uri uri, Map<String, String> headers)
throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(context, uri, headers);
return extract(extractor);
}
public void start() {
Log.i(TAG, "start decoder");
running = true;
handlerThread = new HandlerThread(TAG);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
codec.start();
handler.post(new Runnable() {
@Override
public void run() {
try {
decode();
} catch (IllegalStateException e) {
Log.i(TAG, "Decoding error", e);
} catch (NullPointerException e) {
Log.i(TAG, "Decoder maybe was stopped");
Log.i(TAG, "Decoding error", e);
}
}
});
}
public void stop() {
Log.i(TAG, "stop decoder");
running = false;
stopDecoder();
if (extractor != null) {
extractor.release();
extractor = null;
mime = "";
}
}
protected boolean prepare(Surface surface) {
try {
codec = MediaCodec.createDecoderByType(mime);
codec.configure(mediaFormat, surface, null, 0);
return true;
} catch (IOException e) {
Log.e(TAG, "Prepare decoder error:", e);
return false;
}
}
protected void resetCodec(Surface surface) {
boolean wasRunning = running;
stopDecoder();
if (extractor != null) seekTime = extractor.getSampleTime() / 1000;
prepare(surface);
if (wasRunning) {
start();
}
}
protected void stopDecoder() {
running = false;
seekTime = 0;
if (handlerThread != null) {
if (handlerThread.getLooper() != null) {
if (handlerThread.getLooper().getThread() != null) {
handlerThread.getLooper().getThread().interrupt();
}
handlerThread.getLooper().quit();
}
handlerThread.quit();
if (codec != null) {
try {
codec.flush();
} catch (IllegalStateException ignored) { }
}
//wait for thread to die for 500ms.
try {
handlerThread.getLooper().getThread().join(500);
} catch (Exception ignored) { }
handlerThread = null;
}
try {
codec.stop();
codec.release();
codec = null;
} catch (IllegalStateException | NullPointerException e) {
codec = null;
}
}
public void moveTo(double time) {
extractor.seekTo((long) (time * 10E5), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
seekTime = extractor.getSampleTime() / 1000;
startMs = System.currentTimeMillis();
}
public void setLoopMode(boolean loopMode) {
this.loopMode = loopMode;
}
public boolean isLoopMode() {
return loopMode;
}
public double getDuration() {
return duration / 10E5;
}
public double getTime() {
if (running) {
return extractor.getSampleTime() / 10E5;
} else {
return 0;
}
}
protected abstract boolean extract(MediaExtractor extractor);
protected abstract void decode();
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
/**

View File

@ -1,178 +1,130 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Created by pedro on 20/06/17.
*/
public class VideoDecoder {
private final String TAG = "VideoDecoder";
public class VideoDecoder extends BaseDecoder {
private VideoDecoderInterface videoDecoderInterface;
private LoopFileInterface loopFileInterface;
private MediaExtractor videoExtractor;
private MediaCodec videoDecoder;
private MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();
private boolean decoding;
private Thread thread;
private MediaFormat videoFormat;
private String mime = "";
private int width;
private int height;
private long duration;
private static boolean loopMode = false;
private volatile long seekTime = 0;
private volatile long startMs = 0;
public VideoDecoder(VideoDecoderInterface videoDecoderInterface,
LoopFileInterface loopFileInterface) {
super(loopFileInterface);
TAG = "VideoDecoder";
this.videoDecoderInterface = videoDecoderInterface;
this.loopFileInterface = loopFileInterface;
}
public boolean initExtractor(String filePath) throws IOException {
decoding = false;
videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(filePath);
@Override
protected boolean extract(MediaExtractor videoExtractor) {
running = false;
for (int i = 0; i < videoExtractor.getTrackCount() && !mime.startsWith("video/"); i++) {
videoFormat = videoExtractor.getTrackFormat(i);
mime = videoFormat.getString(MediaFormat.KEY_MIME);
mediaFormat = videoExtractor.getTrackFormat(i);
mime = mediaFormat.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
videoExtractor.selectTrack(i);
} else {
videoFormat = null;
mediaFormat = null;
}
}
if (videoFormat != null) {
width = videoFormat.getInteger(MediaFormat.KEY_WIDTH);
height = videoFormat.getInteger(MediaFormat.KEY_HEIGHT);
duration = videoFormat.getLong(MediaFormat.KEY_DURATION);
if (mediaFormat != null) {
width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
return true;
//video decoder not supported
} else {
mime = "";
videoFormat = null;
mediaFormat = null;
return false;
}
}
public boolean prepareVideo(Surface surface) {
try {
videoDecoder = MediaCodec.createDecoderByType(mime);
videoDecoder.configure(videoFormat, surface, null, 0);
return true;
} catch (IOException e) {
Log.e(TAG, "Prepare decoder error:", e);
return false;
}
return prepare(surface);
}
public void start() {
decoding = true;
videoDecoder.start();
thread = new Thread(new Runnable() {
@Override
public void run() {
try {
decodeVideo();
} catch (IllegalStateException e) {
Log.i(TAG, "Decoding error", e);
}
}
});
thread.start();
public void reset(Surface surface) {
resetCodec(surface);
}
public void stop() {
decoding = false;
seekTime = 0;
if (thread != null) {
thread.interrupt();
try {
thread.join(100);
} catch (InterruptedException e) {
thread.interrupt();
}
thread = null;
}
try {
videoDecoder.stop();
videoDecoder.release();
videoDecoder = null;
} catch (IllegalStateException | NullPointerException e) {
videoDecoder = null;
}
if (videoExtractor != null) {
videoExtractor.release();
videoExtractor = null;
}
}
private void decodeVideo() throws IllegalStateException {
ByteBuffer[] inputBuffers = videoDecoder.getInputBuffers();
@Override
protected void decode() {
ByteBuffer[] inputBuffers = codec.getInputBuffers();
startMs = System.currentTimeMillis();
while (decoding) {
int inIndex = videoDecoder.dequeueInputBuffer(10000);
while (running) {
int inIndex = codec.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = videoExtractor.readSampleData(buffer, 0);
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
videoDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
videoDecoder.queueInputBuffer(inIndex, 0, sampleSize, videoExtractor.getSampleTime(), 0);
videoExtractor.advance();
codec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
}
}
int outIndex = videoDecoder.dequeueOutputBuffer(videoInfo, 10000);
int outIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
if (outIndex >= 0) {
while (videoExtractor.getSampleTime() / 1000
> System.currentTimeMillis() - startMs + seekTime) {
long extractorTs = extractor.getSampleTime() / 1000;
long currentTs = System.currentTimeMillis() - startMs + seekTime;
if (extractorTs > currentTs) {
try {
Thread.sleep(10);
long sleepTime = extractorTs - currentTs;
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
if (thread != null) thread.interrupt();
Thread.currentThread().interrupt();
return;
}
}
videoDecoder.releaseOutputBuffer(outIndex, videoInfo.size != 0);
codec.releaseOutputBuffer(outIndex, bufferInfo.size != 0);
}
if ((videoInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
seekTime = 0;
Log.i(TAG, "end of file out");
if (loopMode) {
loopFileInterface.onReset(true);
} else {
videoDecoderInterface.onVideoDecoderFinished();
}
}
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
seekTime = 0;
Log.i(TAG, "end of file out");
running = false;
if (loopMode) {
loopFileInterface.onReset(true);
} else {
videoDecoderInterface.onVideoDecoderFinished();
}
}
}
public double getTime() {
if (decoding) {
return videoExtractor.getSampleTime() / 10E5;
public void changeOutputSurface(Surface surface) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
codec.setOutputSurface(surface);
} else {
return 0;
reset(surface);
}
}
public void moveTo(double time) {
videoExtractor.seekTo((long) (time * 10E5), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
seekTime = videoExtractor.getSampleTime() / 1000;
startMs = System.currentTimeMillis();
}
public void setLoopMode(boolean loopMode) {
this.loopMode = loopMode;
}
public int getWidth() {
return width;
}
@ -180,8 +132,4 @@ public class VideoDecoder {
public int getHeight() {
return height;
}
public double getDuration() {
return duration / 10E5;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.decoder;
/**

View File

@ -0,0 +1,157 @@
package com.pedro.encoder.input.gl;
import android.graphics.PointF;
import android.view.View;
import com.pedro.encoder.utils.gl.TranslateTo;
/**
* Created by pedro on 3/2/22.
*/
public class AndroidViewSprite {
private PointF scale;
private PointF position;
private PointF rotationAxis;
private int rotation;
private View view;
public AndroidViewSprite() {
reset();
}
public void setView(View view) {
this.view = view;
rotationAxis = new PointF(view.getMeasuredWidth() / 2f, view.getMeasuredHeight() / 2f);
}
/**
* @param deltaX Position x in percent
* @param deltaY Position x in percent
*/
public void translate(float deltaX, float deltaY) {
position.x = deltaX;
position.y = deltaY;
}
/**
* @param translation Predefined position
*/
public void translate(TranslateTo translation) {
switch (translation) {
case CENTER:
this.position.x = 50f - scale.x / 2f;
this.position.y = 50f - scale.x / 2f;
break;
case BOTTOM:
this.position.x = 50f - scale.x / 2f;
this.position.y = 100f - scale.y;
break;
case TOP:
this.position.x = 50f - scale.x / 2f;
this.position.y = 0f;
break;
case LEFT:
this.position.x = 0f;
this.position.y = 50f - scale.y / 2f;
break;
case RIGHT:
this.position.x = 100f - scale.x;
this.position.y = 50f - scale.y / 2f;
break;
case TOP_LEFT:
this.position.x = 0f;
this.position.y = 0f;
break;
case TOP_RIGHT:
this.position.x = 100f - scale.x;
this.position.y = 0f;
break;
case BOTTOM_LEFT:
this.position.x = 0f;
this.position.y = 100f - scale.y;
break;
case BOTTOM_RIGHT:
this.position.x = 100f - scale.x;
this.position.y = 100f - scale.y;
break;
default:
break;
}
}
/**
* @param deltaX Scale x in percent
* @param deltaY Scale y in percent
*/
public void scale(float deltaX, float deltaY) {
//keep old position
position.x /= deltaX / scale.x;
position.y /= deltaY / scale.y;
//set new scale.
scale = new PointF(deltaX, deltaY);
}
/**
* @return Scale in percent
*/
public PointF getScale() {
return scale;
}
/**
* @return Position in percent
*/
public PointF getTranslation() {
return position;
}
public int getRotation() {
return rotation;
}
public PointF getRotationAxis() {
return rotationAxis;
}
public void setRotation(int rotation) {
if (rotation < 0) {
this.rotation = 0;
} else if (rotation > 360) {
this.rotation = 360;
} else {
this.rotation = rotation;
}
}
public void reset() {
scale = new PointF(0f, 0f);
position = new PointF(0f, 0f);
}
/**
* Traduce position in percent to work with canvas.
*/
public PointF getCanvasPosition(float previewX, float previewY) {
return new PointF(previewX * position.x / 100f, previewY * position.y / 100f);
}
/**
* Traduce scale in percent to work with canvas.
*/
public PointF getCanvasScale(float previewX, float previewY) {
float scaleFactorX = 100f * (float) view.getWidth() / previewX;
float scaleFactorY = 100f * (float) view.getHeight() / previewY;
return new PointF(scale.x / scaleFactorX, scale.y / scaleFactorY);
}
/**
* Calculate default scale if none is indicated after load the filter.
*/
public void calculateDefaultScale(float previewX, float previewY) {
if (scale.x == 0f && scale.y == 0f) {
scale.x = 100f * (float) view.getWidth() / previewX;
scale.y = 100f * (float) view.getHeight() / previewY;
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl;
/**
* Created by pedro on 21/9/21.
*/
public enum FilterAction {
SET, SET_INDEX, ADD, ADD_INDEX, CLEAR, REMOVE, REMOVE_INDEX
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl;
import android.graphics.PointF;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl;
import android.graphics.PointF;
@ -5,6 +21,8 @@ import android.os.Build;
import androidx.annotation.RequiresApi;
import android.view.MotionEvent;
import android.view.View;
import com.pedro.encoder.input.gl.render.filters.AndroidViewFilterRender;
import com.pedro.encoder.input.gl.render.filters.BaseFilterRender;
import com.pedro.encoder.input.gl.render.filters.object.BaseObjectFilterRender;
import com.pedro.encoder.input.video.CameraHelper;
@ -16,6 +34,7 @@ import com.pedro.encoder.input.video.CameraHelper;
public class SpriteGestureController {
private BaseObjectFilterRender baseObjectFilterRender;
private AndroidViewFilterRender androidViewFilterRender;
private float lastDistance;
private boolean preventMoveOutside = true;
@ -26,12 +45,27 @@ public class SpriteGestureController {
this.baseObjectFilterRender = sprite;
}
public BaseObjectFilterRender getBaseObjectFilterRender() {
return baseObjectFilterRender;
public SpriteGestureController(AndroidViewFilterRender sprite) {
this.androidViewFilterRender = sprite;
}
public BaseFilterRender getFilterRender() {
return androidViewFilterRender == null ? baseObjectFilterRender : androidViewFilterRender;
}
public void setBaseObjectFilterRender(BaseObjectFilterRender baseObjectFilterRender) {
this.baseObjectFilterRender = baseObjectFilterRender;
this.androidViewFilterRender = null;
}
public void setBaseObjectFilterRender(AndroidViewFilterRender androidViewFilterRender) {
this.androidViewFilterRender = androidViewFilterRender;
this.baseObjectFilterRender = null;
}
public void stopListener() {
this.androidViewFilterRender = null;
this.baseObjectFilterRender = null;
}
public void setPreventMoveOutside(boolean preventMoveOutside) {
@ -39,22 +73,34 @@ public class SpriteGestureController {
}
public boolean spriteTouched(View view, MotionEvent motionEvent) {
if (baseObjectFilterRender == null) return false;
if (baseObjectFilterRender == null && androidViewFilterRender == null) return false;
float xPercent = motionEvent.getX() * 100 / view.getWidth();
float yPercent = motionEvent.getY() * 100 / view.getHeight();
PointF scale = baseObjectFilterRender.getScale();
PointF position = baseObjectFilterRender.getPosition();
PointF scale;
PointF position;
if (baseObjectFilterRender != null) {
scale = baseObjectFilterRender.getScale();
position = baseObjectFilterRender.getPosition();
} else {
scale = androidViewFilterRender.getScale();
position = androidViewFilterRender.getPosition();
}
boolean xTouched = xPercent >= position.x && xPercent <= position.x + scale.x;
boolean yTouched = yPercent >= position.y && yPercent <= position.y + scale.y;
return xTouched && yTouched;
}
public void moveSprite(View view, MotionEvent motionEvent) {
if (baseObjectFilterRender == null) return;
if (baseObjectFilterRender == null && androidViewFilterRender == null) return;
if (motionEvent.getPointerCount() == 1) {
float xPercent = motionEvent.getX() * 100 / view.getWidth();
float yPercent = motionEvent.getY() * 100 / view.getHeight();
PointF scale = baseObjectFilterRender.getScale();
PointF scale;
if (baseObjectFilterRender != null) {
scale = baseObjectFilterRender.getScale();
} else {
scale = androidViewFilterRender.getScale();
}
if (preventMoveOutside) {
float x = xPercent - scale.x / 2.0F;
float y = yPercent - scale.y / 2.0F;
@ -70,22 +116,39 @@ public class SpriteGestureController {
if (y + scale.y > 100.0F) {
y = 100.0F - scale.y;
}
baseObjectFilterRender.setPosition(x, y);
if (baseObjectFilterRender != null) {
baseObjectFilterRender.setPosition(x, y);
} else {
androidViewFilterRender.setPosition(x, y);
}
} else {
baseObjectFilterRender.setPosition(xPercent - scale.x / 2f, yPercent - scale.y / 2f);
if (baseObjectFilterRender != null) {
baseObjectFilterRender.setPosition(xPercent - scale.x / 2f, yPercent - scale.y / 2f);
} else {
androidViewFilterRender.setPosition(xPercent - scale.x / 2f, yPercent - scale.y / 2f);
}
}
}
}
public void scaleSprite(MotionEvent motionEvent) {
if (baseObjectFilterRender == null) return;
if (baseObjectFilterRender == null && androidViewFilterRender == null) return;
if (motionEvent.getPointerCount() > 1) {
float distance = CameraHelper.getFingerSpacing(motionEvent);
float percent = distance >= lastDistance ? 1 : -1;
PointF scale = baseObjectFilterRender.getScale();
PointF scale;
if (baseObjectFilterRender != null) {
scale = baseObjectFilterRender.getScale();
} else {
scale = androidViewFilterRender.getScale();
}
scale.x += percent;
scale.y += percent;
baseObjectFilterRender.setScale(scale.x, scale.y);
if (baseObjectFilterRender != null) {
baseObjectFilterRender.setScale(scale.x, scale.y);
} else {
androidViewFilterRender.setScale(scale.x, scale.y);
}
lastDistance = distance;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl;
import android.opengl.EGL14;
@ -19,45 +35,29 @@ import com.pedro.encoder.utils.gl.GlUtil;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class SurfaceManager {
private static final String TAG = "SurfaceManager";
private static final int EGL_RECORDABLE_ANDROID = 0x3142;
private EGLContext eglContext = null;
private EGLSurface eglSurface = null;
private EGLDisplay eglDisplay = null;
private EGLContext eglContext = EGL14.EGL_NO_CONTEXT;
private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE;
private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY;
private volatile boolean isReady = false;
/**
* Creates an EGL context and an EGL surface.
*/
public SurfaceManager(Surface surface, SurfaceManager manager) {
eglSetup(surface, manager.eglContext);
}
/**
* Creates an EGL context and an EGL surface.
*/
public SurfaceManager(Surface surface, EGLContext eglContext) {
eglSetup(surface, eglContext);
}
/**
* Creates an EGL context and an EGL surface.
*/
public SurfaceManager(Surface surface) {
eglSetup(surface, null);
}
public SurfaceManager() {
eglSetup(null, null);
public boolean isReady() {
return isReady;
}
public void makeCurrent() {
if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
Log.e("Error", "eglMakeCurrent failed");
Log.e(TAG, "eglMakeCurrent failed");
}
}
public void swapBuffer() {
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
if (!EGL14.eglSwapBuffers(eglDisplay, eglSurface)) {
Log.e(TAG, "eglSwapBuffers failed");
}
}
/**
@ -71,7 +71,11 @@ public class SurfaceManager {
/**
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
*/
private void eglSetup(Surface surface, EGLContext eglSharedContext) {
public void eglSetup(int width, int height, Surface surface, EGLContext eglSharedContext) {
if (isReady) {
Log.e(TAG, "already ready, ignored");
return;
}
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
@ -83,23 +87,43 @@ public class SurfaceManager {
// Configure EGL for recording and OpenGL ES 2.0.
int[] attribList;
if (eglSharedContext == null) {
if (eglSharedContext == null && surface == null) {
attribList = new int[]{
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
};
} else if (eglSharedContext == null) {
attribList = new int[]{
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
};
} else if (surface == null) {
attribList = new int[] {
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RECORDABLE_ANDROID, 1,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
};
} else {
attribList = new int[] {
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RECORDABLE_ANDROID, 1,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RECORDABLE_ANDROID, 1,
/* AA https://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 */
//EGL14.EGL_SAMPLE_BUFFERS, 1 /* true */,
//EGL14.EGL_SAMPLES, 4, /* increase to more smooth limit of your GPU */
EGL14.EGL_NONE
};
}
EGLConfig[] configs = new EGLConfig[1];
@ -119,7 +143,7 @@ public class SurfaceManager {
// Create a window surface, and attach it to the Surface we received.
if (surface == null) {
int[] surfaceAttribs = {
EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE
EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE
};
eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configs[0], surfaceAttribs, 0);
} else {
@ -129,6 +153,28 @@ public class SurfaceManager {
eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, configs[0], surface, surfaceAttribs, 0);
}
GlUtil.checkEglError("eglCreateWindowSurface");
isReady = true;
Log.i(TAG, "GL initialized");
}
public void eglSetup(Surface surface, SurfaceManager manager) {
eglSetup(2, 2, surface, manager.eglContext);
}
public void eglSetup(int width, int height, SurfaceManager manager) {
eglSetup(width, height, null, manager.eglContext);
}
public void eglSetup(Surface surface, EGLContext eglContext) {
eglSetup(2, 2, surface, eglContext);
}
public void eglSetup(Surface surface) {
eglSetup(2, 2, surface, null);
}
public void eglSetup() {
eglSetup(2, 2, null, null);
}
/**
@ -142,10 +188,14 @@ public class SurfaceManager {
EGL14.eglDestroyContext(eglDisplay, eglContext);
EGL14.eglReleaseThread();
EGL14.eglTerminate(eglDisplay);
Log.i(TAG, "GL released");
eglDisplay = EGL14.EGL_NO_DISPLAY;
eglContext = EGL14.EGL_NO_CONTEXT;
eglSurface = EGL14.EGL_NO_SURFACE;
isReady = false;
} else {
Log.e(TAG, "GL already released");
}
eglDisplay = EGL14.EGL_NO_DISPLAY;
eglContext = EGL14.EGL_NO_CONTEXT;
eglSurface = EGL14.EGL_NO_SURFACE;
}
public EGLContext getEglContext() {

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl;
import android.graphics.Bitmap;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
import android.content.Context;
@ -63,7 +79,7 @@ public class CameraRender extends BaseRenderOffScreen {
uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
//camera texture
GlUtil.createExternalTextures(1, textureID, 0);
GlUtil.createExternalTextures(textureID.length, textureID, 0);
surfaceTexture = new SurfaceTexture(textureID[0]);
surfaceTexture.setDefaultBufferSize(width, height);
surface = new Surface(surfaceTexture);
@ -107,8 +123,8 @@ public class CameraRender extends BaseRenderOffScreen {
@Override
public void release() {
GLES20.glDeleteProgram(program);
surfaceTexture = null;
surface = null;
surfaceTexture.release();
surface.release();
}
public void updateTexImage() {

View File

@ -0,0 +1,177 @@
package com.pedro.encoder.input.gl.render
import android.content.Context
import android.graphics.SurfaceTexture
import android.os.Build
import android.view.Surface
import androidx.annotation.RequiresApi
import com.pedro.encoder.input.gl.FilterAction
import com.pedro.encoder.input.gl.render.filters.BaseFilterRender
/**
* Created by pedro on 20/3/22.
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
class MainRender {
private val cameraRender = CameraRender()
private val screenRender = ScreenRender()
private var width = 0
private var height = 0
private var previewWidth = 0
private var previewHeight = 0
private var context: Context? = null
private var filterRenders: MutableList<BaseFilterRender> = ArrayList()
fun initGl(context: Context, encoderWidth: Int, encoderHeight: Int, previewWidth: Int, previewHeight: Int) {
this.context = context
width = encoderWidth
height = encoderHeight
this.previewWidth = previewWidth
this.previewHeight = previewHeight
cameraRender.initGl(width, height, context, previewWidth, previewHeight)
screenRender.setStreamSize(encoderWidth, encoderHeight)
screenRender.setTexId(cameraRender.texId)
screenRender.initGl(context)
}
fun drawOffScreen() {
cameraRender.draw()
for (baseFilterRender in filterRenders) baseFilterRender.draw()
}
fun drawScreen(width: Int, height: Int, keepAspectRatio: Boolean, mode: Int, rotation: Int,
flipStreamVertical: Boolean, flipStreamHorizontal: Boolean) {
screenRender.draw(width, height, keepAspectRatio, mode, rotation, flipStreamVertical,
flipStreamHorizontal)
}
fun drawScreenEncoder(width: Int, height: Int, isPortrait: Boolean, rotation: Int,
flipStreamVertical: Boolean, flipStreamHorizontal: Boolean) {
screenRender.drawEncoder(width, height, isPortrait, rotation, flipStreamVertical,
flipStreamHorizontal)
}
fun drawScreenPreview(width: Int, height: Int, isPortrait: Boolean, keepAspectRatio: Boolean,
mode: Int, rotation: Int, flipStreamVertical: Boolean, flipStreamHorizontal: Boolean) {
screenRender.drawPreview(width, height, isPortrait, keepAspectRatio, mode, rotation,
flipStreamVertical, flipStreamHorizontal)
}
fun release() {
cameraRender.release()
for (baseFilterRender in filterRenders) baseFilterRender.release()
filterRenders.clear()
screenRender.release()
}
private fun setFilter(position: Int, baseFilterRender: BaseFilterRender) {
val id = filterRenders[position].previousTexId
val renderHandler = filterRenders[position].renderHandler
filterRenders[position].release()
filterRenders[position] = baseFilterRender
filterRenders[position].previousTexId = id
filterRenders[position].initGl(width, height, context, previewWidth, previewHeight)
filterRenders[position].renderHandler = renderHandler
}
private fun addFilter(baseFilterRender: BaseFilterRender) {
filterRenders.add(baseFilterRender)
baseFilterRender.initGl(width, height, context, previewWidth, previewHeight)
baseFilterRender.initFBOLink()
reOrderFilters()
}
private fun addFilter(position: Int, baseFilterRender: BaseFilterRender) {
filterRenders.add(position, baseFilterRender)
baseFilterRender.initGl(width, height, context, previewWidth, previewHeight)
baseFilterRender.initFBOLink()
reOrderFilters()
}
private fun clearFilters() {
for (baseFilterRender in filterRenders) {
baseFilterRender.release()
}
filterRenders.clear()
reOrderFilters()
}
private fun removeFilter(position: Int) {
filterRenders.removeAt(position).release()
reOrderFilters()
}
private fun removeFilter(baseFilterRender: BaseFilterRender) {
baseFilterRender.release()
filterRenders.remove(baseFilterRender)
reOrderFilters()
}
private fun reOrderFilters() {
for (i in filterRenders.indices) {
val texId = if (i == 0) cameraRender.texId else filterRenders[i - 1].texId
filterRenders[i].previousTexId = texId
}
val texId = if (filterRenders.isEmpty()) {
cameraRender.texId
} else {
filterRenders[filterRenders.size - 1].texId
}
screenRender.setTexId(texId)
}
fun setFilterAction(filterAction: FilterAction?, position: Int, baseFilterRender: BaseFilterRender) {
when (filterAction) {
FilterAction.SET -> if (filterRenders.size > 0) {
setFilter(position, baseFilterRender)
} else {
addFilter(baseFilterRender)
}
FilterAction.SET_INDEX -> setFilter(position, baseFilterRender)
FilterAction.ADD -> addFilter(baseFilterRender)
FilterAction.ADD_INDEX -> addFilter(position, baseFilterRender)
FilterAction.CLEAR -> clearFilters()
FilterAction.REMOVE -> removeFilter(baseFilterRender)
FilterAction.REMOVE_INDEX -> removeFilter(position)
else -> {}
}
}
fun filtersCount(): Int {
return filterRenders.size
}
fun setPreviewSize(previewWidth: Int, previewHeight: Int) {
for (i in filterRenders.indices) {
filterRenders[i].setPreviewSize(previewWidth, previewHeight)
}
}
fun enableAA(AAEnabled: Boolean) {
screenRender.isAAEnabled = AAEnabled
}
fun isAAEnabled(): Boolean {
return screenRender.isAAEnabled
}
fun updateFrame() {
cameraRender.updateTexImage()
}
fun getSurfaceTexture(): SurfaceTexture {
return cameraRender.surfaceTexture
}
fun getSurface(): Surface {
return cameraRender.surface
}
fun setCameraRotation(rotation: Int) {
cameraRender.setRotation(rotation)
}
fun setCameraFlip(isFlipHorizontal: Boolean, isFlipVertical: Boolean) {
cameraRender.setFlip(isFlipHorizontal, isFlipVertical)
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
import android.content.Context;
@ -5,8 +21,9 @@ import android.graphics.SurfaceTexture;
import android.os.Build;
import android.view.Surface;
import androidx.annotation.RequiresApi;
import com.pedro.encoder.input.gl.FilterAction;
import com.pedro.encoder.input.gl.render.filters.BaseFilterRender;
import com.pedro.encoder.input.gl.render.filters.NoFilterRender;
import java.util.ArrayList;
import java.util.List;
@ -17,13 +34,12 @@ import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class ManagerRender {
//Increase it to render more than 1 filter and set filter by position.
// You must modify it before create your rtmp or rtsp object.
public static int numFilters = 1;
//Set filter limit. If the number is 0 or less you can add infinity filters
public static int numFilters = 0;
private CameraRender cameraRender;
private List<BaseFilterRender> baseFilterRender = new ArrayList<>(numFilters);
private ScreenRender screenRender;
private final CameraRender cameraRender;
private final List<BaseFilterRender> filterRenders;
private final ScreenRender screenRender;
private int width;
private int height;
@ -32,8 +48,8 @@ public class ManagerRender {
private Context context;
public ManagerRender() {
filterRenders = new ArrayList<>();
cameraRender = new CameraRender();
for (int i = 0; i < numFilters; i++) baseFilterRender.add(new NoFilterRender());
screenRender = new ScreenRender();
}
@ -45,33 +61,26 @@ public class ManagerRender {
this.previewWidth = previewWidth;
this.previewHeight = previewHeight;
cameraRender.initGl(width, height, context, previewWidth, previewHeight);
for (int i = 0; i < numFilters; i++) {
int textId = i == 0 ? cameraRender.getTexId() : baseFilterRender.get(i - 1).getTexId();
baseFilterRender.get(i).setPreviousTexId(textId);
baseFilterRender.get(i).initGl(width, height, context, previewWidth, previewHeight);
baseFilterRender.get(i).initFBOLink();
}
screenRender.setStreamSize(encoderWidth, encoderHeight);
screenRender.setTexId(baseFilterRender.get(numFilters - 1).getTexId());
screenRender.setTexId(cameraRender.getTexId());
screenRender.initGl(context);
}
public void drawOffScreen() {
cameraRender.draw();
for (BaseFilterRender baseFilterRender : baseFilterRender) baseFilterRender.draw();
for (BaseFilterRender baseFilterRender : filterRenders) baseFilterRender.draw();
}
public void drawScreen(int width, int height, boolean keepAspectRatio, int mode, int rotation,
boolean isPreview) {
screenRender.draw(width, height, keepAspectRatio, mode, rotation, isPreview);
boolean flipStreamVertical, boolean flipStreamHorizontal) {
screenRender.draw(width, height, keepAspectRatio, mode, rotation, flipStreamVertical,
flipStreamHorizontal);
}
public void release() {
cameraRender.release();
for (int i = 0; i < this.baseFilterRender.size(); i++) {
this.baseFilterRender.get(i).release();
this.baseFilterRender.set(i, new NoFilterRender());
}
for (BaseFilterRender baseFilterRender : filterRenders) baseFilterRender.release();
filterRenders.clear();
screenRender.release();
}
@ -95,14 +104,99 @@ public class ManagerRender {
return cameraRender.getSurface();
}
public void setFilter(int position, BaseFilterRender baseFilterRender) {
final int id = this.baseFilterRender.get(position).getPreviousTexId();
final RenderHandler renderHandler = this.baseFilterRender.get(position).getRenderHandler();
this.baseFilterRender.get(position).release();
this.baseFilterRender.set(position, baseFilterRender);
this.baseFilterRender.get(position).setPreviousTexId(id);
this.baseFilterRender.get(position).initGl(width, height, context, previewWidth, previewHeight);
this.baseFilterRender.get(position).setRenderHandler(renderHandler);
private void setFilter(int position, BaseFilterRender baseFilterRender) {
final int id = filterRenders.get(position).getPreviousTexId();
final RenderHandler renderHandler = filterRenders.get(position).getRenderHandler();
filterRenders.get(position).release();
filterRenders.set(position, baseFilterRender);
filterRenders.get(position).setPreviousTexId(id);
filterRenders.get(position).initGl(width, height, context, previewWidth, previewHeight);
filterRenders.get(position).setRenderHandler(renderHandler);
}
private void addFilter(BaseFilterRender baseFilterRender) {
filterRenders.add(baseFilterRender);
baseFilterRender.initGl(width, height, context, previewWidth, previewHeight);
baseFilterRender.initFBOLink();
reOrderFilters();
}
private void addFilter(int position, BaseFilterRender baseFilterRender) {
filterRenders.add(position, baseFilterRender);
baseFilterRender.initGl(width, height, context, previewWidth, previewHeight);
baseFilterRender.initFBOLink();
reOrderFilters();
}
private void clearFilters() {
for (BaseFilterRender baseFilterRender: filterRenders) {
baseFilterRender.release();
}
filterRenders.clear();
reOrderFilters();
}
private void removeFilter(int position) {
filterRenders.remove(position).release();
reOrderFilters();
}
private void removeFilter(BaseFilterRender baseFilterRender) {
baseFilterRender.release();
filterRenders.remove(baseFilterRender);
reOrderFilters();
}
private void reOrderFilters() {
for (int i = 0; i < filterRenders.size(); i++) {
int texId = i == 0 ? cameraRender.getTexId() : filterRenders.get(i - 1).getTexId();
filterRenders.get(i).setPreviousTexId(texId);
}
int texId = filterRenders.isEmpty() ? cameraRender.getTexId() :
filterRenders.get(filterRenders.size() - 1).getTexId();
screenRender.setTexId(texId);
}
public void setFilterAction(FilterAction filterAction, int position, BaseFilterRender baseFilterRender) {
switch (filterAction) {
case SET:
if (filterRenders.size() > 0) {
setFilter(position, baseFilterRender);
} else {
addFilter(baseFilterRender);
}
break;
case SET_INDEX:
setFilter(position, baseFilterRender);
break;
case ADD:
if (numFilters > 0 && filterRenders.size() >= numFilters) {
throw new RuntimeException("limit of filters(" + numFilters + ") exceeded");
}
addFilter(baseFilterRender);
break;
case ADD_INDEX:
if (numFilters > 0 && filterRenders.size() >= numFilters) {
throw new RuntimeException("limit of filters(" + numFilters + ") exceeded");
}
addFilter(position, baseFilterRender);
break;
case CLEAR:
clearFilters();
break;
case REMOVE:
removeFilter(baseFilterRender);
break;
case REMOVE_INDEX:
removeFilter(position);
break;
default:
break;
}
}
public int filtersCount() {
return filterRenders.size();
}
public void setCameraRotation(int rotation) {
@ -114,8 +208,8 @@ public class ManagerRender {
}
public void setPreviewSize(int previewWidth, int previewHeight) {
for (int i = 0; i < this.baseFilterRender.size(); i++) {
this.baseFilterRender.get(i).setPreviewSize(previewWidth, previewHeight);
for (int i = 0; i < filterRenders.size(); i++) {
filterRenders.get(i).setPreviewSize(previewWidth, previewHeight);
}
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
/**

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
import android.content.Context;
@ -22,11 +38,11 @@ public class ScreenRender {
//rotation matrix
private final float[] squareVertexData = {
// X, Y, Z, U, V
-1f, -1f, 0f, 0f, 0f, //bottom left
1f, -1f, 0f, 1f, 0f, //bottom right
-1f, 1f, 0f, 0f, 1f, //top left
1f, 1f, 0f, 1f, 1f, //top right
// X, Y, Z, U, V
-1f, -1f, 0f, 0f, 0f, //bottom left
1f, -1f, 0f, 1f, 0f, //bottom right
-1f, 1f, 0f, 0f, 1f, //top left
1f, 1f, 0f, 1f, 1f, //top right
};
private FloatBuffer squareVertex;
@ -48,20 +64,18 @@ public class ScreenRender {
private int streamWidth;
private int streamHeight;
private boolean isPortrait;
public ScreenRender() {
squareVertex =
ByteBuffer.allocateDirect(squareVertexData.length * BaseRenderOffScreen.FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
ByteBuffer.allocateDirect(squareVertexData.length * BaseRenderOffScreen.FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
squareVertex.put(squareVertexData).position(0);
Matrix.setIdentityM(MVPMatrix, 0);
Matrix.setIdentityM(STMatrix, 0);
}
public void initGl(Context context) {
isPortrait = CameraHelper.isPortrait(context);
GlUtil.checkGlError("initGl start");
String vertexShader = GlUtil.getStringFromRaw(context, R.raw.simple_vertex);
String fragmentShader = GlUtil.getStringFromRaw(context, R.raw.fxaa);
@ -78,15 +92,47 @@ public class ScreenRender {
}
public void draw(int width, int height, boolean keepAspectRatio, int mode, int rotation,
boolean isPreview) {
boolean flipStreamVertical, boolean flipStreamHorizontal) {
GlUtil.checkGlError("drawScreen start");
if (mode == 2 || mode == 3) { //stream rotation is enabled
SizeCalculator.updateMatrix(rotation, width, height, isPreview, isPortrait, MVPMatrix);
}
SizeCalculator.processMatrix(rotation, flipStreamHorizontal, flipStreamVertical,width,height,true,keepAspectRatio, MVPMatrix);
SizeCalculator.calculateViewPort(keepAspectRatio, mode, width, height, streamWidth,
streamHeight);
streamHeight);
draw(width, height);
}
public void drawEncoder(int width, int height, boolean isPortrait, int rotation,
boolean flipStreamVertical, boolean flipStreamHorizontal) {
GlUtil.checkGlError("drawScreen start");
SizeCalculator.processMatrix(rotation, flipStreamHorizontal, flipStreamVertical,width, height, isPortrait,false, MVPMatrix);
SizeCalculator.calculateViewPortEncoder(width, height, isPortrait);
draw(width, height);
}
public void drawPreview(int width, int height, boolean isPortrait, boolean keepAspectRatio,
int mode, int rotation, boolean flipStreamVertical, boolean flipStreamHorizontal) {
GlUtil.checkGlError("drawScreen start");
SizeCalculator.processMatrix(rotation, flipStreamHorizontal, flipStreamVertical, width, height, isPortrait,true, MVPMatrix);
float factor = (float) streamWidth / (float) streamHeight;
int w;
int h;
if (factor >= 1f) {
w = isPortrait ? streamHeight : streamWidth;
h = isPortrait ? streamWidth : streamHeight;
} else {
w = isPortrait ? streamWidth : streamHeight;
h = isPortrait ? streamHeight : streamWidth;
}
SizeCalculator.calculateViewPort(keepAspectRatio, mode, width, height, w, h);
draw(width, height);
}
private void draw(int width, int height) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
@ -94,12 +140,12 @@ public class ScreenRender {
squareVertex.position(BaseRenderOffScreen.SQUARE_VERTEX_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
BaseRenderOffScreen.SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
BaseRenderOffScreen.SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
GLES20.glEnableVertexAttribArray(aPositionHandle);
squareVertex.position(BaseRenderOffScreen.SQUARE_VERTEX_DATA_UV_OFFSET);
GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT, false,
BaseRenderOffScreen.SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
BaseRenderOffScreen.SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
GLES20.glEnableVertexAttribArray(aTextureHandle);
GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render;
import android.content.Context;
@ -49,7 +65,6 @@ public class SimpleCameraRender {
private Surface surface;
private int streamWidth;
private int streamHeight;
private boolean isPortrait;
public SimpleCameraRender() {
Matrix.setIdentityM(MVPMatrix, 0);
@ -86,13 +101,11 @@ public class SimpleCameraRender {
}
public void drawFrame(int width, int height, boolean keepAspectRatio, int mode, int rotation,
boolean isPreview) {
boolean flipStreamVertical, boolean flipStreamHorizontal) {
GlUtil.checkGlError("drawFrame start");
surfaceTexture.getTransformMatrix(STMatrix);
if (mode == 2 || mode == 3) { //stream rotation is enabled
SizeCalculator.updateMatrix(rotation, width, height, isPreview, isPortrait, MVPMatrix);
}
SizeCalculator.processMatrix(rotation, flipStreamHorizontal, flipStreamVertical, width, height, true, false, MVPMatrix);
SizeCalculator.calculateViewPort(keepAspectRatio, mode, width, height, streamWidth,
streamHeight);
@ -125,7 +138,6 @@ public class SimpleCameraRender {
* Initializes GL state. Call this after the EGL surface has been created and made current.
*/
public void initGl(Context context, int streamWidth, int streamHeight) {
isPortrait = CameraHelper.isPortrait(context);
this.streamWidth = streamWidth;
this.streamHeight = streamHeight;
GlUtil.checkGlError("initGl start");
@ -139,7 +151,7 @@ public class SimpleCameraRender {
uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
//camera texture
GlUtil.createExternalTextures(1, texturesID, 0);
GlUtil.createExternalTextures(texturesID.length, texturesID, 0);
textureID = texturesID[0];
surfaceTexture = new SurfaceTexture(textureID);
@ -150,8 +162,8 @@ public class SimpleCameraRender {
public void release() {
GLES20.glDeleteProgram(program);
surfaceTexture = null;
surface = null;
surfaceTexture.release();
surface.release();
}
public void setFlip(boolean isFlipHorizontal, boolean isFlipVertical) {

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,8 +1,25 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
@ -15,10 +32,13 @@ import android.view.Surface;
import android.view.View;
import androidx.annotation.RequiresApi;
import com.pedro.encoder.R;
import com.pedro.encoder.input.gl.AndroidViewSprite;
import com.pedro.encoder.utils.gl.GlUtil;
import com.pedro.encoder.utils.gl.TranslateTo;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by pedro on 4/02/18.
@ -44,16 +64,21 @@ public class AndroidViewFilterRender extends BaseFilterRender {
private int uSamplerHandle = -1;
private int uSamplerViewHandle = -1;
private int[] viewId = new int[1];
private int[] viewId = new int[] { -1, -1 };
private View view;
private SurfaceTexture surfaceTexture;
private Surface surface;
private Handler mainHandler;
//Use 2 surfaces to avoid block render thread
private SurfaceTexture surfaceTexture, surfaceTexture2;
private Surface surface, surface2;
private final Handler mainHandler;
private boolean running = false;
private ExecutorService thread = null;
private boolean hardwareMode = true;
private final AndroidViewSprite sprite;
private volatile Status renderingStatus = Status.DONE1;
private int rotation;
private float positionX, positionY;
private float scaleX = 1f, scaleY = 1f;
private float viewX, viewY;
private enum Status {
RENDER1, RENDER2, DONE1, DONE2
}
public AndroidViewFilterRender() {
squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.length * FLOAT_SIZE_BYTES)
@ -62,6 +87,7 @@ public class AndroidViewFilterRender extends BaseFilterRender {
squareVertex.put(squareVertexDataFilter).position(0);
Matrix.setIdentityM(MVPMatrix, 0);
Matrix.setIdentityM(STMatrix, 0);
sprite = new AndroidViewSprite();
mainHandler = new Handler(Looper.getMainLooper());
}
@ -78,31 +104,37 @@ public class AndroidViewFilterRender extends BaseFilterRender {
uSamplerHandle = GLES20.glGetUniformLocation(program, "uSampler");
uSamplerViewHandle = GLES20.glGetUniformLocation(program, "uSamplerView");
GlUtil.createExternalTextures(1, viewId, 0);
GlUtil.createExternalTextures(viewId.length, viewId, 0);
surfaceTexture = new SurfaceTexture(viewId[0]);
surfaceTexture2 = new SurfaceTexture(viewId[1]);
surface = new Surface(surfaceTexture);
surface2 = new Surface(surfaceTexture2);
}
@Override
protected void drawFilter() {
surfaceTexture.setDefaultBufferSize(getPreviewWidth(), getPreviewHeight());
if (view != null) {
mainHandler.post(new Runnable() {
@Override
public void run() {
Canvas canvas = surface.lockCanvas(null);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.translate(positionX, positionY);
canvas.rotate(rotation, viewX / 2f, viewY / 2f);
float scaleFactorX = (float) getPreviewWidth() / (float) view.getWidth();
float scaleFactorY = (float) getPreviewHeight() / (float) view.getHeight();
canvas.scale(scaleX * scaleFactorX, scaleY * scaleFactorY);
view.draw(canvas);
surface.unlockCanvasAndPost(canvas);
}
});
final Status status = renderingStatus;
switch (status) {
case DONE1:
surfaceTexture.setDefaultBufferSize(getPreviewWidth(), getPreviewHeight());
surfaceTexture.updateTexImage();
renderingStatus = Status.RENDER2;
break;
case DONE2:
surfaceTexture2.setDefaultBufferSize(getPreviewWidth(), getPreviewHeight());
surfaceTexture2.updateTexImage();
renderingStatus = Status.RENDER1;
break;
case RENDER1:
surfaceTexture2.setDefaultBufferSize(getPreviewWidth(), getPreviewHeight());
surfaceTexture2.updateTexImage();
break;
case RENDER2:
default:
surfaceTexture.setDefaultBufferSize(getPreviewWidth(), getPreviewHeight());
surfaceTexture.updateTexImage();
break;
}
surfaceTexture.updateTexImage();
GLES20.glUseProgram(program);
@ -125,12 +157,27 @@ public class AndroidViewFilterRender extends BaseFilterRender {
//android view
GLES20.glUniform1i(uSamplerViewHandle, 5);
GLES20.glActiveTexture(GLES20.GL_TEXTURE5);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, viewId[0]);
switch (status) {
case DONE2:
case RENDER1:
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, viewId[1]);
break;
case RENDER2:
case DONE1:
default:
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, viewId[0]);
break;
}
}
@Override
public void release() {
stopRender();
GLES20.glDeleteProgram(program);
viewId = new int[] { -1, -1 };
surfaceTexture.release();
surfaceTexture2.release();
}
public View getView() {
@ -138,11 +185,12 @@ public class AndroidViewFilterRender extends BaseFilterRender {
}
public void setView(final View view) {
stopRender();
this.view = view;
if (view != null) {
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
viewX = view.getMeasuredWidth();
viewY = view.getMeasuredHeight();
sprite.setView(view);
startRender();
}
}
@ -152,68 +200,105 @@ public class AndroidViewFilterRender extends BaseFilterRender {
* @param y Position in percent
*/
public void setPosition(float x, float y) {
int previewX = getPreviewWidth();
int previewY = getPreviewHeight();
this.positionX = previewX * x / 100f;
this.positionY = previewY * y / 100f;
sprite.translate(x, y);
}
public void setPosition(TranslateTo positionTo) {
int previewX = getPreviewWidth();
int previewY = getPreviewHeight();
switch (positionTo) {
case TOP:
this.positionX = previewX / 2f - (viewX / 2f);
this.positionY = 0f;
break;
case LEFT:
this.positionX = 0;
this.positionY = previewY / 2f - (viewY / 2f);
break;
case RIGHT:
this.positionX = previewX - viewX;
this.positionY = previewY / 2f - (viewY / 2f);
break;
case BOTTOM:
this.positionX = previewX / 2f - (viewX / 2f);
this.positionY = previewY - viewY;
break;
case CENTER:
this.positionX = previewX / 2f - (viewX / 2f);
this.positionY = previewY / 2f - (viewY / 2f);
break;
case TOP_RIGHT:
this.positionX = previewX - viewX;
this.positionY = 0;
break;
case BOTTOM_LEFT:
this.positionX = 0;
this.positionY = previewY - viewY;
break;
case BOTTOM_RIGHT:
this.positionX = previewX - viewX;
this.positionY = previewY - viewY;
break;
case TOP_LEFT:
default:
this.positionX = 0;
this.positionY = 0;
break;
}
sprite.translate(positionTo);
}
public void setRotation(int rotation) {
if (rotation < 0) {
this.rotation = 0;
} else if (rotation > 360) {
this.rotation = 360;
} else {
this.rotation = rotation;
}
sprite.setRotation(rotation);
}
public void setScale(float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
sprite.scale(scaleX, scaleY);
}
}
public PointF getScale() {
return sprite.getScale();
}
public PointF getPosition() {
return sprite.getTranslation();
}
public int getRotation() {
return sprite.getRotation();
}
public boolean isHardwareMode() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hardwareMode;
}
/**
* Draw in surface using hardware canvas. True by default
*/
public void setHardwareMode(boolean hardwareMode) {
this.hardwareMode = hardwareMode;
}
private void startRender() {
running = true;
thread = Executors.newSingleThreadExecutor();
thread.execute(() -> {
while (running) {
final Status status = renderingStatus;
if (status == Status.RENDER1 || status == Status.RENDER2) {
final Canvas canvas;
try {
if (isHardwareMode()) {
canvas = status == Status.RENDER1 ? surface.lockHardwareCanvas() : surface2.lockHardwareCanvas();
} else {
canvas = status == Status.RENDER1 ? surface.lockCanvas(null) : surface2.lockCanvas(null);
}
} catch (IllegalStateException e) {
continue;
}
sprite.calculateDefaultScale(getPreviewWidth(), getPreviewHeight());
PointF canvasPosition = sprite.getCanvasPosition(getPreviewWidth(), getPreviewHeight());
PointF canvasScale = sprite.getCanvasScale(getPreviewWidth(), getPreviewHeight());
PointF rotationAxis = sprite.getRotationAxis();
int rotation = sprite.getRotation();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.translate(canvasPosition.x, canvasPosition.y);
canvas.scale(canvasScale.x, canvasScale.y);
canvas.rotate(rotation, rotationAxis.x, rotationAxis.y);
try {
view.draw(canvas);
if (status == Status.RENDER1) {
surface.unlockCanvasAndPost(canvas);
renderingStatus = Status.DONE1;
} else {
surface2.unlockCanvasAndPost(canvas);
renderingStatus = Status.DONE2;
}
//Sometimes draw could crash if you don't use main thread. Ensuring you can render always
} catch (Exception e) {
mainHandler.post(() -> {
view.draw(canvas);
if (status == Status.RENDER1) {
surface.unlockCanvasAndPost(canvas);
renderingStatus = Status.DONE1;
} else {
surface2.unlockCanvasAndPost(canvas);
renderingStatus = Status.DONE2;
}
});
}
}
}
});
}
private void stopRender() {
running = false;
if (thread != null) {
thread.shutdownNow();
thread = null;
}
renderingStatus = Status.DONE1;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.pedro.encoder.R;
import com.pedro.encoder.input.gl.Sprite;
import com.pedro.encoder.input.gl.TextureLoader;
import com.pedro.encoder.utils.gl.GlUtil;
import com.pedro.encoder.utils.gl.ImageStreamObject;
import com.pedro.encoder.utils.gl.StreamObjectBase;
import com.pedro.encoder.utils.gl.TranslateTo;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class ChromaFilterRender extends BaseFilterRender {
//rotation matrix
private final float[] squareVertexDataFilter = {
// X, Y, Z, U, V
-1f, -1f, 0f, 0f, 0f, //bottom left
1f, -1f, 0f, 1f, 0f, //bottom right
-1f, 1f, 0f, 0f, 1f, //top left
1f, 1f, 0f, 1f, 1f, //top right
};
private int program = -1;
private int aPositionHandle = -1;
private int aTextureHandle = -1;
private int aTextureObjectHandle = -1;
private int uMVPMatrixHandle = -1;
private int uSTMatrixHandle = -1;
private int uSamplerHandle = -1;
private int uObjectHandle = -1;
private int uSensitiveHandle = -1;
private FloatBuffer squareVertexObject;
protected int[] streamObjectTextureId = new int[] { -1 };
protected TextureLoader textureLoader = new TextureLoader();
protected StreamObjectBase streamObject;
private Sprite sprite;
protected boolean shouldLoad = false;
private float sensitive = 0.8f;
public ChromaFilterRender() {
streamObject = new ImageStreamObject();
squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
squareVertex.put(squareVertexDataFilter).position(0);
sprite = new Sprite();
float[] vertices = sprite.getTransformedVertices();
squareVertexObject = ByteBuffer.allocateDirect(vertices.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
squareVertexObject.put(vertices).position(0);
Matrix.setIdentityM(MVPMatrix, 0);
Matrix.setIdentityM(STMatrix, 0);
}
@Override
protected void initGlFilter(Context context) {
String vertexShader = GlUtil.getStringFromRaw(context, R.raw.object_vertex);
String fragmentShader = GlUtil.getStringFromRaw(context, R.raw.chroma_fragment);
program = GlUtil.createProgram(vertexShader, fragmentShader);
aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
aTextureHandle = GLES20.glGetAttribLocation(program, "aTextureCoord");
aTextureObjectHandle = GLES20.glGetAttribLocation(program, "aTextureObjectCoord");
uMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
uSamplerHandle = GLES20.glGetUniformLocation(program, "uSampler");
uObjectHandle = GLES20.glGetUniformLocation(program, "uObject");
uSensitiveHandle = GLES20.glGetUniformLocation(program, "uSensitive");
}
@Override
protected void drawFilter() {
if (shouldLoad) {
releaseTexture();
streamObjectTextureId = textureLoader.load(streamObject.getBitmaps());
shouldLoad = false;
}
GLES20.glUseProgram(program);
squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
GLES20.glEnableVertexAttribArray(aPositionHandle);
squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET);
GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT, false,
SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
GLES20.glEnableVertexAttribArray(aTextureHandle);
squareVertexObject.position(SQUARE_VERTEX_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(aTextureObjectHandle, 2, GLES20.GL_FLOAT, false,
2 * FLOAT_SIZE_BYTES, squareVertexObject);
GLES20.glEnableVertexAttribArray(aTextureObjectHandle);
GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);
GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0);
//Sampler
GLES20.glUniform1i(uSamplerHandle, 4);
GLES20.glActiveTexture(GLES20.GL_TEXTURE4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, previousTexId);
//Object
GLES20.glUniform1i(uObjectHandle, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[0]);
GLES20.glUniform1f(uSensitiveHandle, sensitive);
}
@Override
public void release() {
GLES20.glDeleteProgram(program);
releaseTexture();
sprite.reset();
}
private void releaseTexture() {
GLES20.glDeleteTextures(streamObjectTextureId.length, streamObjectTextureId, 0);
streamObjectTextureId = new int[] { -1 };
}
private void setScale(float scaleX, float scaleY) {
sprite.scale(scaleX, scaleY);
squareVertexObject.put(sprite.getTransformedVertices()).position(0);
}
private void setPosition(TranslateTo positionTo) {
sprite.translate(positionTo);
squareVertexObject.put(sprite.getTransformedVertices()).position(0);
}
public void setImage(Bitmap bitmap) {
((ImageStreamObject) streamObject).load(bitmap);
shouldLoad = true;
setScale(100f, 100f);
setPosition(TranslateTo.CENTER);
}
public void setSensitive(float sensitive) {
this.sensitive = sensitive;
}
}

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright (C) 2021 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.pedro.encoder.input.gl.render.filters;
import android.content.Context;

Some files were not shown because too many files have changed in this diff Show More