├── 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 | --------------------------------------------------------------------------------