├── android-studio-project
├── libscreening
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ └── .gitkeep
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ └── net
│ │ │ └── majorkernelpanic
│ │ │ └── screening
│ │ │ ├── rtsp
│ │ │ └── RtcpDeinterleaver.java
│ │ │ ├── video
│ │ │ ├── H263Stream.java
│ │ │ ├── VideoQuality.java
│ │ │ ├── DrawTask.java
│ │ │ ├── VideoStream.java
│ │ │ └── H264Stream.java
│ │ │ ├── audio
│ │ │ ├── AudioQuality.java
│ │ │ ├── AMRNBStream.java
│ │ │ └── AudioStream.java
│ │ │ ├── mp4
│ │ │ ├── MP4Config.java
│ │ │ └── MP4Parser.java
│ │ │ ├── rtp
│ │ │ ├── MediaCodecInputStream.java
│ │ │ ├── AACLATMPacketizer.java
│ │ │ ├── AMRNBPacketizer.java
│ │ │ ├── H263Packetizer.java
│ │ │ ├── AbstractPacketizer.java
│ │ │ ├── AACADTSPacketizer.java
│ │ │ └── H264Packetizer.java
│ │ │ ├── Stream.java
│ │ │ ├── hw
│ │ │ ├── NV21Convertor.java
│ │ │ └── CodecManager.java
│ │ │ └── rtcp
│ │ │ └── SenderReport.java
│ └── build.gradle
├── libscreening-examples
│ └── RTSP-ScreenCaster
│ │ ├── proguard-rules.txt
│ │ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── integers.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── options.xml
│ │ │ ├── drawable
│ │ │ │ └── launcher.png
│ │ │ ├── layout
│ │ │ │ ├── activity_start_networking_service.xml
│ │ │ │ └── service_notification.xml
│ │ │ ├── values-zh
│ │ │ │ └── strings.xml
│ │ │ ├── values-zh-rTW
│ │ │ │ └── strings.xml
│ │ │ ├── values-ja
│ │ │ │ └── strings.xml
│ │ │ ├── values-ko
│ │ │ │ └── strings.xml
│ │ │ ├── values-hi
│ │ │ │ └── strings.xml
│ │ │ ├── values-bn
│ │ │ │ └── strings.xml
│ │ │ ├── values-he
│ │ │ │ └── strings.xml
│ │ │ ├── values-ur
│ │ │ │ └── strings.xml
│ │ │ ├── values-ar
│ │ │ │ └── strings.xml
│ │ │ ├── values-cy
│ │ │ │ └── strings.xml
│ │ │ ├── values-nb
│ │ │ │ └── strings.xml
│ │ │ ├── values-ne
│ │ │ │ └── strings.xml
│ │ │ ├── values-fi
│ │ │ │ └── strings.xml
│ │ │ ├── values-sv
│ │ │ │ └── strings.xml
│ │ │ ├── values-de
│ │ │ │ └── strings.xml
│ │ │ ├── values-vi
│ │ │ │ └── strings.xml
│ │ │ ├── values-cnr
│ │ │ │ └── strings.xml
│ │ │ ├── values-da
│ │ │ │ └── strings.xml
│ │ │ ├── values-gu
│ │ │ │ └── strings.xml
│ │ │ ├── values-ta
│ │ │ │ └── strings.xml
│ │ │ ├── values-th
│ │ │ │ └── strings.xml
│ │ │ ├── values-tr
│ │ │ │ └── strings.xml
│ │ │ ├── values-et
│ │ │ │ └── strings.xml
│ │ │ ├── values-id
│ │ │ │ └── strings.xml
│ │ │ ├── values-sr
│ │ │ │ └── strings.xml
│ │ │ ├── values-te
│ │ │ │ └── strings.xml
│ │ │ ├── values-bg
│ │ │ │ └── strings.xml
│ │ │ ├── values-cs
│ │ │ │ └── strings.xml
│ │ │ ├── values-ga
│ │ │ │ └── strings.xml
│ │ │ ├── values-lv
│ │ │ │ └── strings.xml
│ │ │ ├── values-ro
│ │ │ │ └── strings.xml
│ │ │ ├── values-si
│ │ │ │ └── strings.xml
│ │ │ ├── values-bs
│ │ │ │ └── strings.xml
│ │ │ ├── values-fr
│ │ │ │ └── strings.xml
│ │ │ ├── values-hr
│ │ │ │ └── strings.xml
│ │ │ ├── values-ms
│ │ │ │ └── strings.xml
│ │ │ ├── values-mt
│ │ │ │ └── strings.xml
│ │ │ ├── values-ru
│ │ │ │ └── strings.xml
│ │ │ ├── values-fr-rCA
│ │ │ │ └── strings.xml
│ │ │ ├── values-lt
│ │ │ │ └── strings.xml
│ │ │ ├── values-ml
│ │ │ │ └── strings.xml
│ │ │ ├── values-pt
│ │ │ │ └── strings.xml
│ │ │ ├── values-el
│ │ │ │ └── strings.xml
│ │ │ ├── values-hu
│ │ │ │ └── strings.xml
│ │ │ ├── values-sk
│ │ │ │ └── strings.xml
│ │ │ ├── values-uk
│ │ │ │ └── strings.xml
│ │ │ ├── values-es
│ │ │ │ └── strings.xml
│ │ │ ├── values-it
│ │ │ │ └── strings.xml
│ │ │ ├── values-nl
│ │ │ │ └── strings.xml
│ │ │ ├── values-sl
│ │ │ │ └── strings.xml
│ │ │ └── values-pl
│ │ │ │ └── strings.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── warren_bank
│ │ │ │ └── rtsp_screencaster
│ │ │ │ ├── utils
│ │ │ │ ├── ResourceUtils.java
│ │ │ │ ├── WakeLockMgr.java
│ │ │ │ ├── NetworkUtils.java
│ │ │ │ └── RuntimePermissions.java
│ │ │ │ ├── MainApp.java
│ │ │ │ ├── constant
│ │ │ │ └── Constant.java
│ │ │ │ ├── ui
│ │ │ │ └── StartNetworkingServiceActivity.java
│ │ │ │ └── service
│ │ │ │ └── NetworkingService.java
│ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── settings.gradle
├── constants.gradle
├── build.gradle
├── gradlew.bat
└── gradlew
├── .gitignore
├── package.json
├── README.md
└── notes
└── 1-libstreaming
└── 2. redesign.txt
/android-studio-project/libscreening/src/main/res/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/proguard-rules.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android-studio-project/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/warren-bank/Android-RTSP-ScreenCaster/HEAD/android-studio-project/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 | 1
3 |
4 |
--------------------------------------------------------------------------------
/android-studio-project/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':libscreening'
2 |
3 | include ':RTSP-ScreenCaster'
4 |
5 | project(':RTSP-ScreenCaster').projectDir = new File(rootDir, 'libscreening-examples/RTSP-ScreenCaster')
6 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/drawable/launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/warren-bank/Android-RTSP-ScreenCaster/HEAD/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/drawable/launcher.png
--------------------------------------------------------------------------------
/android-studio-project/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node.js dev-dependencies
2 | node_modules/
3 |
4 | # Android generated
5 | bin
6 | gen
7 | obj
8 | lint.xml
9 |
10 | # IntelliJ IDEA
11 | .idea
12 | *.iml
13 | *.ipr
14 | *.iws
15 | classes
16 | gen-external-apklibs
17 |
18 | # Gradle
19 | .gradle
20 | build
21 | buildout
22 | out
23 |
24 | # Other
25 | .DS_Store
26 | local.properties
27 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/layout/activity_start_networking_service.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/android-studio-project/constants.gradle:
--------------------------------------------------------------------------------
1 | project.ext {
2 | releaseVersionCode = Integer.parseInt("001010521", 10) //Integer.MAX_VALUE == 2147483647
3 | releaseVersion = '001.01.05-21API'
4 | javaVersion = JavaVersion.VERSION_1_8
5 | minSdkVersion = 21
6 | targetSdkVersion = 28
7 | compileSdkVersion = 28
8 | buildToolsVersion = '28.0.3'
9 |
10 | libGlUtils = '0.0.3'
11 | }
12 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/utils/ResourceUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster.utils;
2 |
3 | import android.content.Context;
4 |
5 | public class ResourceUtils {
6 |
7 | public static int getInteger(Context context, int id) {
8 | return context.getResources().getInteger(id);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/android-studio-project/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.3.0' // https://mvnrepository.com/artifact/com.android.tools.build/gradle?repo=google
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | mavenCentral()
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/MainApp.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster;
2 |
3 | import android.app.Application;
4 |
5 | public class MainApp extends Application {
6 | private static MainApp instance;
7 |
8 | public static MainApp getInstance() {
9 | return instance;
10 | }
11 |
12 | @Override
13 | public void onCreate() {
14 | super.onCreate();
15 | instance = this;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@warren-bank/Android-RTSP-ScreenCaster",
3 | "private": true,
4 | "license": "GPL-2.0",
5 | "devDependencies": {
6 | "@warren-bank/translate-android-strings": "^1.7.3"
7 | },
8 | "scripts": {
9 | "translate": "node_modules/.bin/translate-android-strings -i \"en\" -f \"android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values/strings.xml\" -d \"android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res\" -m --na --nc --nw -b \"RTSP ScreenCaster\""
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/constant/Constant.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster.constant;
2 |
3 | public class Constant {
4 |
5 | public interface ExtraDefaultValue {
6 | public static final int SERVER_PORT = 6554;
7 |
8 | public static final int VIDEO_FRAMERATE = 60;
9 | public static final int VIDEO_BITRATE = 1048576; // 1 Mbps
10 |
11 | public static final int AUDIO_SAMPLERATE = 44100; // 44.1 kHz
12 | public static final int AUDIO_BITRATE = 96000; // 96 kbps
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: 服务运行
4 | RTSP ScreenCaster 奔跑:
5 | 单击以停止服务.
6 | 视频帧速率
7 | 视频比特率
8 | 音频采样率
9 | 音频比特率
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: 服務執行中
4 | RTSP ScreenCaster 在執行:
5 | 按一下以停止服務.
6 | 視訊訊框率
7 | 視訊位元速率
8 | 音訊取樣率
9 | 音訊位元速率
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: サービス実行中
4 | RTSP ScreenCaster 実行:
5 | クリックしてサービスを停止.
6 | ビデオ・フレーム率
7 | ビデオビットレート
8 | オーディオ・サンプリング・レート
9 | オーディオビットレート
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: 서비스 실행
4 | RTSP ScreenCaster 실행 중인:
5 | 서비스를 중지하려면 누르십시오..
6 | 비디오 프레임 속도
7 | 비디오 비트 전송률
8 | 오디오 샘플링 레이트
9 | 오디오 비트 전송률
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-hi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: चल रहा
4 | RTSP ScreenCaster चालू:
5 | सेवा रोकने के लिए क्लिक करें.
6 | वीडियो फ्रेम दर
7 | वीडियो बिटरेट
8 | ऑडियो नमूनाकरण दर
9 | ऑडियो बिटरेट
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-bn/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: সার্ভিস চলছে
4 | RTSP ScreenCaster চলমান:
5 | সার্ভিস বন্ধ করতে ক্লিক করুন.
6 | ভিডিও ফ্রেম রেট
7 | ভিডিও বিটরেট
8 | অডিও স্যামলিং রেট
9 | অডিও বিটরেট
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-he/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: הרצת שירות
4 | RTSP ScreenCaster רץ ב:
5 | לחצו כדי להפסיק את השירות.
6 | תעריף מסגרת וידיאו
7 | Bitתעריף וידאו
8 | שיעור דגימת אודיו
9 | קצב אודיו
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ur/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: سروس چلانے
4 | RTSP ScreenCaster چل رہا ہے:
5 | سٹاپ سروس پر کلک کریں.
6 | ویڈیو Frame Rate
7 | ویڈیو Bitrate
8 | آڈیو سیمپلنگ Rate
9 | آڈیو Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ar/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: جاري تشغيل الخدمة
4 | RTSP ScreenCaster تشغيل في:
5 | اضغط لايقاف الخدمة.
6 | معدل اطار الفيديو
7 | فيديو Bitrate
8 | معدل التقارن الصوتي
9 | صورة نقطية
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-cy/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: rhedeg gwasanaeth
4 | RTSP ScreenCaster rhedeg ar:
5 | Cliciwch i atal y gwasanaeth.
6 | Cyfradd Ffrâm Fideo
7 | Comment
8 | Graddfa Samplu Sain
9 | Name
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-nb/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: tjenestekjøring
4 | RTSP ScreenCaster kjøre på:
5 | Klikk for å stoppe tjeneste.
6 | Rentesats for
7 | Video Bitrate
8 | Lyd Sampling-rate
9 | Lyd Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ne/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: सेवा सञ्चालन
4 | RTSP ScreenCaster चलिरहेको छ:
5 | सेवा रोक्न क्लिक गर्नुहोस्.
6 | भिडियो फ्रेम दर
7 | भिडियो बिटरेट
8 | अडियो नमूना दर
9 | अडियो बिटरेट
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-fi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Palvelun käyttö
4 | RTSP ScreenCaster Ajossa:
5 | Lopeta palvelu napsauttamalla.
6 | Video Frame Rate
7 | Videobitrate
8 | Äänenäytteenotto
9 | Äänen bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-sv/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: service som körs
4 | RTSP ScreenCaster körs på:
5 | Klicka för att stoppa tjänsten.
6 | Videoramper
7 | Bifthasti
8 | Ljudsamplingskat
9 | Ljudbithastigheten
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Service läuft
4 | RTSP ScreenCaster Ausführen unter:
5 | Zum Stoppen des Service klicken.
6 | Videorahmenrate
7 | Video Bitrate
8 | Audio-Abtastrate
9 | Audiobitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-vi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: chạy dịch vụ
4 | RTSP ScreenCaster đang chạy:
5 | Nhấp để dừng dịch vụ.
6 | Tốc độ khung ảnh
7 | Video Bitrate
8 | Tỷ lệ lấy mẫu âm thanh
9 | Thông tin âm thanh
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-cnr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: usluga trčanja
4 | RTSP ScreenCaster U redu\?:
5 | Jedan klik da zaustavite službu..
6 | Stopa video okvira
7 | Video Bittarij
8 | Brzina uzorkovanja
9 | Audio Bittarip
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-da/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: serviceprogram
4 | RTSP ScreenCaster kører på:
5 | Klik for at stoppe serviceprogrammet.
6 | Videorammesats
7 | Videobitrat
8 | Audio-stikprøvehastighed
9 | Audiobitrat
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-gu/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: સેવા ચાલી રહ્યુ છે
4 | RTSP ScreenCaster ચાલી રહ્યુ છે:
5 | સેવા બંધ કરવા માટે ક્લિક કરો.
6 | વીડિયો ચોકઠાં દર
7 | વીડિયો બીટદર
8 | ઓડિયો સામ્પ્લીંગ દર
9 | ઓડિયો બીટદર
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ta/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: சேவை இயங்குகிறது
4 | RTSP ScreenCaster இயங்குகிறது:
5 | சேவை நிறுத்த க்ளிக் செய்யவும்.
6 | வீடியோ சட்டம்
7 | வீடியோ பிட்விகிதம்
8 | ஒலி சாம்பல் ரேட்
9 | ஒலி பிட்விகிதம்
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-th/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: เซอร์วิสที่กำลังรัน
4 | RTSP ScreenCaster การรันที่:
5 | คลิกเพื่อหยุดบริการ.
6 | อัตราเฟรมวิดีโอ
7 | อัตราบิตของวิดีโอ
8 | อัตราการสุ่มตัวอย่างเสียง
9 | อัตราบิตเสียง
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-tr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: hizmet çalışıyor
4 | RTSP ScreenCaster çalıştırma:
5 | Hizmeti durdurmak için tıklatın.
6 | Görüntü Çerçeve Hızı
7 | Video Bit Oranı
8 | Ses Örnekleme Hızı
9 | Ses Bitişi
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-et/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Teenus töötab
4 | RTSP ScreenCaster tööaeg:
5 | Klõpsa teenuse peatamiseks.
6 | Video kaadrisagedus
7 | Video bitikiirus
8 | Heliproovide võtmise kiirus
9 | Audio bitikiirus
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-id/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Layanan berjalan
4 | RTSP ScreenCaster berjalan di:
5 | Klik untuk menghentikan layanan.
6 | Laju Bingkai Video
7 | Bitrate Video
8 | Laju Sampling Audio
9 | Bitrate Audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-sr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: услуга ради
4 | RTSP ScreenCaster трèање на:
5 | Кликните да бисте зауставили услугу.
6 | Брзина видео фрејма
7 | Видео битстот
8 | Брзина узорковања звука
9 | Звучни битритам
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-te/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: సేవలు నడుపుతుంటాయి
4 | RTSP ScreenCaster నడుస్తోంది:
5 | సేవను ఆపడానికి క్లిక్ చేయుము.
6 | వీడియో ఫ్రమ్ రేట్
7 | వీడియో బిట్రేట్
8 | ఆడియో సమ్లింగ్ రేట్
9 | ఆడియో బిట్రేట్
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-bg/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: поддръжка на
4 | RTSP ScreenCaster бягане в:
5 | Щракнете, за да спрете услугата.
6 | Ставка на кадрата:
7 | Видео битрейт
8 | Аудио честота на вземане на проби
9 | Аудио битрейт
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-cs/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: služba spuštěna
4 | RTSP ScreenCaster spuštění v:
5 | Klepnutím zastavíte službu.
6 | Rychlost video rámců
7 | Datový tok videa
8 | Rychlost vzorkování zvuku
9 | Datový tok zvuku
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ga/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: seirbhís á rith
4 | RTSP ScreenCaster á rith ag:
5 | Cliceáil chun seirbhís a stopadh.
6 | Ráta Fráma Físe
7 | Ráta Giotán Giotán
8 | Ráta Samplála Fuaime
9 | Ráta Giotán Fuaime
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-lv/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: dienesta gaitas
4 | RTSP ScreenCaster , kas:
5 | Klikšķināt, lai apturētu pakalpojumu.
6 | Video kadru ātrums
7 | Video Bitrate
8 | Audioparaugu ņemšanas ātrums
9 | Audio Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ro/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: rulare serviciu
4 | RTSP ScreenCaster rulare la:
5 | Faceţi clic pentru a opri serviciul.
6 | Rată cadru video
7 | Bitrata video
8 | Rată de eşantionare audio
9 | Bitrata audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-si/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: සේවාව ක්රියාත්මක කරන්න
4 | RTSP ScreenCaster ක්රියාත්මක කරන්න:
5 | සේවාව නවත්වන්න ක්ලික් කරන්න.
6 | වීඩියෝ ෆ්රේම් අගය
7 | වීඩියෝ බිට් රේට්
8 | ශ්රව්ය සමාප්ති අගය
9 | ශ්රව්ය Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-bs/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: usluga u izvođenju
4 | RTSP ScreenCaster izvodi se na:
5 | Kliknite da biste zaustavili servis.
6 | Brzina video okvira
7 | Video bit mjera
8 | Brzina Uzorkovanja Zvuka
9 | Zvučna Bitna
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Service exécuté
4 | RTSP ScreenCaster Exécution à:
5 | Cliquez pour arrêter le service.
6 | Taux de trames vidéo
7 | Bitrate vidéo
8 | Taux d\'échantillonnage audio
9 | Bitrate audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-hr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: servisna izvođenje
4 | RTSP ScreenCaster izvođenje na:
5 | Kliknite za zaustavljanje servisa.
6 | Mjera Video Okvira
7 | Video Bitrate
8 | Brzina Audio Uzorkovanja
9 | Audio Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ms/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: perkhidmatan berjalan
4 | RTSP ScreenCaster berjalan pada:
5 | Klik untuk henti perkhidmatan.
6 | Kadar Bingkai Video
7 | Kadar Bit Video
8 | Kadar Sampelan Audio
9 | Kadar Bit Audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-mt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: servizz jaħdem
4 | RTSP ScreenCaster taħdem f \':
5 | Ikklikkja biex tieqaf servizz.
6 | Rata ta \' Qafas
7 | Birata tal-vidjow
8 | Rata Tal-awdjo Tal-kampjunar
9 | Birata tal-awdjo
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: служба работает
4 | RTSP ScreenCaster запуск в:
5 | Щелкните, чтобы остановить службу.
6 | Частота кадров видео
7 | Видеобитрейт
8 | Частота дискретизации звука
9 | Звуковой битрейт
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-fr-rCA/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: exécution du service
4 | RTSP ScreenCaster exécution à:
5 | Cliquez pour arrêter le service.
6 | Taux de trame vidéo
7 | Vidéo Bitrate
8 | Taux d\'échantillonnage audio
9 | Débit audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-lt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: eksploatuos
4 | RTSP ScreenCaster Veikia:
5 | Spragtelkite norėdami sustabdyti paslaugą.
6 | Vaizdo kadrų dažnis
7 | Vaizdo bitų dažnis
8 | Garso Ėminių Ėmimo Dažnis
9 | Garso bitų dažnis
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-ml/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: സേവനം നടത്തുന്നു
4 | RTSP ScreenCaster പ്രവര്ത്തിപ്പിക്കുന്നു:
5 | സേവനം നിര്ത്താന് ഞൊട്ടുക.
6 | വീഡിയോ ഫ്രെയിം റേറ്റ്
7 | വീഡിയോ ബിറ്റേറ്റ്
8 | ഓഡിയോ സാംപ്ലിങ് റേറ്റ്
9 | ഓഡിയോ ബിറ്റേറ്റ്
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-pt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: serviço em execução
4 | RTSP ScreenCaster correndo em:
5 | Clique para parar o serviço.
6 | Taxa De Quadro De Vídeo
7 | Bitrato de vídeo
8 | Taxa De Amostragem De Áudio
9 | Bitrato de áudio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-el/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: εκτέλεση υπηρεσίας.
4 | RTSP ScreenCaster εκτέλεση σε::
5 | Πατήστε για τερματισμό της υπηρεσίας..
6 | Ρυθμός πλαισίου βίντεο
7 | Βίντεο bitrate
8 | Ρυθμός δειγματοληψίας ήχου
9 | Bitrate ήχου
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-hu/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: Szolgáltatás fut
4 | RTSP ScreenCaster Futás közben:
5 | Kattintson ide a szolgáltatás leállításához.
6 | Videó keret aránya
7 | Videó bitmutató
8 | Audio mintavételezési arány
9 | Audio Bitrate
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-sk/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: služba spustená
4 | RTSP ScreenCaster spustenie na:
5 | Kliknutím zastavte službu.
6 | Rýchlosť obrazového rámca
7 | Bitová rýchlosť videa
8 | Rýchlosť vzorkovania audia
9 | Zvukový dátový tok
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-uk/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: служба запуску
4 | RTSP ScreenCaster запущено на:
5 | Клацніть, щоб зупинити службу.
6 | Частота кадрів відео
7 | Бітова швидкість відео
8 | Частота дискретизації звуку
9 | Бітова швидкість звуку
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: servicio en ejecución
4 | RTSP ScreenCaster ejecución en:
5 | Pulse para detener el servicio.
6 | Velocidad de trama de vídeo
7 | Vídeo Bitrate
8 | Velocidad de muestreo de audio
9 | Bitasa de audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: servizio in esecuzione
4 | RTSP ScreenCaster esecuzione a:
5 | Fare clic per arrestare il servizio.
6 | Velocità frame video
7 | Bitrate video
8 | Velocità Di Campionamento Audio
9 | Bitrate audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-nl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: service wordt uitgevoerd
4 | RTSP ScreenCaster uitvoeren op:
5 | Klik om de service te stoppen.
6 | Snelheid video-frame
7 | Videobitsnelheid
8 | Geluidsbemonsteringsfrequentie
9 | Audio-bitsnelheid
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-sl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: izvajanje storitve
4 | RTSP ScreenCaster izvajanje na:
5 | Kliknite, da zaustavite storitev.
6 | Hitrost Slikatega Okvirja
7 | Bitna slika videa
8 | Stopnja Avdio Vzorčenja
9 | Zvočna bitna hitrost
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 | RTSP ScreenCaster: uruchamianie usługi
4 | RTSP ScreenCaster uruchamianie w:
5 | Kliknij, aby zatrzymać usługę.
6 | Szybkość transmisji wideo
7 | Bitrate wideo
8 | Częstotliwość próbkowania dźwięku
9 | Szybkość transmisji audio
10 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: '../constants.gradle'
2 | apply plugin: 'com.android.library'
3 |
4 | android {
5 | compileSdkVersion project.ext.compileSdkVersion
6 | buildToolsVersion project.ext.buildToolsVersion
7 |
8 | compileOptions {
9 | sourceCompatibility project.ext.javaVersion
10 | targetCompatibility project.ext.javaVersion
11 | }
12 |
13 | defaultConfig {
14 | minSdkVersion project.ext.minSdkVersion
15 | targetSdkVersion project.ext.targetSdkVersion
16 | }
17 |
18 | lintOptions {
19 | disable 'MissingTranslation'
20 | abortOnError true
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation 'com.serenegiant:common:' + project.ext.libGlUtils // (787 KB) https://mvnrepository.com/artifact/com.serenegiant/common?repo=jcenter
26 | }
27 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RTSP ScreenCaster
3 |
4 |
5 | RTSP ScreenCaster: service running
6 | RTSP ScreenCaster running at:
7 | Click to stop service.
8 |
9 |
10 | Video Frame Rate
11 | Video Bitrate
12 | Audio Sampling Rate
13 | Audio Bitrate
14 |
15 |
16 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: '../../constants.gradle'
2 | apply plugin: 'com.android.application'
3 |
4 | android {
5 | compileSdkVersion project.ext.compileSdkVersion
6 | buildToolsVersion project.ext.buildToolsVersion
7 |
8 | compileOptions {
9 | sourceCompatibility project.ext.javaVersion
10 | targetCompatibility project.ext.javaVersion
11 | }
12 |
13 | defaultConfig {
14 | minSdkVersion project.ext.minSdkVersion
15 | targetSdkVersion project.ext.targetSdkVersion
16 |
17 | applicationId "com.github.warren_bank.rtsp_screencaster"
18 | versionName project.ext.releaseVersion
19 | versionCode project.ext.releaseVersionCode
20 | }
21 |
22 | buildTypes {
23 | release {
24 | shrinkResources true
25 | minifyEnabled true
26 | proguardFiles = [
27 | "proguard-rules.txt",
28 | getDefaultProguardFile('proguard-android.txt')
29 | ]
30 | }
31 | debug {
32 | debuggable true
33 | jniDebuggable true
34 | }
35 | }
36 |
37 | lintOptions {
38 | disable 'MissingTranslation'
39 | abortOnError true
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation project(':libscreening')
45 | }
46 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/utils/WakeLockMgr.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster.utils;
2 |
3 | import android.content.Context;
4 | import android.net.wifi.WifiManager;
5 | import android.os.PowerManager;
6 |
7 | public final class WakeLockMgr {
8 | private static PowerManager.WakeLock wakeLock;
9 | private static WifiManager.WifiLock wifiLock;
10 |
11 | public static void acquire(Context context) {
12 | release();
13 |
14 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
15 | WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
16 |
17 | wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakeLock");
18 | wakeLock.setReferenceCounted(false);
19 | wakeLock.acquire();
20 |
21 | wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "WifiLock");
22 | wifiLock.setReferenceCounted(false);
23 | wifiLock.acquire();
24 | }
25 |
26 | public static void release() {
27 |
28 | if (wakeLock != null) {
29 | if (wakeLock.isHeld())
30 | wakeLock.release();
31 | wakeLock = null;
32 | }
33 |
34 | if (wifiLock != null) {
35 | if (wifiLock.isHeld())
36 | wifiLock.release();
37 | wifiLock = null;
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
18 |
19 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/layout/service_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
31 |
40 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtsp/RtcpDeinterleaver.java:
--------------------------------------------------------------------------------
1 | package net.majorkernelpanic.screening.rtsp;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.PipedInputStream;
6 | import java.io.PipedOutputStream;
7 |
8 | class RtcpDeinterleaver extends InputStream implements Runnable {
9 | public final static String TAG = "RtcpDeinterleaver";
10 |
11 | private IOException mIOException;
12 | private InputStream mInputStream;
13 | private PipedInputStream mPipedInputStream;
14 | private PipedOutputStream mPipedOutputStream;
15 | private byte[] mBuffer;
16 |
17 | public RtcpDeinterleaver(InputStream inputStream) {
18 | mInputStream = inputStream;
19 | mPipedInputStream = new PipedInputStream(4096);
20 | try {
21 | mPipedOutputStream = new PipedOutputStream(mPipedInputStream);
22 | } catch (IOException e) {}
23 | mBuffer = new byte[1024];
24 | new Thread(this).start();
25 | }
26 |
27 | @Override
28 | public void run() {
29 | try {
30 | while (true) {
31 | int len = mInputStream.read(mBuffer, 0, 1024);
32 | mPipedOutputStream.write(mBuffer, 0, len);
33 | }
34 | } catch (IOException e) {
35 | try {
36 | mPipedInputStream.close();
37 | } catch (IOException ignore) {}
38 | mIOException = e;
39 | }
40 | }
41 |
42 | @Override
43 | public int read(byte[] buffer) throws IOException {
44 | if (mIOException != null) {
45 | throw mIOException;
46 | }
47 | return mPipedInputStream.read(buffer);
48 | }
49 |
50 | @Override
51 | public int read(byte[] buffer, int offset, int length) throws IOException {
52 | if (mIOException != null) {
53 | throw mIOException;
54 | }
55 | return mPipedInputStream.read(buffer, offset, length);
56 | }
57 |
58 | @Override
59 | public int read() throws IOException {
60 | if (mIOException != null) {
61 | throw mIOException;
62 | }
63 | return mPipedInputStream.read();
64 | }
65 |
66 | @Override
67 | public void close() throws IOException {
68 | mInputStream.close();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### [RTSP ScreenCaster](https://github.com/warren-bank/Android-RTSP-ScreenCaster)
2 |
3 | Android app that serves an RTSP video stream to mirror the device's display screen.
4 |
5 | #### Overview:
6 |
7 | * There is no UI when the app starts.
8 | - It's a foreground service with a notification, which runs an RTSP server on port 6554.
9 | - The URL to access the video stream from a client is given in the notification message.
10 |
11 | #### Limitations:
12 |
13 | * Android doesn't support system audio capture
14 | - Bluetooth works nicely (in combination)
15 |
16 | #### Requirements:
17 |
18 | * Android 5.0 (API Level 21) or higher
19 | - [MediaProjectionManager](https://developer.android.com/reference/android/media/projection/MediaProjectionManager)
20 |
21 | #### Credits:
22 |
23 | * [libstreaming](https://github.com/fyhertz/libstreaming) library
24 | - author/copyright:
25 | * [Simon Guigui](https://github.com/fyhertz)
26 | - license:
27 | * [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) after commit [71558f6](https://github.com/fyhertz/libstreaming/commit/71558f6c587bbcc115718292df332d05d13651d0) on Mar 13, 2017, 3:47 PM PDT
28 | * [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.txt) prior
29 | - the `libscreening` library is forked from:
30 | * commit [f620117](https://github.com/fyhertz/libstreaming/tree/f6201177b4669bb9fe50dac9632510a8ad75ad7b) on Mar 13, 2017, 9:34 AM PDT
31 | * GPL-3.0 [license](https://github.com/fyhertz/libstreaming/blob/f6201177b4669bb9fe50dac9632510a8ad75ad7b/LICENSE)
32 | * [ScreenRecordingSample](https://github.com/saki4510t/ScreenRecordingSample)
33 | - author/copyright:
34 | * [saki](https://github.com/saki4510t)
35 | - license:
36 | * [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)
37 | - the [`DrawTask` class](https://github.com/warren-bank/Android-RTSP-ScreenCaster/blob/v01.01.00/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/DrawTask.java) in `libscreening` is forked from:
38 | * commit [9419559](https://github.com/saki4510t/ScreenRecordingSample/tree/9419559a193f2b90c8f86de82c975494a7b2f7d0) on Jun 28, 2020, 6:13 AM PDT
39 | * Apache-2.0 [license](https://github.com/saki4510t/ScreenRecordingSample/blob/9419559a193f2b90c8f86de82c975494a7b2f7d0/LICENSE)
40 |
41 | #### Legal:
42 |
43 | * copyright: [Warren Bank](https://github.com/warren-bank)
44 | * license: [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.txt)
45 |
--------------------------------------------------------------------------------
/notes/1-libstreaming/2. redesign.txt:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 |
3 | file:
4 | majorkernelpanic\streaming\video\VideoStream.java
5 |
6 | no-longer needed:
7 | import android.hardware.Camera;
8 | import android.hardware.Camera.CameraInfo;
9 |
10 | protected SurfaceView mSurfaceView = null;
11 | protected Camera mCamera = null;
12 | protected int mCameraId = 0;
13 |
14 | notes:
15 | - this class should be entirely replaced
16 | * remove logic regarding the camera, and the surfaceview on which to display the camera preview
17 | * add logic to record the screen, and stream its encoded video
18 | - encodeWithMediaCodecMethod2()
19 | * most of the MediaFormat and mMediaCodec logic can be replaced by:
20 | https://github.com/magicsih/AndroidScreenCaster/blob/v0.1/app/src/main/java/com/github/magicsih/androidscreencaster/service/ScreenCastService.java#L153
21 | * the important part to retain is at the end:
22 | // The packetizer encapsulates the bit stream in an RTP stream and send it over the network
23 | mPacketizer.setInputStream(new MediaCodecInputStream(mMediaCodec));
24 | mPacketizer.start();
25 | * best of both worlds..
26 |
27 | --------------------------------------------------------------------------------
28 |
29 | file:
30 | majorkernelpanic\streaming\video\H264Stream.java
31 | majorkernelpanic\streaming\Session.java
32 | majorkernelpanic\streaming\SessionBuilder.java
33 |
34 | notes:
35 | - these classes only need minor housekeeping to remove any lingering remnants of camera/surfaceview
36 |
37 | --------------------------------------------------------------------------------
38 |
39 | other things that are no-longer needed:
40 | - majorkernelpanic\streaming\rtsp\UriParser.java
41 | * libstreaming accepts querystring parameters in rtsp:// URLs
42 | - https://github.com/fyhertz/spydroid-ipcamera/wiki/Advanced-Use-of-Spydroid
43 | - this functionality isn't needed, so the URL parser can be removed
44 | - every request can be initialized with a new Session having dynamically determined values
45 | * width/height are determined by screen resolution
46 | * video format is hardcoded to h.264 ("video/avc")
47 | * bitrate and framerate can be hardcoded (for now),
48 | maybe read from Preferences later
49 |
50 | --------------------------------------------------------------------------------
51 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/H263Stream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.video;
22 |
23 | import net.majorkernelpanic.screening.Session;
24 | import net.majorkernelpanic.screening.SessionBuilder;
25 | import net.majorkernelpanic.screening.rtp.H263Packetizer;
26 |
27 | import android.media.MediaRecorder;
28 |
29 | import java.io.IOException;
30 |
31 | /**
32 | * A class for streaming H.263 from the display screen of an android device using RTP.
33 | * You should use a {@link Session} instantiated with {@link SessionBuilder} instead of using this class directly.
34 | * Call {@link #setDestinationAddress(InetAddress)}, {@link #setDestinationPorts(int)} and {@link #setVideoQuality(VideoQuality)}
35 | * to configure the stream. You can then call {@link #start()} to start the RTP stream.
36 | * Call {@link #stop()} to stop the stream.
37 | */
38 | public class H263Stream extends VideoStream {
39 |
40 | /**
41 | * Constructs the H.263 stream.
42 | * @throws IOException
43 | */
44 | public H263Stream() {
45 | super();
46 | mVideoEncoder = MediaRecorder.VideoEncoder.H263;
47 | mPacketizer = new H263Packetizer();
48 | }
49 |
50 | public synchronized void configure() throws IllegalStateException, IOException {
51 | super.configure();
52 | mMode = MODE_MEDIARECORDER_API;
53 | }
54 |
55 | /**
56 | * Returns a description of the stream using SDP. It can then be included in an SDP file.
57 | */
58 | public String getSessionDescription() {
59 | return "m=video "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" +
60 | "a=rtpmap:96 H263-1998/90000\r\n";
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/android-studio-project/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/audio/AudioQuality.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.audio;
22 |
23 | /**
24 | * A class that represents the quality of an audio stream.
25 | */
26 | public class AudioQuality {
27 |
28 | /** Default audio stream quality. */
29 | public final static AudioQuality DEFAULT_AUDIO_QUALITY = new AudioQuality(8000,32000);
30 |
31 | public int samplingRate = 0;
32 | public int bitRate = 0;
33 |
34 | /** Represents a quality for a video stream. */
35 | public AudioQuality() {}
36 |
37 | /**
38 | * Represents a quality for an audio stream.
39 | * @param samplingRate The sampling rate
40 | * @param bitRate The bitrate in bit per seconds
41 | */
42 | public AudioQuality(int samplingRate, int bitRate) {
43 | this.samplingRate = samplingRate;
44 | this.bitRate = bitRate;
45 | }
46 |
47 | public boolean equals(AudioQuality quality) {
48 | if (quality==null) return false;
49 | return (
50 | (quality.samplingRate == this.samplingRate) &&
51 | (quality.bitRate == this.bitRate)
52 | );
53 | }
54 |
55 | public AudioQuality clone() {
56 | return new AudioQuality(samplingRate, bitRate);
57 | }
58 |
59 | public static AudioQuality parseQuality(String str) {
60 | AudioQuality quality = DEFAULT_AUDIO_QUALITY.clone();
61 | if (str != null) {
62 | String[] config = str.split("-");
63 | try {
64 | quality.samplingRate = Integer.parseInt(config[0]);
65 | quality.bitRate = Integer.parseInt(config[1]);
66 | }
67 | catch (IndexOutOfBoundsException ignore) {}
68 | catch (NumberFormatException ignore) {}
69 | }
70 | return quality;
71 | }
72 |
73 | public String toString() {
74 | return ""+(samplingRate/1000)+" kHz, "+(bitRate/1000)+" kbps";
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/res/values/options.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - 20 fps
6 | - 30 fps
7 | - 45 fps
8 | - 60 fps
9 |
10 |
11 |
12 | - 20
13 | - 30
14 | - 45
15 | - 60
16 |
17 |
18 |
19 | - 0.5 Mbps
20 | - 1.0 Mbps
21 | - 2.0 Mbps
22 | - 4.0 Mbps
23 | - 6.0 Mbps
24 |
25 |
26 |
27 | - 524288
28 | - 1048576
29 | - 2097152
30 | - 4194304
31 | - 6291456
32 |
33 |
34 |
35 | - 4 kHz
36 | - 8 kHz
37 | - 16 kHz
38 | - 32 kHz
39 | - 44.1 kHz
40 | - 48 kHz
41 |
42 |
43 |
44 | - 4000
45 | - 8000
46 | - 16000
47 | - 32000
48 | - 44100
49 | - 48000
50 |
51 |
52 |
53 | - 8 kbps
54 | - 16 kbps
55 | - 24 kbps
56 | - 32 kbps
57 | - 40 kbps
58 | - 48 kbps
59 | - 56 kbps
60 | - 64 kbps
61 | - 80 kbps
62 | - 96 kbps
63 | - 112 kbps
64 | - 128 kbps
65 | - 144 kbps
66 | - 160 kbps
67 | - 192 kbps
68 | - 224 kbps
69 | - 256 kbps
70 | - 320 kbps
71 |
72 |
73 |
74 | - 8000
75 | - 16000
76 | - 24000
77 | - 32000
78 | - 40000
79 | - 48000
80 | - 56000
81 | - 64000
82 | - 80000
83 | - 96000
84 | - 112000
85 | - 128000
86 | - 144000
87 | - 160000
88 | - 192000
89 | - 224000
90 | - 256000
91 | - 320000
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/mp4/MP4Config.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.mp4;
22 |
23 | import android.util.Base64;
24 | import android.util.Log;
25 |
26 | import java.io.FileNotFoundException;
27 | import java.io.IOException;
28 |
29 | /**
30 | * Finds SPS & PPS parameters in mp4 file.
31 | */
32 | public class MP4Config {
33 |
34 | public final static String TAG = "MP4Config";
35 |
36 | private MP4Parser mp4Parser;
37 | private String mProfilLevel, mPPS, mSPS;
38 |
39 | public MP4Config(String profil, String sps, String pps) {
40 | mProfilLevel = profil;
41 | mPPS = pps;
42 | mSPS = sps;
43 | }
44 |
45 | public MP4Config(String sps, String pps) {
46 | mPPS = pps;
47 | mSPS = sps;
48 | mProfilLevel = MP4Parser.toHexString(Base64.decode(sps, Base64.NO_WRAP),1,3);
49 | }
50 |
51 | public MP4Config(byte[] sps, byte[] pps) {
52 | mPPS = Base64.encodeToString(pps, 0, pps.length, Base64.NO_WRAP);
53 | mSPS = Base64.encodeToString(sps, 0, sps.length, Base64.NO_WRAP);
54 | mProfilLevel = MP4Parser.toHexString(sps,1,3);
55 | }
56 |
57 | /**
58 | * Finds SPS & PPS parameters inside a .mp4.
59 | * @param path Path to the file to analyze
60 | * @throws IOException
61 | * @throws FileNotFoundException
62 | */
63 | public MP4Config (String path) throws IOException, FileNotFoundException {
64 |
65 | StsdBox stsdBox;
66 |
67 | // We open the mp4 file and parse it
68 | try {
69 | mp4Parser = MP4Parser.parse(path);
70 | } catch (IOException ignore) {
71 | // Maybe enough of the file has been parsed and we can get the stsd box
72 | }
73 |
74 | // We find the stsdBox
75 | stsdBox = mp4Parser.getStsdBox();
76 | mPPS = stsdBox.getB64PPS();
77 | mSPS = stsdBox.getB64SPS();
78 | mProfilLevel = stsdBox.getProfileLevel();
79 |
80 | mp4Parser.close();
81 |
82 | }
83 |
84 | public String getProfileLevel() {
85 | return mProfilLevel;
86 | }
87 |
88 | public String getB64PPS() {
89 | Log.d(TAG, "PPS: "+mPPS);
90 | return mPPS;
91 | }
92 |
93 | public String getB64SPS() {
94 | Log.d(TAG, "SPS: "+mSPS);
95 | return mSPS;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/audio/AMRNBStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.audio;
22 |
23 | import net.majorkernelpanic.screening.SessionBuilder;
24 | import net.majorkernelpanic.screening.rtp.AMRNBPacketizer;
25 |
26 | import android.media.MediaRecorder;
27 | import android.service.textservice.SpellCheckerService.Session;
28 |
29 | import java.io.IOException;
30 | import java.lang.reflect.Field;
31 |
32 | /**
33 | * A class for streaming AAC from the microphone of an android device using RTP.
34 | * You should use a {@link Session} instantiated with {@link SessionBuilder} instead of using this class directly.
35 | * Call {@link #setDestinationAddress(InetAddress)}, {@link #setDestinationPorts(int)} and {@link #setAudioQuality(AudioQuality)}
36 | * to configure the stream. You can then call {@link #start()} to start the RTP stream.
37 | * Call {@link #stop()} to stop the stream.
38 | */
39 | public class AMRNBStream extends AudioStream {
40 |
41 | public AMRNBStream() {
42 | super();
43 |
44 | mPacketizer = new AMRNBPacketizer();
45 |
46 | setAudioSource(MediaRecorder.AudioSource.MIC);
47 |
48 | try {
49 | // RAW_AMR was deprecated in API level 16.
50 | Field deprecatedName = MediaRecorder.OutputFormat.class.getField("RAW_AMR");
51 | setOutputFormat(deprecatedName.getInt(null));
52 | } catch (Exception e) {
53 | setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
54 | }
55 |
56 | setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
57 | }
58 |
59 | /**
60 | * Starts the stream.
61 | */
62 | public synchronized void start() throws IllegalStateException, IOException {
63 | if (!mStreaming) {
64 | configure();
65 | super.start();
66 | }
67 | }
68 |
69 | public synchronized void configure() throws IllegalStateException, IOException {
70 | super.configure();
71 | mMode = MODE_MEDIARECORDER_API;
72 | }
73 |
74 | /**
75 | * Returns a description of the stream using SDP. It can then be included in an SDP file.
76 | */
77 | public String getSessionDescription() {
78 | return "m=audio "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" +
79 | "a=rtpmap:96 AMR/"+mQuality.samplingRate+"\r\n" +
80 | "a=fmtp:96 octet-align=1;\r\n";
81 | }
82 |
83 | @Override
84 | protected void encodeWithMediaCodec() throws IOException {
85 | super.encodeWithMediaRecorder();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/utils/NetworkUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster.utils;
2 |
3 | import java.net.Inet4Address;
4 | import java.net.InetAddress;
5 | import java.net.NetworkInterface;
6 | import java.net.SocketException;
7 | import java.util.Enumeration;
8 |
9 | import android.content.Context;
10 | import android.net.ConnectivityManager;
11 | import android.net.NetworkInfo;
12 | import android.net.wifi.WifiInfo;
13 | import android.net.wifi.WifiManager;
14 |
15 | public class NetworkUtils {
16 |
17 | public synchronized static Inet4Address getLocalIpAddress() {
18 | try {
19 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
20 | NetworkInterface intf = en.nextElement();
21 |
22 | if (!intf.supportsMulticast())
23 | continue;
24 |
25 | for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
26 | InetAddress inetAddress = enumIpAddr.nextElement();
27 |
28 | if (!inetAddress.isLoopbackAddress()) {
29 | if (inetAddress instanceof Inet4Address) {
30 | return ((Inet4Address) inetAddress);
31 | }
32 | }
33 | }
34 | }
35 | }
36 | catch (SocketException ex) {
37 | }
38 | return null;
39 | }
40 |
41 | public synchronized static String[] getMACAddress(InetAddress ia) throws Exception {
42 | //Obtain the network interface object (that is, the network card), and get the mac address. The mac address exists in a byte array.
43 | byte[] mac = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
44 |
45 | //The following code assembles the mac address into a String
46 | String[] str_array = new String[2];
47 | StringBuffer sb1 = new StringBuffer();
48 | StringBuffer sb2 = new StringBuffer();
49 |
50 | for (int i = 0; i < mac.length; i++) {
51 | if (i != 0) {
52 | sb1.append(":");
53 | }
54 | //mac[i] & 0xFF ..to convert bytes into positive integers
55 | String s = Integer.toHexString(mac[i] & 0xFF);
56 | sb1.append(s.length() == 1 ? 0 + s : s);
57 | sb2.append(s.length() == 1 ? 0 + s : s);
58 | }
59 | //Change all lowercase letters of the string to regular mac addresses and return
60 | str_array[0] = sb1.toString();
61 | str_array[1] = sb2.toString();
62 | return str_array;
63 | //return sb1.toString().toUpperCase();
64 | }
65 |
66 | public static String getLocalIp(Context context) {
67 | //Get wifi service
68 | WifiManager wifiManager = (WifiManager) context
69 | .getSystemService(Context.WIFI_SERVICE);
70 | //Determine if wifi is on
71 | if (!wifiManager.isWifiEnabled()) {
72 | wifiManager.setWifiEnabled(true);
73 | }
74 | WifiInfo wifiInfo = wifiManager.getConnectionInfo();
75 | int ipAddress = wifiInfo.getIpAddress();
76 | String ip = intToIp(ipAddress);
77 |
78 | return ip;
79 | }
80 |
81 | public static boolean isWifiConnected(Context context) {
82 | ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
83 | //Get status
84 | NetworkInfo.State wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
85 | //Determine the conditions of wifi connection
86 | if (wifi == NetworkInfo.State.CONNECTED)
87 | return true;
88 | else
89 | return false;
90 | }
91 |
92 | private static String intToIp(int i) {
93 | return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening-examples/RTSP-ScreenCaster/src/main/java/com/github/warren_bank/rtsp_screencaster/utils/RuntimePermissions.java:
--------------------------------------------------------------------------------
1 | package com.github.warren_bank.rtsp_screencaster.utils;
2 |
3 | import net.majorkernelpanic.screening.SessionBuilder;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.content.pm.PackageManager;
8 | import android.os.Build;
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 |
12 | public final class RuntimePermissions {
13 |
14 | public static interface Callback {
15 | public void onPermissionsCancelled();
16 | public void onPermissionsGranted();
17 | public void onPermissionsDenied(String[] permissions);
18 | }
19 |
20 | public static int REQUEST_CODE = 1;
21 |
22 | private static ArrayList REQUIRED_PERMISSIONS = null;
23 |
24 | public static String[] getMissingPermissions(Context context) {
25 | return getMissingPermissions(context, null);
26 | }
27 |
28 | public static String[] getMissingPermissions(Context context, SessionBuilder builder) {
29 | if (Build.VERSION.SDK_INT < 23)
30 | return new String[0];
31 |
32 | REQUIRED_PERMISSIONS = new ArrayList(
33 | Arrays.asList(
34 | SessionBuilder.getRequiredPermissions(builder)
35 | )
36 | );
37 |
38 | if (REQUIRED_PERMISSIONS.isEmpty())
39 | return new String[0];
40 |
41 | ArrayList missingPermissions = new ArrayList<>();
42 | PackageManager pkgMngr = context.getPackageManager();
43 | String pkgName = context.getPackageName();
44 |
45 | for (String permission : REQUIRED_PERMISSIONS) {
46 | if (pkgMngr.checkPermission(permission, pkgName) != PackageManager.PERMISSION_GRANTED)
47 | missingPermissions.add(permission);
48 | }
49 |
50 | return missingPermissions.toArray(new String[missingPermissions.size()]);
51 | }
52 |
53 | public static boolean isEnabled(Activity activity) {
54 | if (Build.VERSION.SDK_INT < 23)
55 | return true;
56 |
57 | String[] missingPermissions = getMissingPermissions(activity);
58 |
59 | if (missingPermissions.length == 0)
60 | return true;
61 |
62 | activity.requestPermissions(missingPermissions, REQUEST_CODE);
63 | return false;
64 | }
65 |
66 | public static void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults, Callback cb) {
67 | if (
68 | (requestCode != REQUEST_CODE)
69 | || (null == cb)
70 | || (null == REQUIRED_PERMISSIONS)
71 | ) {
72 | return;
73 | }
74 |
75 | if (grantResults.length == 0) {
76 | if (permissions.length == 0) {
77 | // no "dangerous" permissions are needed
78 | cb.onPermissionsGranted();
79 | }
80 | else {
81 | // request was cancelled. show the prompts again.
82 | cb.onPermissionsCancelled();
83 | }
84 | }
85 | else {
86 | ArrayList deniedPermissions = new ArrayList<>();
87 |
88 | for (int i=0; i < grantResults.length; i++) {
89 | if (
90 | (grantResults[i] != PackageManager.PERMISSION_GRANTED) &&
91 | REQUIRED_PERMISSIONS.contains(permissions[i])
92 | ) {
93 | // a mandatory permission is not granted
94 | deniedPermissions.add(permissions[i]);
95 | }
96 | }
97 |
98 | if (deniedPermissions.isEmpty()) {
99 | cb.onPermissionsGranted();
100 | }
101 | else {
102 | cb.onPermissionsDenied(
103 | deniedPermissions.toArray(new String[deniedPermissions.size()])
104 | );
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/VideoQuality.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.video;
22 |
23 | /**
24 | * A class that represents the quality of a video stream.
25 | * It contains the resolution, the framerate (in fps) and the bitrate (in bps) of the stream.
26 | */
27 | public class VideoQuality {
28 | public final static String TAG = "VideoQuality";
29 |
30 | /** Default video stream quality. */
31 | public static VideoQuality DEFAULT_VIDEO_QUALITY = null;
32 |
33 | public static void init(int screenWidth, int screenHeight, int screenDpi) {
34 | DEFAULT_VIDEO_QUALITY = new VideoQuality(screenWidth, screenHeight, screenDpi, /* framerate= */ 20, /* bitrate= */ 524288);
35 | }
36 |
37 | public int screenWidth = 0;
38 | public int screenHeight = 0;
39 | public int screenDpi = 0;
40 | public int framerate = 0;
41 | public int bitrate = 0;
42 |
43 | /** Represents a quality for a video stream. */
44 | public VideoQuality() {}
45 |
46 | /**
47 | * Represents a quality for a video stream.
48 | * @param framerate The framerate in frame per seconds
49 | * @param bitrate The bitrate in bit per seconds
50 | */
51 | public VideoQuality(int screenWidth, int screenHeight, int screenDpi, int framerate, int bitrate) {
52 | this.screenWidth = screenWidth;
53 | this.screenHeight = screenHeight;
54 | this.screenDpi = screenDpi;
55 | this.framerate = framerate;
56 | this.bitrate = bitrate;
57 | }
58 |
59 | public boolean equals(VideoQuality quality) {
60 | if (quality==null) return false;
61 | return (
62 | (quality.screenWidth == this.screenWidth) &&
63 | (quality.screenHeight == this.screenHeight) &&
64 | (quality.screenDpi == this.screenDpi) &&
65 | (quality.framerate == this.framerate) &&
66 | (quality.bitrate == this.bitrate)
67 | );
68 | }
69 |
70 | public VideoQuality clone() {
71 | return new VideoQuality(screenWidth, screenHeight, screenDpi, framerate, bitrate);
72 | }
73 |
74 | public static VideoQuality extend(int framerate, int bitrate) {
75 | VideoQuality quality = null;
76 |
77 | if (DEFAULT_VIDEO_QUALITY == null)
78 | return quality;
79 |
80 | quality = DEFAULT_VIDEO_QUALITY.clone();
81 | quality.framerate = framerate;
82 | quality.bitrate = bitrate;
83 |
84 | return quality;
85 | }
86 |
87 | public static VideoQuality parseQuality(String str) {
88 | VideoQuality quality = null;
89 |
90 | if (DEFAULT_VIDEO_QUALITY == null)
91 | return quality;
92 |
93 | quality = DEFAULT_VIDEO_QUALITY.clone();
94 | if (str == null)
95 | return quality;
96 |
97 | String[] config = str.split("-");
98 | try {
99 | quality.framerate = Integer.parseInt(config[0]);
100 | quality.bitrate = Integer.parseInt(config[1]);
101 | }
102 | catch (IndexOutOfBoundsException ignore) {}
103 | catch (NumberFormatException ignore) {}
104 |
105 | return quality;
106 | }
107 |
108 | public String toString() {
109 | return screenWidth+"x"+screenHeight+" px, "+screenDpi+" dpi, "+framerate+" fps, "+(bitrate/1024/1024)+" Mbps";
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtp/MediaCodecInputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtp;
22 |
23 | import android.annotation.SuppressLint;
24 | import android.media.MediaCodec;
25 | import android.media.MediaCodec.BufferInfo;
26 | import android.media.MediaFormat;
27 | import android.util.Log;
28 |
29 | import java.io.IOException;
30 | import java.io.InputStream;
31 | import java.nio.ByteBuffer;
32 |
33 | /**
34 | * An InputStream that uses data from a MediaCodec.
35 | * The purpose of this class is to interface existing RTP packetizers of
36 | * libscreening with the new MediaCodec API. This class is not thread safe !
37 | */
38 | @SuppressLint("NewApi")
39 | public class MediaCodecInputStream extends InputStream {
40 | public final String TAG = "MediaCodecInputStream";
41 |
42 | private MediaCodec mMediaCodec = null;
43 | private BufferInfo mBufferInfo = new BufferInfo();
44 | private ByteBuffer mBuffer = null;
45 | private int mIndex = -1;
46 | private boolean mClosed = false;
47 |
48 | public MediaCodecInputStream(MediaCodec mediaCodec) {
49 | mMediaCodec = mediaCodec;
50 | }
51 |
52 | @Override
53 | public void close() {
54 | mClosed = true;
55 | }
56 |
57 | @Override
58 | public int read() throws IOException {
59 | return 0;
60 | }
61 |
62 | @Override
63 | public int read(byte[] buffer, int offset, int length) throws IOException {
64 | int min = 0;
65 |
66 | try {
67 | if (mBuffer==null) {
68 | while (!Thread.interrupted() && !mClosed) {
69 | mIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 500000);
70 | if (mIndex>=0 ){
71 | //Log.d(TAG,"Index: "+mIndex+" Time: "+mBufferInfo.presentationTimeUs+" size: "+mBufferInfo.size);
72 | mBuffer = mMediaCodec.getOutputBuffer(mIndex);
73 | mBuffer.position(0);
74 | break;
75 | } else if (mIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
76 | Log.v(TAG,"Output buffers have changed...");
77 | } else if (mIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
78 | Log.i(TAG, "Output format has changed to: " + mMediaCodec.getOutputFormat().toString());
79 | } else if (mIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
80 | Log.v(TAG,"No buffer available...");
81 | } else {
82 | Log.e(TAG,"Message: "+mIndex);
83 | //return 0;
84 | }
85 | }
86 | }
87 |
88 | if (!Thread.interrupted()) {
89 | if (mClosed) throw new IOException("This InputStream was closed");
90 |
91 | min = length < mBufferInfo.size - mBuffer.position() ? length : mBufferInfo.size - mBuffer.position();
92 | mBuffer.get(buffer, offset, min);
93 | if (mBuffer.position()>=mBufferInfo.size) {
94 | mMediaCodec.releaseOutputBuffer(mIndex, false);
95 | mBuffer = null;
96 | }
97 | }
98 | } catch (RuntimeException e) {
99 | e.printStackTrace();
100 | }
101 |
102 | return min;
103 | }
104 |
105 | public int available() {
106 | if (mBuffer != null)
107 | return mBufferInfo.size - mBuffer.position();
108 | else
109 | return 0;
110 | }
111 |
112 | public BufferInfo getLastBufferInfo() {
113 | return mBufferInfo;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/Stream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening;
22 |
23 | import java.io.IOException;
24 | import java.io.OutputStream;
25 | import java.net.InetAddress;
26 |
27 | /**
28 | * An interface that represents a Stream.
29 | */
30 | public interface Stream {
31 |
32 | /**
33 | * Configures the stream. You need to call this before calling {@link #getSessionDescription()}
34 | * to apply your configuration of the stream.
35 | */
36 | public void configure() throws IllegalStateException, IOException;
37 |
38 | /**
39 | * Starts the stream.
40 | * This method can only be called after {@link Stream#configure()}.
41 | */
42 | public void start() throws IllegalStateException, IOException;
43 |
44 | /**
45 | * Stops the stream.
46 | */
47 | public void stop();
48 |
49 | /**
50 | * Sets the Time To Live of packets sent over the network.
51 | * @param ttl The time to live
52 | * @throws IOException
53 | */
54 | public void setTimeToLive(int ttl) throws IOException;
55 |
56 | /**
57 | * Sets the destination ip address of the stream.
58 | * @param dest The destination address of the stream
59 | */
60 | public void setDestinationAddress(InetAddress dest);
61 |
62 | /**
63 | * Sets the destination ports of the stream.
64 | * If an odd number is supplied for the destination port then the next
65 | * lower even number will be used for RTP and it will be used for RTCP.
66 | * If an even number is supplied, it will be used for RTP and the next odd
67 | * number will be used for RTCP.
68 | * @param dport The destination port
69 | */
70 | public void setDestinationPorts(int dport);
71 |
72 | /**
73 | * Sets the destination ports of the stream.
74 | * @param rtpPort Destination port that will be used for RTP
75 | * @param rtcpPort Destination port that will be used for RTCP
76 | */
77 | public void setDestinationPorts(int rtpPort, int rtcpPort);
78 |
79 | /**
80 | * If a TCP is used as the transport protocol for the RTP session,
81 | * the output stream to which RTP packets will be written to must
82 | * be specified with this method.
83 | */
84 | public void setOutputStream(OutputStream stream, byte channelIdentifier);
85 |
86 | /**
87 | * Returns a pair of source ports, the first one is the
88 | * one used for RTP and the second one is used for RTCP.
89 | **/
90 | public int[] getLocalPorts();
91 |
92 | /**
93 | * Returns a pair of destination ports, the first one is the
94 | * one used for RTP and the second one is used for RTCP.
95 | **/
96 | public int[] getDestinationPorts();
97 |
98 | /**
99 | * Returns the SSRC of the underlying {@link net.majorkernelpanic.screening.rtp.RtpSocket}.
100 | * @return the SSRC of the stream.
101 | */
102 | public int getSSRC();
103 |
104 | /**
105 | * Returns an approximation of the bit rate consumed by the stream in bit per seconde.
106 | */
107 | public long getBitrate();
108 |
109 | /**
110 | * Returns a description of the stream using SDP.
111 | * This method can only be called after {@link Stream#configure()}.
112 | * @throws IllegalStateException Thrown when {@link Stream#configure()} wa not called.
113 | */
114 | public String getSessionDescription() throws IllegalStateException;
115 |
116 | public boolean isStreaming();
117 | }
118 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtp/AACLATMPacketizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtp;
22 |
23 | import android.annotation.SuppressLint;
24 | import android.media.MediaCodec.BufferInfo;
25 | import android.os.SystemClock;
26 | import android.util.Log;
27 |
28 | import java.io.IOException;
29 |
30 | /**
31 | * RFC 3640.
32 | *
33 | * Encapsulates AAC Access Units in RTP packets as specified in the RFC 3640.
34 | * This packetizer is used by the AACStream class in conjunction with the
35 | * MediaCodec API introduced in Android 4.1 (API Level 16).
36 | *
37 | */
38 | @SuppressLint("NewApi")
39 | public class AACLATMPacketizer extends AbstractPacketizer implements Runnable {
40 | private final static String TAG = "AACLATMPacketizer";
41 |
42 | private Thread t;
43 |
44 | public AACLATMPacketizer() {
45 | super();
46 | socket.setCacheSize(0);
47 | }
48 |
49 | public void start() {
50 | if (t==null) {
51 | t = new Thread(this);
52 | t.start();
53 | }
54 | }
55 |
56 | public void stop() {
57 | if (t != null) {
58 | try {
59 | is.close();
60 | } catch (IOException ignore) {}
61 | t.interrupt();
62 | try {
63 | t.join();
64 | } catch (InterruptedException e) {}
65 | t = null;
66 | }
67 | }
68 |
69 | public void setSamplingRate(int samplingRate) {
70 | socket.setClockFrequency(samplingRate);
71 | }
72 |
73 | @SuppressLint("NewApi")
74 | public void run() {
75 | Log.d(TAG,"AAC LATM packetizer started !");
76 |
77 | int length = 0;
78 | long oldts;
79 | BufferInfo bufferInfo;
80 |
81 | try {
82 | while (!Thread.interrupted()) {
83 | buffer = socket.requestBuffer();
84 | length = is.read(buffer, rtphl+4, MAXPACKETSIZE-(rtphl+4));
85 |
86 | if (length>0) {
87 |
88 | bufferInfo = ((MediaCodecInputStream)is).getLastBufferInfo();
89 | //Log.d(TAG,"length: "+length+" ts: "+bufferInfo.presentationTimeUs);
90 | oldts = ts;
91 | ts = bufferInfo.presentationTimeUs*1000;
92 |
93 | // Seems to happen sometimes
94 | if (oldts>ts) {
95 | socket.commitBuffer();
96 | continue;
97 | }
98 |
99 | socket.markNextPacket();
100 | socket.updateTimestamp(ts);
101 |
102 | // AU-headers-length field: contains the size in bits of a AU-header
103 | // 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta
104 | // 13 bits will be enough because ADTS uses 13 bits for frame length
105 | buffer[rtphl] = 0;
106 | buffer[rtphl+1] = 0x10;
107 |
108 | // AU-size
109 | buffer[rtphl+2] = (byte) (length>>5);
110 | buffer[rtphl+3] = (byte) (length<<3);
111 |
112 | // AU-Index
113 | buffer[rtphl+3] &= 0xF8;
114 | buffer[rtphl+3] |= 0x00;
115 |
116 | send(rtphl+length+4);
117 |
118 | } else {
119 | socket.commitBuffer();
120 | }
121 | }
122 | } catch (IOException e) {
123 | } catch (ArrayIndexOutOfBoundsException e) {
124 | Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
125 | e.printStackTrace();
126 | } catch (InterruptedException ignore) {}
127 |
128 | Log.d(TAG,"AAC LATM packetizer stopped !");
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtp/AMRNBPacketizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtp;
22 |
23 | import android.util.Log;
24 |
25 | import java.io.IOException;
26 |
27 | /**
28 | *
29 | * RFC 3267.
30 | *
31 | * AMR Streaming over RTP.
32 | *
33 | * Must be fed with an InputStream containing raw AMR NB
34 | * Stream must begin with a 6 bytes long header: "#!AMR\n", it will be skipped
35 | *
36 | */
37 | public class AMRNBPacketizer extends AbstractPacketizer implements Runnable {
38 | public final static String TAG = "AMRNBPacketizer";
39 |
40 | private final int AMR_HEADER_LENGTH = 6; // "#!AMR\n"
41 | private static final int AMR_FRAME_HEADER_LENGTH = 1; // Each frame has a short header
42 | private static final int[] sFrameBits = {95, 103, 118, 134, 148, 159, 204, 244};
43 | private int samplingRate = 8000;
44 |
45 | private Thread t;
46 |
47 | public AMRNBPacketizer() {
48 | super();
49 | socket.setClockFrequency(samplingRate);
50 | }
51 |
52 | public void start() {
53 | if (t==null) {
54 | t = new Thread(this);
55 | t.start();
56 | }
57 | }
58 |
59 | public void stop() {
60 | if (t != null) {
61 | try {
62 | is.close();
63 | } catch (IOException ignore) {}
64 | t.interrupt();
65 | try {
66 | t.join();
67 | } catch (InterruptedException e) {}
68 | t = null;
69 | }
70 | }
71 |
72 | public void run() {
73 | int frameLength, frameType;
74 | long now = System.nanoTime(), oldtime = now;
75 | byte[] header = new byte[AMR_HEADER_LENGTH];
76 |
77 | try {
78 | // Skip raw AMR header
79 | fill(header,0,AMR_HEADER_LENGTH);
80 |
81 | if (header[5] != '\n') {
82 | Log.e(TAG,"Bad header ! AMR not correcty supported by the phone !");
83 | return;
84 | }
85 |
86 | while (!Thread.interrupted()) {
87 |
88 | buffer = socket.requestBuffer();
89 | buffer[rtphl] = (byte) 0xF0;
90 |
91 | // First we read the frame header
92 | fill(buffer, rtphl+1,AMR_FRAME_HEADER_LENGTH);
93 |
94 | // Then we calculate the frame payload length
95 | frameType = (Math.abs(buffer[rtphl + 1]) >> 3) & 0x0f;
96 | frameLength = (sFrameBits[frameType]+7)/8;
97 |
98 | // And we read the payload
99 | fill(buffer, rtphl+2,frameLength);
100 |
101 | //Log.d(TAG,"Frame length: "+frameLength+" frameType: "+frameType);
102 |
103 | // RFC 3267 Page 14: "For AMR, the sampling frequency is 8 kHz"
104 | // FIXME: Is this really always the case ??
105 | ts += 160L*1000000000L/samplingRate; //stats.average();
106 | socket.updateTimestamp(ts);
107 | socket.markNextPacket();
108 |
109 | //Log.d(TAG,"expected: "+ expected + " measured: "+measured);
110 |
111 | send(rtphl+1+AMR_FRAME_HEADER_LENGTH+frameLength);
112 | }
113 |
114 | } catch (IOException e) {
115 | } catch (InterruptedException e) {}
116 |
117 | Log.d(TAG,"AMR packetizer stopped !");
118 | }
119 |
120 | private int fill(byte[] buffer, int offset,int length) throws IOException {
121 | int sum = 0, len;
122 | while (sum>2;
98 | //Log.d(TAG,"j: "+j+" buffer: "+printBuffer(rtphl, rtphl+5)+" tr: "+tr);
99 | if (firstFragment) {
100 | // This is the first fragment of the frame -> header is set to 0x0400
101 | buffer[rtphl] = 4;
102 | firstFragment = false;
103 | } else {
104 | buffer[rtphl] = 0;
105 | }
106 | if (j>0) {
107 | // We have found the end of the frame
108 | stats.push(duration);
109 | ts+= stats.average(); duration = 0;
110 | //Log.d(TAG,"End of frame ! duration: "+stats.average());
111 | // The last fragment of a frame has to be marked
112 | socket.markNextPacket();
113 | send(j);
114 | nextBuffer = socket.requestBuffer();
115 | System.arraycopy(buffer,j+2,nextBuffer,rtphl+2,MAXPACKETSIZE-j-2);
116 | buffer = nextBuffer;
117 | j = MAXPACKETSIZE-j-2;
118 | firstFragment = true;
119 | } else {
120 | // We have not found the beginning of another frame
121 | // The whole packet is a fragment of a frame
122 | send(MAXPACKETSIZE);
123 | }
124 | }
125 | } catch (IOException e) {
126 | } catch (InterruptedException e) {}
127 |
128 | Log.d(TAG,"H263 Packetizer stopped !");
129 | }
130 |
131 | private int fill(int offset,int length) throws IOException {
132 | int sum = 0, len;
133 |
134 | while (sum0) {
132 | System.arraycopy(data, 0, mBuffer, 0, mSize);
133 | System.arraycopy(data, mSize, mBuffer, mSize+mYPadding, mSize/2);
134 | return mBuffer;
135 | }
136 | return data;
137 | }
138 | } else {
139 | if (mSliceHeight==mHeight && mStride==mWidth) {
140 | // De-interleave U and V
141 | if (!mPanesReversed) {
142 | for (int i = 0; i < mSize/4; i+=1) {
143 | mBuffer[i] = data[mSize+2*i+1];
144 | mBuffer[mSize/4+i] = data[mSize+2*i];
145 | }
146 | } else {
147 | for (int i = 0; i < mSize/4; i+=1) {
148 | mBuffer[i] = data[mSize+2*i];
149 | mBuffer[mSize/4+i] = data[mSize+2*i+1];
150 | }
151 | }
152 | if (mYPadding == 0) {
153 | System.arraycopy(mBuffer, 0, data, mSize, mSize/2);
154 | } else {
155 | System.arraycopy(data, 0, mBuffer, 0, mSize);
156 | System.arraycopy(mBuffer, 0, mBuffer, mSize+mYPadding, mSize/2);
157 | return mBuffer;
158 | }
159 | return data;
160 | }
161 | }
162 |
163 | return data;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtp/AbstractPacketizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtp;
22 |
23 | import net.majorkernelpanic.screening.rtcp.SenderReport;
24 |
25 | import java.io.IOException;
26 | import java.io.InputStream;
27 | import java.io.OutputStream;
28 | import java.net.InetAddress;
29 | import java.util.Random;
30 |
31 | /**
32 | *
33 | * Each packetizer inherits from this one and therefore uses RTP and UDP.
34 | *
35 | */
36 | abstract public class AbstractPacketizer {
37 |
38 | protected static final int rtphl = RtpSocket.RTP_HEADER_LENGTH;
39 |
40 | // Maximum size of RTP packets
41 | protected final static int MAXPACKETSIZE = RtpSocket.MTU-28;
42 |
43 | protected RtpSocket socket = null;
44 | protected InputStream is = null;
45 | protected byte[] buffer;
46 |
47 | protected long ts = 0;
48 |
49 | public AbstractPacketizer() {
50 | int ssrc = new Random().nextInt();
51 | ts = new Random().nextInt();
52 | socket = new RtpSocket();
53 | socket.setSSRC(ssrc);
54 | }
55 |
56 | public RtpSocket getRtpSocket() {
57 | return socket;
58 | }
59 |
60 | public void setSSRC(int ssrc) {
61 | socket.setSSRC(ssrc);
62 | }
63 |
64 | public int getSSRC() {
65 | return socket.getSSRC();
66 | }
67 |
68 | public void setInputStream(InputStream is) {
69 | this.is = is;
70 | }
71 |
72 | public void setTimeToLive(int ttl) throws IOException {
73 | socket.setTimeToLive(ttl);
74 | }
75 |
76 | /**
77 | * Sets the destination of the stream.
78 | * @param dest The destination address of the stream
79 | * @param rtpPort Destination port that will be used for RTP
80 | * @param rtcpPort Destination port that will be used for RTCP
81 | */
82 | public void setDestination(InetAddress dest, int rtpPort, int rtcpPort) {
83 | socket.setDestination(dest, rtpPort, rtcpPort);
84 | }
85 |
86 | /** Starts the packetizer. */
87 | public abstract void start();
88 |
89 | /** Stops the packetizer. */
90 | public abstract void stop();
91 |
92 | /** Updates data for RTCP SR and sends the packet. */
93 | protected void send(int length) throws IOException {
94 | socket.commitBuffer(length);
95 | }
96 |
97 | /** For debugging purposes. */
98 | protected static String printBuffer(byte[] buffer, int start,int end) {
99 | String str = "";
100 | for (int i=start;iperiod) {
135 | elapsed = 0;
136 | long now = System.nanoTime();
137 | if (!initoffset || (now - start < 0)) {
138 | start = now;
139 | duration = 0;
140 | initoffset = true;
141 | }
142 | // Prevents drifting issues by comparing the real duration of the
143 | // stream with the sum of all temporal lengths of RTP packets.
144 | value += (now - start) - duration;
145 | //Log.d(TAG, "sum1: "+duration/1000000+" sum2: "+(now-start)/1000000+" drift: "+((now-start)-duration)/1000000+" v: "+value/1000000);
146 | }
147 | if (c<5) {
148 | // We ignore the first 20 measured values because they may not be accurate
149 | c++;
150 | m = value;
151 | } else {
152 | m = (m*q+value)/(q+1);
153 | if (q \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/hw/CodecManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.hw;
22 |
23 | import android.annotation.SuppressLint;
24 | import android.media.MediaCodecInfo;
25 | import android.media.MediaCodecList;
26 | import android.util.Log;
27 |
28 | import java.util.ArrayList;
29 | import java.util.HashSet;
30 | import java.util.Set;
31 |
32 | @SuppressLint("InlinedApi")
33 | public class CodecManager {
34 |
35 | public final static String TAG = "CodecManager";
36 |
37 | public static final int[] SUPPORTED_COLOR_FORMATS = {
38 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
39 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar,
40 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
41 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar,
42 | MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar
43 | };
44 |
45 | private static Codec[] sEncoders = null;
46 | private static Codec[] sDecoders = null;
47 |
48 | static class Codec {
49 | public Codec(String name, Integer[] formats) {
50 | this.name = name;
51 | this.formats = formats;
52 | }
53 | public String name;
54 | public Integer[] formats;
55 | }
56 |
57 | /**
58 | * Lists all encoders that claim to support a color format that we know how to use.
59 | * @return A list of those encoders
60 | */
61 | @SuppressLint("NewApi")
62 | public synchronized static Codec[] findEncodersForMimeType(String mimeType) {
63 | if (sEncoders != null) return sEncoders;
64 |
65 | ArrayList encoders = new ArrayList();
66 |
67 | // We loop through the encoders, apparently this can take up to a sec (testes on a GS3)
68 | for(int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--){
69 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
70 | if (!codecInfo.isEncoder()) continue;
71 |
72 | String[] types = codecInfo.getSupportedTypes();
73 | for (int i = 0; i < types.length; i++) {
74 | if (types[i].equalsIgnoreCase(mimeType)) {
75 | try {
76 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
77 | Set formats = new HashSet();
78 |
79 | // And through the color formats supported
80 | for (int k = 0; k < capabilities.colorFormats.length; k++) {
81 | int format = capabilities.colorFormats[k];
82 |
83 | for (int l=0;l decoders = new ArrayList();
111 |
112 | // We loop through the decoders, apparently this can take up to a sec (testes on a GS3)
113 | for(int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--){
114 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
115 | if (codecInfo.isEncoder()) continue;
116 |
117 | String[] types = codecInfo.getSupportedTypes();
118 | for (int i = 0; i < types.length; i++) {
119 | if (types[i].equalsIgnoreCase(mimeType)) {
120 | try {
121 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
122 | Set formats = new HashSet();
123 |
124 | // And through the color formats supported
125 | for (int k = 0; k < capabilities.colorFormats.length; k++) {
126 | int format = capabilities.colorFormats[k];
127 |
128 | for (int l=0;l0 ? true : false;
106 | frameLength = (header[3]&0x03) << 11 |
107 | (header[4]&0xFF) << 3 |
108 | (header[5]&0xFF) >> 5 ;
109 | frameLength -= (protection ? 7 : 9);
110 |
111 | // Number of AAC frames in the ADTS frame
112 | nbau = (header[6]&0x03) + 1;
113 |
114 | // The number of RTP packets that will be sent for this ADTS frame
115 | nbpk = frameLength/MAXPACKETSIZE + 1;
116 |
117 | // Read CRS if any
118 | if (!protection) is.read(header,0,2);
119 |
120 | samplingRate = AACStream.AUDIO_SAMPLING_RATES[(header[2]&0x3C) >> 2];
121 | profile = ( (header[2]&0xC0) >> 6 ) + 1 ;
122 |
123 | // We update the RTP timestamp
124 | ts += 1024L*1000000000L/samplingRate; //stats.average();
125 |
126 | //Log.d(TAG,"frameLength: "+frameLength+" protection: "+protection+" p: "+profile+" sr: "+samplingRate);
127 |
128 | sum = 0;
129 | while (sum MAXPACKETSIZE-rtphl-4) {
136 | length = MAXPACKETSIZE-rtphl-4;
137 | }
138 | else {
139 | length = frameLength-sum;
140 | socket.markNextPacket();
141 | }
142 | sum += length;
143 | fill(buffer, rtphl+4, length);
144 |
145 | // AU-headers-length field: contains the size in bits of a AU-header
146 | // 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta
147 | // 13 bits will be enough because ADTS uses 13 bits for frame length
148 | buffer[rtphl] = 0;
149 | buffer[rtphl+1] = 0x10;
150 |
151 | // AU-size
152 | buffer[rtphl+2] = (byte) (frameLength>>5);
153 | buffer[rtphl+3] = (byte) (frameLength<<3);
154 |
155 | // AU-Index
156 | buffer[rtphl+3] &= 0xF8;
157 | buffer[rtphl+3] |= 0x00;
158 |
159 | send(rtphl+4+length);
160 | }
161 | }
162 | } catch (IOException e) {
163 | // Ignore
164 | } catch (ArrayIndexOutOfBoundsException e) {
165 | Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
166 | e.printStackTrace();
167 | } catch (InterruptedException ignore) {}
168 |
169 | Log.d(TAG,"AAC ADTS packetizer stopped !");
170 | }
171 |
172 | private int fill(byte[] buffer, int offset,int length) throws IOException {
173 | int sum = 0, len;
174 | while (sum= 26) {
89 | String channelId = getNotificationChannelId();
90 | NotificationManager NM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
91 | NotificationChannel NC = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
92 |
93 | NC.setDescription(channelId);
94 | NC.setSound(null, null);
95 | NM.createNotificationChannel(NC);
96 | }
97 | }
98 |
99 | private int getNotificationId() {
100 | return ResourceUtils.getInteger(instance, R.integer.NOTIFICATION_ID_NETWORKING_SERVICE);
101 | }
102 |
103 | private void showNotification() {
104 | Notification notification = getNotification();
105 | int NOTIFICATION_ID = getNotificationId();
106 |
107 | if (Build.VERSION.SDK_INT >= 5) {
108 | createNotificationChannel();
109 | startForeground(NOTIFICATION_ID, notification);
110 | }
111 | else {
112 | NotificationManager NM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
113 | NM.notify(NOTIFICATION_ID, notification);
114 | }
115 | }
116 |
117 | private void hideNotification() {
118 | if (Build.VERSION.SDK_INT >= 5) {
119 | stopForeground(true);
120 | }
121 | else {
122 | NotificationManager NM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
123 | int NOTIFICATION_ID = getNotificationId();
124 | NM.cancel(NOTIFICATION_ID);
125 | }
126 | }
127 |
128 | private Notification getNotification() {
129 | Notification notification = (Build.VERSION.SDK_INT >= 26)
130 | ? (new Notification.Builder(/* context= */ instance, /* channelId= */ getNotificationChannelId())).build()
131 | : new Notification()
132 | ;
133 |
134 | notification.when = System.currentTimeMillis();
135 | notification.flags = 0;
136 | notification.flags |= Notification.FLAG_ONGOING_EVENT;
137 | notification.flags |= Notification.FLAG_NO_CLEAR;
138 | notification.icon = R.drawable.launcher;
139 | notification.tickerText = getString(R.string.notification_service_ticker);
140 | notification.contentIntent = getPendingIntent_StopService();
141 | notification.deleteIntent = getPendingIntent_StopService();
142 |
143 | if (Build.VERSION.SDK_INT >= 16) {
144 | notification.priority = Notification.PRIORITY_HIGH;
145 | }
146 | else {
147 | notification.flags |= Notification.FLAG_HIGH_PRIORITY;
148 | }
149 |
150 | if (Build.VERSION.SDK_INT >= 21) {
151 | notification.visibility = Notification.VISIBILITY_PUBLIC;
152 | }
153 |
154 | RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.service_notification);
155 | contentView.setImageViewResource(R.id.notification_icon, R.drawable.launcher);
156 | contentView.setTextViewText(R.id.notification_text_line1, getString(R.string.notification_service_content_line1));
157 | contentView.setTextViewText(R.id.notification_text_line2, getNetworkAddress());
158 | contentView.setTextViewText(R.id.notification_text_line3, getString(R.string.notification_service_content_line3));
159 | notification.contentView = contentView;
160 |
161 | return notification;
162 | }
163 |
164 | private PendingIntent getPendingIntent_StopService() {
165 | Intent intent = new Intent(instance, NetworkingService.class);
166 | intent.setAction(ACTION_STOP);
167 |
168 | return PendingIntent.getService(instance, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
169 | }
170 |
171 | private String getNetworkAddress() {
172 | if (localAddress == null)
173 | localAddress = NetworkUtils.getLocalIpAddress(); //Get local IP object
174 |
175 | return (localAddress == null)
176 | ? "[offline]"
177 | : "rtsp://" + localAddress.getHostAddress() + ":" + getPort();
178 | }
179 |
180 | // -------------------------------------------------------------------------
181 | // process inbound intents
182 |
183 | private void processIntent(Intent intent) {
184 | if (intent == null) return;
185 |
186 | String action = intent.getAction();
187 |
188 | if ((action != null) && action.equals(ACTION_STOP))
189 | instance.stopSelf();
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtcp/SenderReport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtcp;
22 |
23 | import static net.majorkernelpanic.screening.rtp.RtpSocket.TRANSPORT_TCP;
24 | import static net.majorkernelpanic.screening.rtp.RtpSocket.TRANSPORT_UDP;
25 |
26 | import android.os.SystemClock;
27 | import android.util.Log;
28 |
29 | import java.io.IOException;
30 | import java.io.OutputStream;
31 | import java.net.DatagramPacket;
32 | import java.net.InetAddress;
33 | import java.net.MulticastSocket;
34 | import java.nio.channels.IllegalSelectorException;
35 |
36 | /**
37 | * Implementation of Sender Report RTCP packets.
38 | */
39 | public class SenderReport {
40 |
41 | public static final int MTU = 1500;
42 |
43 | private static final int PACKET_LENGTH = 28;
44 |
45 | private MulticastSocket usock;
46 | private DatagramPacket upack;
47 |
48 | private int mTransport;
49 | private OutputStream mOutputStream = null;
50 | private byte[] mBuffer = new byte[MTU];
51 | private int mSSRC, mPort = -1;
52 | private int mOctetCount = 0, mPacketCount = 0;
53 | private long interval, delta, now, oldnow;
54 | private byte mTcpHeader[];
55 |
56 | public SenderReport(int ssrc) throws IOException {
57 | super();
58 | this.mSSRC = ssrc;
59 | }
60 |
61 | public SenderReport() {
62 |
63 | mTransport = TRANSPORT_UDP;
64 | mTcpHeader = new byte[] {'$',0,0,PACKET_LENGTH};
65 |
66 | /* Version(2) Padding(0) */
67 | /* ^ ^ PT = 0 */
68 | /* | | ^ */
69 | /* | -------- | */
70 | /* | |--------------------- */
71 | /* | || */
72 | /* | || */
73 | mBuffer[0] = (byte) Integer.parseInt("10000000",2);
74 |
75 | /* Packet Type PT */
76 | mBuffer[1] = (byte) 200;
77 |
78 | /* Byte 2,3 -> Length */
79 | setLong(PACKET_LENGTH/4-1, 2, 4);
80 |
81 | /* Byte 4,5,6,7 -> SSRC */
82 | /* Byte 8,9,10,11 -> NTP timestamp hb */
83 | /* Byte 12,13,14,15 -> NTP timestamp lb */
84 | /* Byte 16,17,18,19 -> RTP timestamp */
85 | /* Byte 20,21,22,23 -> packet count */
86 | /* Byte 24,25,26,27 -> octet count */
87 |
88 | try {
89 | usock = new MulticastSocket();
90 | } catch (IOException e) {
91 | // Very unlikely to happen. Means that all UDP ports are already being used
92 | throw new RuntimeException(e.getMessage());
93 | }
94 | upack = new DatagramPacket(mBuffer, 1);
95 |
96 | // By default we sent one report every 3 secconde
97 | interval = 3000;
98 |
99 | }
100 |
101 | public void close() {
102 | usock.close();
103 | }
104 |
105 | /**
106 | * Sets the temporal interval between two RTCP Sender Reports.
107 | * Default interval is set to 3 seconds.
108 | * Set 0 to disable RTCP.
109 | * @param interval The interval in milliseconds
110 | */
111 | public void setInterval(long interval) {
112 | this.interval = interval;
113 | }
114 |
115 | /**
116 | * Updates the number of packets sent, and the total amount of data sent.
117 | * @param length The length of the packet
118 | * @param rtpts
119 | * The RTP timestamp.
120 | * @throws IOException
121 | **/
122 | public void update(int length, long rtpts) throws IOException {
123 | mPacketCount += 1;
124 | mOctetCount += length;
125 | setLong(mPacketCount, 20, 24);
126 | setLong(mOctetCount, 24, 28);
127 |
128 | now = SystemClock.elapsedRealtime();
129 | delta += oldnow != 0 ? now-oldnow : 0;
130 | oldnow = now;
131 | if (interval>0 && delta>=interval) {
132 | // We send a Sender Report
133 | send(System.nanoTime(), rtpts);
134 | delta = 0;
135 | }
136 | }
137 |
138 | public void setSSRC(int ssrc) {
139 | this.mSSRC = ssrc;
140 | setLong(ssrc,4,8);
141 | mPacketCount = 0;
142 | mOctetCount = 0;
143 | setLong(mPacketCount, 20, 24);
144 | setLong(mOctetCount, 24, 28);
145 | }
146 |
147 | public void setDestination(InetAddress dest, int dport) {
148 | mTransport = TRANSPORT_UDP;
149 | mPort = dport;
150 | upack.setPort(dport);
151 | upack.setAddress(dest);
152 | }
153 |
154 | /**
155 | * If a TCP is used as the transport protocol for the RTP session,
156 | * the output stream to which RTP packets will be written to must
157 | * be specified with this method.
158 | */
159 | public void setOutputStream(OutputStream os, byte channelIdentifier) {
160 | mTransport = TRANSPORT_TCP;
161 | mOutputStream = os;
162 | mTcpHeader[1] = channelIdentifier;
163 | }
164 |
165 | public int getPort() {
166 | return mPort;
167 | }
168 |
169 | public int getLocalPort() {
170 | return usock.getLocalPort();
171 | }
172 |
173 | public int getSSRC() {
174 | return mSSRC;
175 | }
176 |
177 | /**
178 | * Resets the reports (total number of bytes sent, number of packets sent, etc.)
179 | */
180 | public void reset() {
181 | mPacketCount = 0;
182 | mOctetCount = 0;
183 | setLong(mPacketCount, 20, 24);
184 | setLong(mOctetCount, 24, 28);
185 | delta = now = oldnow = 0;
186 | }
187 |
188 | private void setLong(long n, int begin, int end) {
189 | for (end--; end >= begin; end--) {
190 | mBuffer[end] = (byte) (n % 256);
191 | n >>= 8;
192 | }
193 | }
194 |
195 | /**
196 | * Sends the RTCP packet over the network.
197 | *
198 | * @param ntpts
199 | * the NTP timestamp.
200 | * @param rtpts
201 | * the RTP timestamp.
202 | */
203 | private void send(long ntpts, long rtpts) throws IOException {
204 | long hb = ntpts/1000000000;
205 | long lb = ( ( ntpts - hb*1000000000 ) * 4294967296L )/1000000000;
206 | setLong(hb, 8, 12);
207 | setLong(lb, 12, 16);
208 | setLong(rtpts, 16, 20);
209 | if (mTransport == TRANSPORT_UDP) {
210 | upack.setLength(PACKET_LENGTH);
211 | usock.send(upack);
212 | } else {
213 | synchronized (mOutputStream) {
214 | try {
215 | mOutputStream.write(mTcpHeader);
216 | mOutputStream.write(mBuffer, 0, PACKET_LENGTH);
217 | } catch (Exception e) {}
218 | }
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/mp4/MP4Parser.java:
--------------------------------------------------------------------------------
1 | package net.majorkernelpanic.screening.mp4;
2 | /*
3 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
4 | *
5 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
6 | *
7 | * Spydroid is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation; either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This source code is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with this source code; if not, write to the Free Software
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 | */
21 |
22 |
23 | import android.util.Base64;
24 | import android.util.Log;
25 |
26 | import java.io.File;
27 | import java.io.FileNotFoundException;
28 | import java.io.IOException;
29 | import java.io.RandomAccessFile;
30 | import java.nio.ByteBuffer;
31 | import java.util.HashMap;
32 |
33 | /**
34 | * Parse an mp4 file.
35 | * An mp4 file contains a tree where each node has a name and a size.
36 | * This class is used by H264Stream.java to determine the SPS and PPS parameters of a short video recorded by the phone.
37 | */
38 | public class MP4Parser {
39 |
40 | private static final String TAG = "MP4Parser";
41 |
42 | private HashMap mBoxes = new HashMap();
43 | private final RandomAccessFile mFile;
44 | private long mPos = 0;
45 |
46 |
47 | /** Parses the mp4 file. **/
48 | public static MP4Parser parse(String path) throws IOException {
49 | return new MP4Parser(path);
50 | }
51 |
52 | private MP4Parser(final String path) throws IOException, FileNotFoundException {
53 | mFile = new RandomAccessFile(new File(path), "r");
54 | try {
55 | parse("",mFile.length());
56 | } catch (Exception e) {
57 | e.printStackTrace();
58 | throw new IOException("Parse error: malformed mp4 file");
59 | }
60 | }
61 |
62 | public void close() {
63 | try {
64 | mFile.close();
65 | } catch (Exception e) {};
66 | }
67 |
68 | public long getBoxPos(String box) throws IOException {
69 | Long r = mBoxes.get(box);
70 |
71 | if (r==null) throw new IOException("Box not found: "+box);
72 | return mBoxes.get(box);
73 | }
74 |
75 | public StsdBox getStsdBox() throws IOException {
76 | try {
77 | return new StsdBox(mFile,getBoxPos("/moov/trak/mdia/minf/stbl/stsd"));
78 | } catch (IOException e) {
79 | throw new IOException("stsd box could not be found");
80 | }
81 | }
82 |
83 | private void parse(String path, long len) throws IOException {
84 | ByteBuffer byteBuffer;
85 | long sum = 0, newlen = 0;
86 | byte[] buffer = new byte[8];
87 | String name = "";
88 |
89 | if(!path.equals("")) mBoxes.put(path, mPos-8);
90 |
91 | while (sum name: "+name+" position: "+mPos+", length: "+newlen);
115 | sum += newlen;
116 | parse(path+'/'+name,newlen);
117 |
118 | }
119 | else {
120 | if( len < 8){
121 | mFile.seek(mFile.getFilePointer() - 8 + len);
122 | sum += len-8;
123 | } else {
124 | int skipped = mFile.skipBytes((int)(len-8));
125 | if (skipped < ((int)(len-8))) {
126 | throw new IOException();
127 | }
128 | mPos += len-8;
129 | sum += len-8;
130 | }
131 | }
132 | }
133 | }
134 |
135 | private boolean validBoxName(byte[] buffer) {
136 | for (int i=0;i<4;i++) {
137 | // If the next 4 bytes are neither lowercase letters nor numbers
138 | if ((buffer[i+4]< 'a' || buffer[i+4]>'z') && (buffer[i+4]<'0'|| buffer[i+4]>'9') ) return false;
139 | }
140 | return true;
141 | }
142 |
143 | static String toHexString(byte[] buffer,int start, int len) {
144 | String c;
145 | StringBuilder s = new StringBuilder();
146 | for (int i=start;i
198 | * aligned(8) class AVCDecoderConfigurationRecord {
199 | * unsigned int(8) configurationVersion = 1;
200 | * unsigned int(8) AVCProfileIndication;
201 | * unsigned int(8) profile_compatibility;
202 | * unsigned int(8) AVCLevelIndication;
203 | * bit(6) reserved = ‘111111’b;
204 | * unsigned int(2) lengthSizeMinusOne;
205 | * bit(3) reserved = ‘111’b;
206 | * unsigned int(5) numOfSequenceParameterSets;
207 | * for (i=0; i< numOfSequenceParameterSets; i++) {
208 | * unsigned int(16) sequenceParameterSetLength ;
209 | * bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
210 | * }
211 | * unsigned int(8) numOfPictureParameterSets;
212 | * for (i=0; i< numOfPictureParameterSets; i++) {
213 | * unsigned int(16) pictureParameterSetLength;
214 | * bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
215 | * }
216 | * }
217 | *
218 | */
219 | try {
220 |
221 | // TODO: Here we assume that numOfSequenceParameterSets = 1, numOfPictureParameterSets = 1 !
222 | // Here we extract the SPS parameter
223 | fis.skipBytes(7);
224 | spsLength = 0xFF&fis.readByte();
225 | sps = new byte[spsLength];
226 | fis.read(sps,0,spsLength);
227 | // Here we extract the PPS parameter
228 | fis.skipBytes(2);
229 | ppsLength = 0xFF&fis.readByte();
230 | pps = new byte[ppsLength];
231 | fis.read(pps,0,ppsLength);
232 |
233 | } catch (IOException e) {
234 | return false;
235 | }
236 |
237 | return true;
238 | }
239 |
240 | private boolean findBoxAvcc() {
241 | try {
242 | fis.seek(pos+8);
243 | while (true) {
244 | while (fis.read() != 'a');
245 | fis.read(buffer,0,3);
246 | if (buffer[0] == 'v' && buffer[1] == 'c' && buffer[2] == 'C') break;
247 | }
248 | } catch (IOException e) {
249 | return false;
250 | }
251 |
252 | return true;
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/DrawTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * ScreenRecordingSample
3 | * Sample project to cature and save audio from internal and video from screen as MPEG4 file.
4 | *
5 | * Copyright (c) 2015-2016 saki t_saki@serenegiant.com
6 | *
7 | * File name: MediaScreenEncoder.java
8 | *
9 | * Licensed under the Apache License, Version 2.0 (the "License");
10 | * you may not use this file except in compliance with the License.
11 | * You may obtain a copy of the License at
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | *
15 | * Unless required by applicable law or agreed to in writing, software
16 | * distributed under the License is distributed on an "AS IS" BASIS,
17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | * See the License for the specific language governing permissions and
19 | * limitations under the License.
20 | *
21 | * All files in the folder are under this Apache License, Version 2.0.
22 | */
23 |
24 | /*
25 | * =======
26 | * Origin:
27 | * =======
28 | * https://github.com/saki4510t/ScreenRecordingSample
29 | * https://github.com/saki4510t/ScreenRecordingSample/blob/9419559a193f2b90c8f86de82c975494a7b2f7d0/app/src/main/java/com/serenegiant/media/MediaScreenEncoder.java#L112
30 | *
31 | * =============
32 | * Dependencies:
33 | * =============
34 | * https://github.com/saki4510t/libcommon/blob/master/common/src/main/java/com/serenegiant/glutils/EglTask.java
35 | * https://github.com/saki4510t/libcommon/blob/master/common/src/main/java/com/serenegiant/utils/MessageTask.java
36 | */
37 |
38 | package net.majorkernelpanic.screening.video;
39 |
40 | import com.serenegiant.glutils.EGLBase;
41 | import com.serenegiant.glutils.EglTask;
42 | import com.serenegiant.glutils.GLDrawer2D;
43 |
44 | import android.graphics.SurfaceTexture;
45 | import android.hardware.display.DisplayManager;
46 | import android.hardware.display.VirtualDisplay;
47 | import android.media.projection.MediaProjection;
48 | import android.opengl.GLES20;
49 | import android.os.Handler;
50 | import android.os.HandlerThread;
51 | import android.util.Log;
52 | import android.view.Surface;
53 |
54 | public final class DrawTask extends EglTask {
55 | private static final boolean DEBUG = true;
56 | private static final String TAG = "VideoStream:DrawTask";
57 |
58 | private final Object mSync = new Object();
59 | private final float[] mTexMatrix = new float[16];
60 |
61 | private MediaProjection mMediaProjection;
62 | private Surface mSurface;
63 | private VideoQuality mQuality;
64 | private Handler mHandler;
65 |
66 | private GLDrawer2D mDrawer;
67 | private int mTexId;
68 | private SurfaceTexture mSourceTexture;
69 | private Surface mSourceSurface;
70 | private EGLBase.IEglSurface mEncoderSurface;
71 | private long intervals;
72 | private VirtualDisplay display;
73 | private boolean mIsRecording;
74 | private boolean requestDraw;
75 |
76 | // Callback listener when receiving the video at TextureSurface
77 | private final SurfaceTexture.OnFrameAvailableListener mOnFrameAvailableListener = new SurfaceTexture.OnFrameAvailableListener() {
78 | @Override
79 | public void onFrameAvailable(final SurfaceTexture surfaceTexture) {
80 | //if (DEBUG) Log.v(TAG, "SurfaceTexture.Callback#onFrameAvailable: mIsRecording=" + mIsRecording);
81 | if (mIsRecording) {
82 | synchronized (mSync) {
83 | requestDraw = true;
84 | mSync.notifyAll();
85 | }
86 | }
87 | }
88 | };
89 |
90 | private final VirtualDisplay.Callback mCallback = (!DEBUG) ? null : new VirtualDisplay.Callback() {
91 | /**
92 | * Called when the virtual display video projection has been
93 | * paused by the system or when the surface has been detached
94 | * by the application by calling setSurface(null).
95 | * The surface will not receive any more buffers while paused.
96 | */
97 | @Override
98 | public void onPaused() {
99 | if (DEBUG) Log.v(TAG, "VirtualDisplay.Callback#onPaused");
100 | }
101 |
102 | /**
103 | * Called when the virtual display video projection has been
104 | * resumed after having been paused.
105 | */
106 | @Override
107 | public void onResumed() {
108 | if (DEBUG) Log.v(TAG, "VirtualDisplay.Callback#onResumed");
109 | }
110 |
111 | /**
112 | * Called when the virtual display video projection has been
113 | * stopped by the system. It will no longer receive frames
114 | * and it will never be resumed. It is still the responsibility
115 | * of the application to release() the virtual display.
116 | */
117 | @Override
118 | public void onStopped() {
119 | if (DEBUG) Log.v(TAG, "VirtualDisplay.Callback#onStopped");
120 | }
121 | };
122 |
123 | private final Runnable mDrawTask = new Runnable() {
124 | @Override
125 | public void run() {
126 | //if (DEBUG) Log.v(TAG, "draw");
127 | boolean local_request_draw;
128 | synchronized (mSync) {
129 | local_request_draw = requestDraw;
130 | if (!requestDraw) {
131 | try {
132 | mSync.wait(intervals);
133 | local_request_draw = requestDraw;
134 | requestDraw = false;
135 | } catch (final InterruptedException e) {
136 | return;
137 | }
138 | }
139 | }
140 | if (mIsRecording) {
141 | if (local_request_draw) {
142 | mSourceTexture.updateTexImage();
143 | mSourceTexture.getTransformMatrix(mTexMatrix);
144 | }
145 |
146 | // Draw the image received by SurfaceTexture on the input Surface of MediaCodec
147 | mEncoderSurface.makeCurrent();
148 | mDrawer.draw(mTexId, mTexMatrix, 0);
149 | mEncoderSurface.swap();
150 |
151 | // Workaround for models that hang if not drawn off-screen for EGL retention
152 | makeCurrent();
153 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
154 | GLES20.glFlush();
155 | queueEvent(this);
156 | } else {
157 | releaseSelf();
158 | }
159 | //if (DEBUG) Log.v(TAG, "draw:finished");
160 | }
161 | };
162 |
163 | public DrawTask(
164 | final EGLBase.IContext sharedContext,
165 | final int flags,
166 | final MediaProjection mediaProjection,
167 | final Surface surface,
168 | final VideoQuality quality
169 | ) {
170 | super(sharedContext, flags);
171 |
172 | this.mMediaProjection = mediaProjection;
173 | this.mSurface = surface;
174 | this.mQuality = quality;
175 |
176 | final HandlerThread thread = new HandlerThread(TAG);
177 | thread.start();
178 | mHandler = new Handler(thread.getLooper());
179 | }
180 |
181 | @Override
182 | protected void onStart() {
183 | if (DEBUG) Log.d(TAG, "onStart");
184 |
185 | mDrawer = new GLDrawer2D(true);
186 | mTexId = mDrawer.initTex();
187 | mSourceTexture = new SurfaceTexture(mTexId);
188 | mSourceTexture.setDefaultBufferSize(mQuality.screenWidth, mQuality.screenHeight); // If you don't put this in, you can't get the image
189 | mSourceSurface = new Surface(mSourceTexture);
190 | mSourceTexture.setOnFrameAvailableListener(mOnFrameAvailableListener, mHandler);
191 | mEncoderSurface = getEgl().createFromSurface(mSurface);
192 |
193 | if (DEBUG) Log.d(TAG, "setup VirtualDisplay");
194 | intervals = (long)(1000f / mQuality.framerate);
195 | display = mMediaProjection.createVirtualDisplay(
196 | "RTSP ScreenCaster",
197 | mQuality.screenWidth, mQuality.screenHeight, mQuality.screenDpi,
198 | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
199 | mSourceSurface, mCallback, mHandler
200 | );
201 | if (DEBUG) Log.v(TAG, "screen capture loop: display=" + display);
202 |
203 | mIsRecording = true;
204 | requestDraw = false;
205 |
206 | queueEvent(mDrawTask);
207 | }
208 |
209 | private void releaseAllResources() {
210 | mIsRecording = false;
211 | requestDraw = false;
212 |
213 | if (display != null) {
214 | if (DEBUG) Log.d(TAG, "release VirtualDisplay");
215 | display.release();
216 | display = null;
217 | }
218 | if (mEncoderSurface != null) {
219 | mEncoderSurface.release();
220 | mEncoderSurface = null;
221 | }
222 | if (mSourceSurface != null) {
223 | mSourceSurface.release();
224 | mSourceSurface = null;
225 | }
226 | if (mSourceTexture != null) {
227 | mSourceTexture.release();
228 | mSourceTexture = null;
229 | }
230 | if (mDrawer != null) {
231 | mDrawer.release();
232 | mDrawer = null;
233 | }
234 |
235 | try {
236 | makeCurrent();
237 | }
238 | catch (final Exception e) {}
239 | }
240 |
241 | @Override
242 | protected void onStop() {
243 | if (DEBUG) Log.d(TAG, "onStop");
244 | releaseAllResources();
245 | }
246 |
247 | @Override
248 | protected void onRelease() {
249 | super.onRelease();
250 | if (DEBUG) Log.d(TAG, "onRelease");
251 | releaseAllResources();
252 | }
253 |
254 | @Override
255 | protected boolean onError(final Exception e) {
256 | super.onError(e);
257 | if (DEBUG) Log.w(TAG, e);
258 |
259 | return false;
260 | }
261 |
262 | @Override
263 | protected Object processRequest(final int request, final int arg1, final int arg2, final Object obj) {
264 | return null;
265 | }
266 |
267 | @Override
268 | public void release() {
269 | releaseAllResources();
270 |
271 | super.release(true);
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/rtp/H264Packetizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.rtp;
22 |
23 | import android.annotation.SuppressLint;
24 | import android.util.Log;
25 |
26 | import java.io.IOException;
27 |
28 | /**
29 | *
30 | * RFC 3984.
31 | *
32 | * H.264 streaming over RTP.
33 | *
34 | * Must be fed with an InputStream containing H.264 NAL units preceded by their length (4 bytes).
35 | * The stream must start with mpeg4 or 3gpp header, it will be skipped.
36 | *
37 | */
38 | public class H264Packetizer extends AbstractPacketizer implements Runnable {
39 | public final static String TAG = "H264Packetizer";
40 |
41 | private Thread t = null;
42 | private int naluLength = 0;
43 | private long delay = 0, oldtime = 0;
44 | private Statistics stats = new Statistics();
45 | private byte[] sps = null, pps = null, stapa = null;
46 | byte[] header = new byte[5];
47 | private int count = 0;
48 | private int streamType = 1;
49 |
50 | public H264Packetizer() {
51 | super();
52 | socket.setClockFrequency(90000);
53 | }
54 |
55 | public void start() {
56 | if (t == null) {
57 | t = new Thread(this);
58 | t.start();
59 | }
60 | }
61 |
62 | public void stop() {
63 | if (t != null) {
64 | try {
65 | is.close();
66 | } catch (IOException e) {}
67 | t.interrupt();
68 | try {
69 | t.join();
70 | } catch (InterruptedException e) {}
71 | t = null;
72 | }
73 | }
74 |
75 | public void setStreamParameters(byte[] pps, byte[] sps) {
76 | this.pps = pps;
77 | this.sps = sps;
78 |
79 | // A STAP-A NAL (NAL type 24) containing the sps and pps of the stream
80 | if (pps != null && sps != null) {
81 | // STAP-A NAL header + NALU 1 (SPS) size + NALU 2 (PPS) size = 5 bytes
82 | stapa = new byte[sps.length + pps.length + 5];
83 |
84 | // STAP-A NAL header is 24
85 | stapa[0] = 24;
86 |
87 | // Write NALU 1 size into the array (NALU 1 is the SPS).
88 | stapa[1] = (byte) (sps.length >> 8);
89 | stapa[2] = (byte) (sps.length & 0xFF);
90 |
91 | // Write NALU 2 size into the array (NALU 2 is the PPS).
92 | stapa[sps.length + 3] = (byte) (pps.length >> 8);
93 | stapa[sps.length + 4] = (byte) (pps.length & 0xFF);
94 |
95 | // Write NALU 1 into the array, then write NALU 2 into the array.
96 | System.arraycopy(sps, 0, stapa, 3, sps.length);
97 | System.arraycopy(pps, 0, stapa, 5 + sps.length, pps.length);
98 | }
99 | }
100 |
101 | public void run() {
102 | long duration = 0;
103 | Log.d(TAG,"H264 packetizer started !");
104 | stats.reset();
105 | count = 0;
106 |
107 | if (is instanceof MediaCodecInputStream) {
108 | streamType = 1;
109 | socket.setCacheSize(0);
110 | } else {
111 | streamType = 0;
112 | socket.setCacheSize(400);
113 | }
114 |
115 | try {
116 | while (!Thread.interrupted()) {
117 |
118 | oldtime = System.nanoTime();
119 | // We read a NAL units from the input stream and we send them
120 | send();
121 | // We measure how long it took to receive NAL units from the phone
122 | duration = System.nanoTime() - oldtime;
123 |
124 | stats.push(duration);
125 | // Computes the average duration of a NAL unit
126 | delay = stats.average();
127 | //Log.d(TAG,"duration: "+duration/1000000+" delay: "+delay/1000000);
128 |
129 | }
130 | } catch (IOException e) {
131 | } catch (InterruptedException e) {}
132 |
133 | Log.d(TAG,"H264 packetizer stopped !");
134 | }
135 |
136 | /**
137 | * Reads a NAL unit in the FIFO and sends it.
138 | * If it is too big, we split it in FU-A units (RFC 3984).
139 | */
140 | @SuppressLint("NewApi")
141 | private void send() throws IOException, InterruptedException {
142 | int sum = 1, len = 0, type;
143 |
144 | if (streamType == 0) {
145 | // NAL units are preceeded by their length, we parse the length
146 | fill(header,0,5);
147 | ts += delay;
148 | naluLength = header[3]&0xFF | (header[2]&0xFF)<<8 | (header[1]&0xFF)<<16 | (header[0]&0xFF)<<24;
149 | if (naluLength>100000 || naluLength<0) resync();
150 | } else if (streamType == 1) {
151 | // NAL units are preceeded with 0x00000001
152 | fill(header,0,5);
153 | ts = ((MediaCodecInputStream)is).getLastBufferInfo().presentationTimeUs*1000L;
154 | //ts += delay;
155 | naluLength = is.available()+1;
156 | if (!(header[0]==0 && header[1]==0 && header[2]==0)) {
157 | // Turns out, the NAL units are not preceeded with 0x00000001
158 | Log.e(TAG, "NAL units are not preceeded by 0x00000001");
159 | streamType = 2;
160 | return;
161 | }
162 | } else {
163 | // Nothing preceededs the NAL units
164 | fill(header,0,1);
165 | header[4] = header[0];
166 | ts = ((MediaCodecInputStream)is).getLastBufferInfo().presentationTimeUs*1000L;
167 | //ts += delay;
168 | naluLength = is.available()+1;
169 | }
170 |
171 | // Parses the NAL unit type
172 | type = header[4]&0x1F;
173 |
174 | // The stream already contains NAL unit type 7 or 8, we don't need
175 | // to add them to the stream ourselves
176 | if (type == 7 || type == 8) {
177 | Log.v(TAG,"SPS or PPS present in the stream.");
178 | count++;
179 | if (count>4) {
180 | sps = null;
181 | pps = null;
182 | }
183 | }
184 |
185 | // We send two packets containing NALU type 7 (SPS) and 8 (PPS)
186 | // Those should allow the H264 stream to be decoded even if no SDP was sent to the decoder.
187 | if (type == 5 && sps != null && pps != null) {
188 | buffer = socket.requestBuffer();
189 | socket.markNextPacket();
190 | socket.updateTimestamp(ts);
191 | System.arraycopy(stapa, 0, buffer, rtphl, stapa.length);
192 | super.send(rtphl+stapa.length);
193 | }
194 |
195 | //Log.d(TAG,"- Nal unit length: " + naluLength + " delay: "+delay/1000000+" type: "+type);
196 |
197 | // Small NAL unit => Single NAL unit
198 | if (naluLength<=MAXPACKETSIZE-rtphl-2) {
199 | buffer = socket.requestBuffer();
200 | buffer[rtphl] = header[4];
201 | len = fill(buffer, rtphl+1, naluLength-1);
202 | socket.updateTimestamp(ts);
203 | socket.markNextPacket();
204 | super.send(naluLength+rtphl);
205 | //Log.d(TAG,"----- Single NAL unit - len:"+len+" delay: "+delay);
206 | }
207 | // Large NAL unit => Split nal unit
208 | else {
209 | // Set FU-A header
210 | header[1] = (byte) (header[4] & 0x1F); // FU header type
211 | header[1] += 0x80; // Start bit
212 | // Set FU-A indicator
213 | header[0] = (byte) ((header[4] & 0x60) & 0xFF); // FU indicator NRI
214 | header[0] += 28;
215 |
216 | while (sum < naluLength) {
217 | buffer = socket.requestBuffer();
218 | buffer[rtphl] = header[0];
219 | buffer[rtphl+1] = header[1];
220 | socket.updateTimestamp(ts);
221 | if ((len = fill(buffer, rtphl+2, naluLength-sum > MAXPACKETSIZE-rtphl-2 ? MAXPACKETSIZE-rtphl-2 : naluLength-sum ))<0) return; sum += len;
222 | // Last packet before next NAL
223 | if (sum >= naluLength) {
224 | // End bit on
225 | buffer[rtphl+1] += 0x40;
226 | socket.markNextPacket();
227 | }
228 | super.send(len+rtphl+2);
229 | // Switch start bit
230 | header[1] = (byte) (header[1] & 0x7F);
231 | //Log.d(TAG,"----- FU-A unit, sum:"+sum);
232 | }
233 | }
234 | }
235 |
236 | private int fill(byte[] buffer, int offset,int length) throws IOException {
237 | int sum = 0, len;
238 | while (sum0 && naluLength<100000) {
266 | oldtime = System.nanoTime();
267 | Log.e(TAG,"A NAL unit may have been found in the bit stream !");
268 | break;
269 | }
270 | if (naluLength==0) {
271 | Log.e(TAG,"NAL unit with NULL size found...");
272 | } else if (header[3]==0xFF && header[2]==0xFF && header[1]==0xFF && header[0]==0xFF) {
273 | Log.e(TAG,"NAL unit with 0xFFFFFFFF size found...");
274 | }
275 | }
276 | }
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/VideoStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2015 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.video;
22 |
23 | import net.majorkernelpanic.screening.MediaStream;
24 | import net.majorkernelpanic.screening.Stream;
25 | import net.majorkernelpanic.screening.hw.EncoderDebugger;
26 | import net.majorkernelpanic.screening.rtp.MediaCodecInputStream;
27 |
28 | import android.content.SharedPreferences;
29 | import android.media.MediaCodec;
30 | import android.media.MediaCodecInfo;
31 | import android.media.MediaFormat;
32 | import android.media.MediaRecorder;
33 | import android.media.projection.MediaProjection;
34 | import android.os.ParcelFileDescriptor;
35 | import android.util.Log;
36 | import android.view.Surface;
37 |
38 | import java.io.FileDescriptor;
39 | import java.io.IOException;
40 | import java.io.InputStream;
41 |
42 | /**
43 | * Don't use this class directly.
44 | */
45 | public abstract class VideoStream extends MediaStream {
46 | protected final static String TAG = "VideoStream";
47 |
48 | protected static MediaProjection MEDIA_PROJECTION = null;
49 |
50 | public static void init(MediaProjection mp) {
51 | MEDIA_PROJECTION = mp;
52 | }
53 |
54 | protected VideoQuality mRequestedQuality = null;
55 | protected VideoQuality mQuality = null;
56 | protected SharedPreferences mSettings = null;
57 | protected int mVideoEncoder = 0;
58 |
59 | private Surface mVideoSurface = null;
60 | private DrawTask mScreenCaptureTask = null;
61 | private Thread mScreenCaptureThread = null;
62 |
63 | public VideoStream() {
64 | super();
65 |
66 | /** Default video stream quality. */
67 | mRequestedQuality = VideoQuality.DEFAULT_VIDEO_QUALITY.clone();
68 | }
69 |
70 | /**
71 | * Sets the configuration of the stream. You can call this method at any time
72 | * and changes will take effect next time you call {@link #configure()}.
73 | * @param videoQuality Quality of the stream
74 | */
75 | public void setVideoQuality(VideoQuality videoQuality) {
76 | if (!mRequestedQuality.equals(videoQuality)) {
77 | mRequestedQuality = videoQuality.clone();
78 | }
79 | }
80 |
81 | /**
82 | * Returns the quality of the stream.
83 | */
84 | public VideoQuality getVideoQuality() {
85 | return mRequestedQuality;
86 | }
87 |
88 | /**
89 | * Some data (SPS and PPS params) needs to be stored when {@link #getSessionDescription()} is called
90 | * @param prefs The SharedPreferences that will be used to save SPS and PPS parameters
91 | */
92 | public void setPreferences(SharedPreferences prefs) {
93 | mSettings = prefs;
94 | }
95 |
96 | /**
97 | * Configures the stream. You need to call this before calling {@link #getSessionDescription()}
98 | * to apply your configuration of the stream.
99 | */
100 | public synchronized void configure() throws IllegalStateException, IOException {
101 | super.configure();
102 | mQuality = mRequestedQuality.clone();
103 | }
104 |
105 | /**
106 | * Starts the stream.
107 | */
108 | public synchronized void start() throws IllegalStateException, IOException {
109 | super.start();
110 | Log.d(TAG,"Stream configuration: FPS: "+mQuality.framerate+" Width: "+mQuality.screenWidth+" Height: "+mQuality.screenHeight);
111 | }
112 |
113 | /** Stops the stream. */
114 | public synchronized void stop() {
115 | if (mScreenCaptureThread != null) {
116 | mScreenCaptureThread.interrupt();
117 | mScreenCaptureThread = null;
118 | }
119 | if (mScreenCaptureTask != null) {
120 | mScreenCaptureTask.release();
121 | mScreenCaptureTask = null;
122 | }
123 | if (mVideoSurface != null) {
124 | mVideoSurface.release();
125 | mVideoSurface = null;
126 | }
127 | super.stop();
128 | }
129 |
130 | /**
131 | * Video encoding is done by a MediaRecorder.
132 | */
133 | protected void encodeWithMediaRecorder() throws RuntimeException, IOException {
134 | Log.d(TAG,"Video encoded using the MediaRecorder API");
135 |
136 | if (MEDIA_PROJECTION == null)
137 | throw new RuntimeException("VideoStream requires a MediaProjection");
138 |
139 | // We need a local socket to forward data output by the camera to the packetizer
140 | createSockets();
141 |
142 | try {
143 | mMediaRecorder = new MediaRecorder();
144 | mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
145 | mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
146 | mMediaRecorder.setVideoEncoder(mVideoEncoder);
147 | mMediaRecorder.setVideoSize(mQuality.screenWidth, mQuality.screenHeight);
148 | mMediaRecorder.setVideoFrameRate(mQuality.framerate);
149 | mMediaRecorder.setVideoEncodingBitRate((int)(mQuality.bitrate*0.8));
150 |
151 | // We write the output of the camera in a local socket instead of a file !
152 | // This one little trick makes streaming feasible quiet simply: data from the camera
153 | // can then be manipulated at the other end of the socket
154 | FileDescriptor fd = null;
155 | if (sPipeApi == PIPE_API_PFD) {
156 | fd = mParcelWrite.getFileDescriptor();
157 | } else {
158 | fd = mSender.getFileDescriptor();
159 | }
160 | mMediaRecorder.setOutputFile(fd);
161 |
162 | mMediaRecorder.prepare();
163 | } catch (Exception e) {
164 | throw new RuntimeException(e.getMessage());
165 | }
166 |
167 | mVideoSurface = mMediaRecorder.getSurface();
168 | mMediaRecorder.start();
169 |
170 | mScreenCaptureTask = new DrawTask(/* sharedContext= */ null, /* flags= */ 0, MEDIA_PROJECTION, mVideoSurface, mQuality);
171 | mScreenCaptureThread = new Thread(mScreenCaptureTask, "ScreenCaptureThread");
172 | mScreenCaptureThread.start();
173 |
174 | InputStream is = null;
175 |
176 | if (sPipeApi == PIPE_API_PFD) {
177 | is = new ParcelFileDescriptor.AutoCloseInputStream(mParcelRead);
178 | } else {
179 | is = mReceiver.getInputStream();
180 | }
181 |
182 | // This will skip the MPEG4 header if this step fails we can't stream anything :(
183 | try {
184 | byte buffer[] = new byte[4];
185 | // Skip all atoms preceding mdat atom
186 | while (!Thread.interrupted()) {
187 | while (is.read() != 'm');
188 | is.read(buffer,0,3);
189 | if (buffer[0] == 'd' && buffer[1] == 'a' && buffer[2] == 't') break;
190 | }
191 | } catch (IOException e) {
192 | Log.e(TAG,"Couldn't skip mp4 header :/");
193 | stop();
194 | throw e;
195 | }
196 |
197 | // The packetizer encapsulates the bit stream in an RTP stream and send it over the network
198 | mPacketizer.setInputStream(is);
199 | mPacketizer.start();
200 | }
201 |
202 | /**
203 | * Video encoding is done by a MediaCodec.
204 | */
205 | protected void encodeWithMediaCodec() throws RuntimeException, IOException {
206 | Log.d(TAG,"Video encoded using the MediaCodec API with a surface");
207 |
208 | if (MEDIA_PROJECTION == null)
209 | throw new RuntimeException("VideoStream requires a MediaProjection");
210 |
211 | String videoFormat = MediaFormat.MIMETYPE_VIDEO_AVC; // "video/avc"
212 | MediaFormat mediaFormat = MediaFormat.createVideoFormat(videoFormat, mQuality.screenWidth, mQuality.screenHeight);
213 |
214 | mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mQuality.framerate);
215 | mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mQuality.bitrate);
216 | mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 0);
217 | mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
218 | mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
219 |
220 | //mMediaCodec = MediaCodec.createEncoderByType(videoFormat);
221 |
222 | EncoderDebugger debugger = EncoderDebugger.debug(mSettings, mQuality.screenWidth, mQuality.screenHeight);
223 | mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName());
224 |
225 | mMediaCodec.configure(mediaFormat, /* surface= */ null, /* crypto= */ null, MediaCodec.CONFIGURE_FLAG_ENCODE);
226 |
227 | mVideoSurface = mMediaCodec.createInputSurface();
228 | mMediaCodec.start();
229 |
230 | mScreenCaptureTask = new DrawTask(/* sharedContext= */ null, /* flags= */ 0, MEDIA_PROJECTION, mVideoSurface, mQuality);
231 | mScreenCaptureThread = new Thread(mScreenCaptureTask, "ScreenCaptureThread");
232 | mScreenCaptureThread.start();
233 |
234 | // The packetizer encapsulates the bit stream in an RTP stream and send it over the network
235 | mPacketizer.setInputStream(new MediaCodecInputStream(mMediaCodec));
236 | mPacketizer.start();
237 | }
238 |
239 | /**
240 | * Returns a description of the stream using SDP.
241 | * This method can only be called after {@link Stream#configure()}.
242 | * @throws IllegalStateException Thrown when {@link Stream#configure()} wa not called.
243 | */
244 | public abstract String getSessionDescription() throws IllegalStateException;
245 | }
246 |
--------------------------------------------------------------------------------
/android-studio-project/libscreening/src/main/java/net/majorkernelpanic/screening/video/H264Stream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.screening.video;
22 |
23 | import net.majorkernelpanic.screening.Session;
24 | import net.majorkernelpanic.screening.SessionBuilder;
25 | import net.majorkernelpanic.screening.hw.EncoderDebugger;
26 | import net.majorkernelpanic.screening.mp4.MP4Config;
27 | import net.majorkernelpanic.screening.rtp.H264Packetizer;
28 |
29 | import android.annotation.SuppressLint;
30 | import android.content.SharedPreferences.Editor;
31 | import android.hardware.display.DisplayManager;
32 | import android.hardware.display.VirtualDisplay;
33 | import android.media.MediaRecorder;
34 | import android.util.Base64;
35 | import android.util.Log;
36 | import android.view.Surface;
37 |
38 | import java.io.File;
39 | import java.io.IOException;
40 | import java.util.concurrent.Semaphore;
41 | import java.util.concurrent.TimeUnit;
42 |
43 | /**
44 | * A class for streaming H.264 from the display screen of an android device using RTP.
45 | * You should use a {@link Session} instantiated with {@link SessionBuilder} instead of using this class directly.
46 | * Call {@link #setDestinationAddress(InetAddress)}, {@link #setDestinationPorts(int)} and {@link #setVideoQuality(VideoQuality)}
47 | * to configure the stream. You can then call {@link #start()} to start the RTP stream.
48 | * Call {@link #stop()} to stop the stream.
49 | */
50 | public class H264Stream extends VideoStream {
51 | public final static String TAG = "H264Stream";
52 |
53 | private Semaphore mLock = new Semaphore(0);
54 | private MP4Config mConfig;
55 |
56 | /**
57 | * Constructs the H.264 stream.
58 | * @throws IOException
59 | */
60 | public H264Stream() {
61 | super();
62 | mVideoEncoder = MediaRecorder.VideoEncoder.H264;
63 | mPacketizer = new H264Packetizer();
64 | }
65 |
66 | /**
67 | * Returns a description of the stream using SDP. It can then be included in an SDP file.
68 | */
69 | public synchronized String getSessionDescription() throws IllegalStateException {
70 | if (mConfig == null) throw new IllegalStateException("You need to call configure() first !");
71 | return "m=video "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" +
72 | "a=rtpmap:96 H264/90000\r\n" +
73 | "a=fmtp:96 packetization-mode=1;profile-level-id="+mConfig.getProfileLevel()+";sprop-parameter-sets="+mConfig.getB64SPS()+","+mConfig.getB64PPS()+";\r\n";
74 | }
75 |
76 | /**
77 | * Starts the stream.
78 | */
79 | public synchronized void start() throws IllegalStateException, IOException {
80 | if (!mStreaming) {
81 | if (mConfig == null)
82 | updateMP4Config();
83 |
84 | if (mConfig != null) {
85 | byte[] pps = Base64.decode(mConfig.getB64PPS(), Base64.NO_WRAP);
86 | byte[] sps = Base64.decode(mConfig.getB64SPS(), Base64.NO_WRAP);
87 | ((H264Packetizer)mPacketizer).setStreamParameters(pps, sps);
88 | }
89 | }
90 | super.start();
91 | }
92 |
93 | /**
94 | * Configures the stream. You need to call this before calling {@link #getSessionDescription()} to apply
95 | * your configuration of the stream.
96 | */
97 | public synchronized void configure() throws IllegalStateException, IOException {
98 | super.configure();
99 | updateMP4Config();
100 | }
101 |
102 | private void updateMP4Config() throws IllegalStateException, IOException {
103 | mConfig = testH264();
104 | }
105 |
106 | /**
107 | * Tests if streaming with the given configuration (bit rate, frame rate, resolution) is possible
108 | * and determines the pps and sps. Should not be called by the UI thread.
109 | **/
110 | private MP4Config testH264() throws IllegalStateException, IOException {
111 | if (mQuality.screenWidth >= 640) {
112 | // MediaCodec API is too slow for high resolutions
113 | mMode = MODE_MEDIARECORDER_API;
114 | }
115 |
116 | return (mMode == MODE_MEDIARECORDER_API)
117 | ? testMediaRecorderAPI()
118 | : testMediaCodecAPI()
119 | ;
120 | }
121 |
122 | @SuppressLint("NewApi")
123 | private MP4Config testMediaCodecAPI() throws RuntimeException, IOException {
124 | try {
125 | EncoderDebugger debugger = EncoderDebugger.debug(mSettings, mQuality.screenWidth, mQuality.screenHeight);
126 | return new MP4Config(debugger.getB64SPS(), debugger.getB64PPS());
127 | } catch (Exception e) {
128 | Log.e(TAG,"Resolution not supported with the MediaCodec API.");
129 |
130 | // Fallback to the MediaRecorder API
131 | mMode = MODE_MEDIARECORDER_API;
132 | return testH264();
133 | }
134 | }
135 |
136 | // Should not be called by the UI thread
137 | private MP4Config testMediaRecorderAPI() throws RuntimeException, IOException {
138 | String key = PREF_PREFIX+"h264-mr-"+mRequestedQuality.framerate+","+mRequestedQuality.screenWidth+","+mRequestedQuality.screenHeight;
139 |
140 | if (mSettings != null && mSettings.contains(key) ) {
141 | String[] s = mSettings.getString(key, "").split(",");
142 | return new MP4Config(s[0],s[1],s[2]);
143 | }
144 |
145 | if (MEDIA_PROJECTION == null)
146 | throw new RuntimeException("VideoStream requires a MediaProjection");
147 |
148 | if (CACHE_DIR == null)
149 | throw new RuntimeException("Application cache directory is not configured. Context is required.");
150 |
151 | final String TESTFILE = CACHE_DIR.getPath()+"/spydroid-test.mp4";
152 |
153 | Log.i(TAG,"Testing H264 support... Test file saved at: "+TESTFILE);
154 |
155 | try {
156 | File file = new File(TESTFILE);
157 | file.createNewFile();
158 | } catch (IOException e) {
159 | throw e;
160 | }
161 |
162 | MediaRecorder mediaRecorder = null;
163 | Surface videoSurface = null;
164 | VirtualDisplay virtualDisplay = null;
165 |
166 | try {
167 | mediaRecorder = new MediaRecorder();
168 | mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
169 | mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
170 | mediaRecorder.setVideoEncoder(mVideoEncoder);
171 | mediaRecorder.setVideoSize(mRequestedQuality.screenWidth, mRequestedQuality.screenHeight);
172 | mediaRecorder.setVideoFrameRate(mRequestedQuality.framerate);
173 | mediaRecorder.setVideoEncodingBitRate((int)(mRequestedQuality.bitrate*0.8));
174 |
175 | mediaRecorder.setOutputFile(TESTFILE);
176 | mediaRecorder.setMaxDuration(3000);
177 |
178 | // We wait a little and stop recording
179 | mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
180 | public void onInfo(MediaRecorder mr, int what, int extra) {
181 | Log.d(TAG,"MediaRecorder callback called !");
182 | if (what==MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
183 | Log.d(TAG,"MediaRecorder: MAX_DURATION_REACHED");
184 | } else if (what==MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
185 | Log.d(TAG,"MediaRecorder: MAX_FILESIZE_REACHED");
186 | } else if (what==MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN) {
187 | Log.d(TAG,"MediaRecorder: INFO_UNKNOWN");
188 | } else {
189 | Log.d(TAG,"WTF ?");
190 | }
191 | mLock.release();
192 | }
193 | });
194 |
195 | // Start recording
196 | mediaRecorder.prepare();
197 |
198 | videoSurface = mMediaRecorder.getSurface();
199 | mediaRecorder.start();
200 |
201 | int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
202 | virtualDisplay = MEDIA_PROJECTION.createVirtualDisplay("ScreenCaster", mQuality.screenWidth, mQuality.screenHeight, mQuality.screenDpi, flags, videoSurface, /* callback= */ null, /* handler= */ null);
203 |
204 | if (mLock.tryAcquire(6,TimeUnit.SECONDS)) {
205 | Log.d(TAG,"MediaRecorder callback was called :)");
206 | Thread.sleep(400);
207 | } else {
208 | Log.d(TAG,"MediaRecorder callback was not called after 6 seconds... :(");
209 | }
210 | } catch (IOException e) {
211 | throw e;
212 | } catch (RuntimeException e) {
213 | throw e;
214 | } catch (InterruptedException e) {
215 | e.printStackTrace();
216 | } finally {
217 | try {
218 | mediaRecorder.stop();
219 | } catch (Exception e) {}
220 | mediaRecorder.release();
221 | mediaRecorder = null;
222 |
223 | virtualDisplay.release();
224 | virtualDisplay = null;
225 |
226 | videoSurface.release();
227 | videoSurface = null;
228 | }
229 |
230 | // Retrieve SPS & PPS & ProfileId with MP4Config
231 | MP4Config config = new MP4Config(TESTFILE);
232 |
233 | // Delete dummy video
234 | File file = new File(TESTFILE);
235 | if (!file.delete()) Log.e(TAG,"Temp file could not be erased");
236 |
237 | Log.i(TAG,"H264 Test succeded...");
238 |
239 | // Save test result
240 | if (mSettings != null) {
241 | Editor editor = mSettings.edit();
242 | editor.putString(key, config.getProfileLevel()+","+config.getB64SPS()+","+config.getB64PPS());
243 | editor.commit();
244 | }
245 |
246 | return config;
247 | }
248 | }
249 |
--------------------------------------------------------------------------------