├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── xuchongyang
│ │ │ │ └── mediacodecdemo
│ │ │ │ ├── InitSession.java
│ │ │ │ ├── Constant.java
│ │ │ │ ├── jlibrtp
│ │ │ │ ├── PktBufNode.java
│ │ │ │ ├── ValidateParticipantDatabase.java
│ │ │ │ ├── RTPAppIntf.java
│ │ │ │ ├── DebugAppIntf.java
│ │ │ │ ├── RtcpPktAPP.java
│ │ │ │ ├── RTCPAVPFIntf.java
│ │ │ │ ├── RtcpPktBYE.java
│ │ │ │ ├── AppCallerThread.java
│ │ │ │ ├── ValidatePktBuffer.java
│ │ │ │ ├── ValidateStaticProcs.java
│ │ │ │ ├── RtcpPkt.java
│ │ │ │ ├── ValidateRtcpPkt.java
│ │ │ │ ├── RTCPAppIntf.java
│ │ │ │ ├── RtcpPktRTPFB.java
│ │ │ │ ├── RTPReceiverThread.java
│ │ │ │ ├── RtcpPktSR.java
│ │ │ │ ├── StaticProcs.java
│ │ │ │ ├── RtcpPktRR.java
│ │ │ │ ├── RtcpPktSDES.java
│ │ │ │ ├── CompRtcpPkt.java
│ │ │ │ ├── DataFrame.java
│ │ │ │ ├── ParticipantDatabase.java
│ │ │ │ ├── RtpPkt.java
│ │ │ │ ├── RtcpPktPSFB.java
│ │ │ │ └── RTCPReceiverThread.java
│ │ │ │ ├── EncodeThread.java
│ │ │ │ ├── Util.java
│ │ │ │ └── decode
│ │ │ │ ├── NV21Convertor.java
│ │ │ │ └── CodecManager.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── xuchongyang
│ │ │ └── mediacodecdemo
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── xuchongyang
│ │ └── mediacodecdemo
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── modules.xml
├── runConfigurations.xml
└── compiler.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── .gitignore
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MediaCodecDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/InitSession.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xcy396/MediaCodecDemo/HEAD/app/src/main/java/com/xuchongyang/mediacodecdemo/InitSession.java
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 27 08:44:49 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/Constant.java:
--------------------------------------------------------------------------------
1 | package com.xuchongyang.mediacodecdemo;
2 |
3 | /**
4 | * Created by Administrator on 2017/4/29.
5 | */
6 |
7 | public class Constant {
8 | public static int WIDTH = 480;
9 | public static int HEIGHT = 320;
10 | public static int FRAME_RATE = 30;
11 | public static String REMOTE_IP = "192.168.9.107";
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/xuchongyang/mediacodecdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.xuchongyang.mediacodecdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/xuchongyang/mediacodecdemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.xuchongyang.mediacodecdemo;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.xuchongyang.mediacodecdemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/libraries
41 | .idea/misc.xml
42 | .idea/vcs.xml
43 | .idea/codeStyleSettings.xml
44 | .idea/dictionaries/
45 |
46 | # Keystore files
47 | *.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Import file
56 | import-summary.txt
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/xu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "com.xuchongyang.mediacodecdemo"
8 | minSdkVersion 16
9 | targetSdkVersion 22
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.3.1'
28 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 | testCompile 'junit:junit:4.12'
30 | //ButterKnife
31 | compile 'com.jakewharton:butterknife:8.5.1'
32 | compile 'com.jakewharton:butterknife-compiler:8.5.1'
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/PktBufNode.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * This is a four-directional data structures used for
23 | * the frame buffer, i.e. buffer for pkts that need
24 | * to be assimilated into complete frames.
25 | *
26 | * All the actual work is done by PktBuffer.
27 | *
28 | * @author Arne Kepp
29 | *
30 | */
31 | public class PktBufNode {
32 | /** The next node (RTP Timestamp), looking from the back -> next means older */
33 | protected PktBufNode nextFrameQueueNode = null;
34 | /** The previous node (RTP Timestmap), looking from the back -> prev means newer */
35 | protected PktBufNode prevFrameQueueNode = null;
36 | /** The next node within the frame, i.e. higher sequence number, same RTP timestamp */
37 | protected PktBufNode nextFrameNode = null;
38 | /** Number of packets with the same RTP timestamp */
39 | protected int pktCount;
40 | /** The RTP timeStamp associated with this node */
41 | protected long timeStamp;
42 | /** The sequence number associated with this node */
43 | protected int seqNum;
44 | /** The payload, a parsed RTP Packet */
45 | protected RtpPkt pkt = null;
46 |
47 | /**
48 | * Create a new packet buffer node based on a packet
49 | * @param aPkt the packet
50 | */
51 | protected PktBufNode(RtpPkt aPkt) {
52 | pkt = aPkt;
53 | timeStamp = aPkt.getTimeStamp();
54 | seqNum = aPkt.getSeqNumber();
55 | pktCount = 1;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/ValidateParticipantDatabase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.net.DatagramSocket;
22 | import java.net.InetAddress;
23 |
24 | public class ValidateParticipantDatabase {
25 |
26 | /**
27 | * @param args
28 | */
29 | public static void main(String[] args) {
30 | DatagramSocket rtpSocket = null;
31 | DatagramSocket rtcpSocket = null;
32 | try {
33 | rtpSocket = new DatagramSocket(6002);
34 | rtcpSocket = new DatagramSocket(6003);
35 | } catch (Exception e) {
36 | System.out.println("RTPSession failed to obtain port");
37 | }
38 | RTPSession rtpSession = new RTPSession(rtpSocket, rtcpSocket);
39 |
40 | ParticipantDatabase partDb = new ParticipantDatabase(rtpSession);
41 |
42 | Participant part0 = new Participant("127.0.0.1", 4545, 4555);
43 | Participant part1 = new Participant("127.0.0.1", 4546, 4556);
44 | Participant part2 = new Participant("127.0.0.1", 4547, 4556);
45 |
46 | partDb.addParticipant(0,part0);
47 | partDb.addParticipant(0,part1);
48 | partDb.addParticipant(0,part2);
49 |
50 | partDb.debugPrint();
51 |
52 | System.out.println("********************* Removing Participant 1 (4546) ***********************");
53 | partDb.removeParticipant(part1);
54 | partDb.debugPrint();
55 |
56 |
57 | InetAddress inetAdr = null;
58 | try { inetAdr = InetAddress.getByName("127.0.0.1"); } catch (Exception e) { };
59 |
60 | //Participant part3 = partDb.getParticipant(inetAdr);
61 | //part3.ssrc = 12345678;
62 | System.out.println("********************* Updating Participant 3 (4546) ***********************");
63 | //partDb.updateParticipant(part3);
64 |
65 | partDb.debugPrint();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/EncodeThread.java:
--------------------------------------------------------------------------------
1 | package com.xuchongyang.mediacodecdemo;
2 |
3 | import android.media.MediaCodec;
4 | import android.util.Log;
5 | import android.view.Surface;
6 |
7 | import java.nio.ByteBuffer;
8 | import java.util.Arrays;
9 |
10 | /**
11 | * Created by Mark Xu on 17/4/27.
12 | */
13 |
14 | public class EncodeThread extends Thread {
15 | private static final String TAG = "EncodeThread";
16 | private MediaCodec mEncoder;
17 | protected InitSession mInitSession;
18 | protected boolean isEncode = true;
19 | private Surface mSurface;
20 |
21 | public EncodeThread(MediaCodec encoder, Surface surface){
22 | mEncoder = encoder;
23 | mSurface = surface;
24 | }
25 |
26 | @Override
27 | public void run() {
28 | // TODO: 17/6/15 MediaCodec 线程安全?
29 | mInitSession = new InitSession(mSurface);
30 | while (isEncode) {
31 | synchronized (this) {
32 | try {
33 | wait();
34 | } catch (InterruptedException e) {
35 | e.printStackTrace();
36 | }
37 | }
38 | getEncodeData();
39 | }
40 | }
41 |
42 | /**
43 | * 获取编码后的数据并发送
44 | */
45 | private void getEncodeData(){
46 | // TODO: 17/4/28 移到循环外部
47 | ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers();
48 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
49 | int outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
50 | while (outputBufferIndex >= 0 && isEncode) {
51 | ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
52 | byte[] outData = new byte[bufferInfo.size];
53 | outputBuffer.get(outData);
54 | // TODO: 17/6/15 发送问题
55 | sendData(outData);
56 | mEncoder.releaseOutputBuffer(outputBufferIndex, false);
57 | outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
58 | }
59 | }
60 |
61 | /**
62 | * 将每帧进行分包并发送数据
63 | * @param bytes
64 | */
65 | private void sendData(byte[] bytes) {
66 | int dataLength = (bytes.length - 1) / 1480 + 1;
67 | final byte[][] data = new byte[dataLength][];
68 | final boolean[] marks = new boolean[dataLength];
69 | marks[marks.length - 1] = true;
70 | int x = 0;
71 | int y = 0;
72 | int length = bytes.length;
73 | for (int i = 0; i < length; i++){
74 | if (y == 0){
75 | data[x] = new byte[length - i > 1480 ? 1480 : length - i];
76 | }
77 | data[x][y] = bytes[i];
78 | y++;
79 | if (y == data[x].length){
80 | y = 0;
81 | x++;
82 | }
83 | }
84 | // TODO: 17/6/15
85 | mInitSession.rtpSession.sendData(data, null, marks, -1, null);
86 | Log.e(TAG, "sendData: " + Arrays.deepToString(data));
87 | }
88 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RTPAppIntf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | package com.xuchongyang.mediacodecdemo.jlibrtp;
21 |
22 |
23 | /**
24 | * This is the callback interface for RTP packets.
25 | *
26 | * It is mandatory, but you can ignore the data if you like.
27 | *
28 | * @author Arne Kepp
29 | */
30 | public interface RTPAppIntf {
31 |
32 | /**
33 | * The callback method through which the application will receive
34 | * data from jlibrtp. These calls are synchronous, so you will not
35 | * receive any new packets until this call returns.
36 | *
37 | * @param frame the frame containing the data
38 | * @param participant the participant from which the data came
39 | */
40 | public void receiveData(DataFrame frame, Participant participant);
41 |
42 |
43 | /**
44 | * The callback method through which the application will receive
45 | * notifications about user updates, additions and byes.
46 | * Types:
47 | * 1 - Bye
48 | * 2 - New through RTP, check .getRtpSendSock()
49 | * 3 - New through RTCP, check .getRtcpSendSock()
50 | * 4 - SDES packet received, check the getCname() etc methods
51 | * 5 - Matched SSRC to ip-address provided by application
52 | *
53 | * @param type the type of event
54 | * @param participant the participants in question
55 | */
56 | public void userEvent(int type, Participant[] participant);
57 |
58 | /**
59 | * The callback method through which the application can specify
60 | * the number of packets that make up a frame for a given payload type.
61 | *
62 | * A negative value denotes frames of variable length, so jlibrtp
63 | * will return whatever it has at the time.
64 | *
65 | * In most applications, this function can simply return 1.
66 | *
67 | * This should be implemented as something fast, such as an
68 | * integer array with the indeces being the payload type.
69 | *
70 | * @param payloadType the payload type specified in the RTP packet
71 | * @return the number of packets that make up a frame
72 | */
73 | public int frameSize(int payloadType);
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/Util.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2015 duolabao.com All right reserved. This software is the
3 | * confidential and proprietary information of duolabao.com ("Confidential
4 | * Information"). You shall not disclose such Confidential Information and shall
5 | * use it only in accordance with the terms of the license agreement you entered
6 | * into with duolabao.com.
7 | */
8 |
9 | package com.xuchongyang.mediacodecdemo;
10 |
11 | import java.io.FileNotFoundException;
12 | import java.io.FileOutputStream;
13 | import java.io.IOException;
14 | import java.util.Arrays;
15 |
16 | /**
17 | * Util 工具类
18 | */
19 | public class Util {
20 |
21 | /**
22 | * 将YUV420SP数据顺时针旋转90度
23 | *
24 | * @param data 要旋转的数据
25 | * @param imageWidth 要旋转的图片宽度
26 | * @param imageHeight 要旋转的图片高度
27 | * @return 旋转后的数据
28 | */
29 | public static byte[] rotateNV21Degree90(byte[] data, int imageWidth, int imageHeight) {
30 | byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
31 | // Rotate the Y luma
32 | int i = 0;
33 | for (int x = 0; x < imageWidth; x++) {
34 | for (int y = imageHeight - 1; y >= 0; y--) {
35 | yuv[i] = data[y * imageWidth + x];
36 | i++;
37 | }
38 | }
39 | // Rotate the U and V color components
40 | i = imageWidth * imageHeight * 3 / 2 - 1;
41 | for (int x = imageWidth - 1; x > 0; x = x - 2) {
42 | for (int y = 0; y < imageHeight / 2; y++) {
43 | yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
44 | i--;
45 | yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
46 | i--;
47 | }
48 | }
49 | return yuv;
50 | }
51 |
52 | /**
53 | * 保存数据到本地
54 | *
55 | * @param buffer 要保存的数据
56 | * @param offset 要保存数据的起始位置
57 | * @param length 要保存数据长度
58 | * @param path 保存路径
59 | * @param append 是否追加
60 | */
61 | public static void save(byte[] buffer, int offset, int length, String path, boolean append) {
62 | FileOutputStream fos = null;
63 | try {
64 | fos = new FileOutputStream(path, append);
65 | fos.write(buffer, offset, length);
66 | } catch (FileNotFoundException e) {
67 | e.printStackTrace();
68 | } catch (IOException e) {
69 | e.printStackTrace();
70 | } finally {
71 | if (fos != null) {
72 | try {
73 | fos.flush();
74 | fos.close();
75 | } catch (IOException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 | }
80 | }
81 |
82 | /**
83 | * byte 数组合并
84 | * @param first 原始数组
85 | * @param second 合并数组
86 | * @return 合并后数组
87 | */
88 | public static byte[] merge(byte[] first, byte[] second) {
89 | byte[] result = Arrays.copyOf(first, first.length + second.length);
90 | System.arraycopy(second, 0, result, first.length, second.length);
91 | return result;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/DebugAppIntf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 | import java.net.InetSocketAddress;
21 |
22 | /**
23 | * DebugAppIntf can be registered on RTPSession to provide simple
24 | * debugging functionality. This is particularly useful to determine
25 | * whether the client is receing any data at all.
26 | *
27 | * @author Arne Kepp
28 | *
29 | */
30 |
31 | public interface DebugAppIntf {
32 | /**
33 | * This function wil notify you of any packets received, valid or not.
34 | * Useful for network debugging, and finding bugs in jlibrtp.
35 | *
36 | * Type is an integer describing the type of event
37 | * -2 - Invalid RTCP packet received
38 | * -1 - Invalid RTP packet received
39 | * 0 - RTP packet received
40 | * 1 - RTCP packet received
41 | *
42 | * Description is a string that should be meaningful to advanced users, such as
43 | * "RTP packet received from 127.0.0.1:12312, SSRC: 1380912 , payload type 1, packet size 16 octets"
44 | * or
45 | * "Invalid RTP packet received from 127.0.0.1:12312"
46 | *
47 | * This function is synchonous and should return quickly.
48 | *
49 | * @param type , the type of event, see above.
50 | * @param socket , taken directly from the UDP packet
51 | * @param description , see above.
52 | */
53 | public void packetReceived(int type, InetSocketAddress socket, String description);
54 |
55 | /**
56 | * This function will notify you of any packets sent from this instance of RTPSession.
57 | * Useful for network debugging, and finding bugs in jlibrtp.
58 | *
59 | * Type is an integer describing the type of event
60 | * 0 - RTP unicast packet sent
61 | * 1 - RTP multicast packet sent
62 | * 2 - RTCP unicast packet sent
63 | * 3 - RTCP multicast packet sent
64 | *
65 | * Description is a string that should be meaningful to advanced users, such as
66 | *
67 | * This function is synchonous and should return quickly.
68 | *
69 | * @param type , the type of event, see above
70 | * @param socket , taken directly from the UDP packet
71 | * @param description , see above
72 | */
73 | public void packetSent(int type, InetSocketAddress socket, String description);
74 |
75 | /**
76 | * Other important events that can occur in session
77 | * -1 SSRC conflict
78 | * 0 Session is terminating
79 | * @param type see above
80 | * @param description , see above
81 | */
82 | public void importantEvent(int type, String description);
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPktAPP.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * Application specific RTCP packets
23 | *
24 | * @author Arne Kepp
25 | */
26 | public class RtcpPktAPP extends RtcpPkt {
27 | /** Name of packet, 4 bytes ASCII */
28 | protected byte[] pktName = null;
29 | /** Data of packet */
30 | protected byte[] pktData = null;
31 |
32 | /**
33 | * Constructor for a new Application RTCP packet
34 | *
35 | * @param ssrc the SSRC of the sender, presumably taken from RTPSession
36 | * @param subtype the subtype of packet, application specific
37 | * @param pktName byte[4] representing ASCII name of packet
38 | * @param pktData the byte[4x] data that represents the message itself
39 | */
40 | protected RtcpPktAPP(long ssrc, int subtype, byte[] pktName, byte[] pktData) {
41 | // Fetch all the right stuff from the database
42 | super.ssrc = ssrc;
43 | super.packetType = 204;
44 | super.itemCount = subtype;
45 | this.pktName = pktName;
46 | this.pktData = pktData;
47 | }
48 |
49 | /**
50 | * Constructor that parses a received Application RTCP packet
51 | *
52 | * @param aRawPkt the raw packet containing the date
53 | * @param start where in the raw packet this packet starts
54 | */
55 | protected RtcpPktAPP(byte[] aRawPkt, int start) {
56 | super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt,4);
57 | super.rawPkt = aRawPkt;
58 |
59 | if(!super.parseHeaders(start) || packetType != 204 ) {
60 | if(RTPSession.rtpDebugLevel > 2) {
61 | System.out.println(" <-> RtcpPktAPP.parseHeaders() etc. problem");
62 | }
63 | super.problem = -204;
64 | } else {
65 | //System.out.println("super.length: " + super.length);
66 | if(super.length > 1) {
67 | pktName = new byte[4];
68 | System.arraycopy(aRawPkt, 8, pktName, 0, 4);
69 | }
70 | if(super.length > 2) {
71 | pktData = new byte[(super.length+1)*4 - 12];
72 | System.arraycopy(aRawPkt, 12, pktData, 0, pktData.length);
73 | }
74 | }
75 | }
76 |
77 | /**
78 | * Encode the packet into a byte[], saved in .rawPkt
79 | *
80 | * CompRtcpPkt will call this automatically
81 | */
82 | protected void encode() {
83 | super.rawPkt = new byte[12 + this.pktData.length];
84 | byte[] tmp = StaticProcs.uIntLongToByteWord(super.ssrc);
85 | System.arraycopy(tmp, 0, super.rawPkt, 4, 4);
86 | System.arraycopy(this.pktName, 0, super.rawPkt, 8, 4);
87 | System.arraycopy(this.pktData, 0, super.rawPkt, 12, this.pktData.length);
88 | writeHeaders();
89 | //System.out.println("ENCODE: " + super.length + " " + rawPkt.length + " " + pktData.length);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RTCPAVPFIntf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | package com.xuchongyang.mediacodecdemo.jlibrtp;
21 |
22 |
23 | /**
24 | * This is the callback interface for the AVPF profile (RFC 4585)
25 | *
26 | * It is optional, you do not have to register it.
27 | *
28 | * If there are specific events you wish to ignore,
29 | * you can simply implement empty functions.
30 | *
31 | * These are all syncrhonous, make sure to return quickly
32 | * or do the handling in a new thread.
33 | *
34 | * @author Arne Kepp
35 | */
36 | public interface RTCPAVPFIntf {
37 |
38 | /**
39 | * This function is called when a
40 | * Picture Loss Indication (PLI, FMT = 1) is received
41 | *
42 | * @param ssrcPacketSender the SSRC of the participant reporting loss of picture
43 | */
44 | public void PSFBPktPictureLossReceived(
45 | long ssrcPacketSender);
46 |
47 | /**
48 | * This function is called when a
49 | * Slice Loss Indication (SLI, FMT=2) is received
50 | *
51 | * @param ssrcPacketSender the SSRC of the participant reporting loss of slice(s)
52 | * @param sliceFirst macroblock address of first macroblock
53 | * @param sliceNumber number of lost macroblocks, in scan order
54 | * @param slicePictureId the six least significant bits of the picture identifier
55 | */
56 | public void PSFBPktSliceLossIndic(
57 | long ssrcPacketSender,
58 | int[] sliceFirst, int[] sliceNumber, int[] slicePictureId);
59 |
60 | /**
61 | * This function is called when a
62 | * Reference Picture Selection Indication (RPSI, FMT=3) is received
63 | *
64 | * @param ssrcPacketSender the SSRC of the participant reporting the selection
65 | * @param rpsiPayloadType the RTP payload type related to the RPSI bit string
66 | * @param rpsiBitString the RPSI information as natively defined by the video codec
67 | * @param rpsiPaddingBits the number of padding bits at the end of the string
68 | */
69 | public void PSFBPktRefPictureSelIndic(
70 | long ssrcPacketSender,
71 | int rpsiPayloadType, byte[] rpsiBitString, int rpsiPaddingBits);
72 |
73 | /**
74 | * This function is called when a
75 | * Transport Layer Feedback Messages is received
76 | *
77 | * @param ssrcPacketSender
78 | * @param alfBitString
79 | */
80 | public void PSFBPktAppLayerFBReceived(
81 | long ssrcPacketSender,
82 | byte[] alfBitString);
83 |
84 | /**
85 | * This function is called when a
86 | * Transport Layer Feedback Messages is received
87 | *
88 | * @param ssrcPacketSender
89 | * @param FMT 1: NACK, 0,2-30: unassigned, 31: reserved
90 | * @param packetID the RTP sequence number of the lost packet
91 | * @param bitmaskLostPackets the bitmask of following lost packets
92 | */
93 | public void RTPFBPktReceived(
94 | long ssrcPacketSender,
95 | int FMT, int[] packetID, int[] bitmaskLostPackets);
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPktBYE.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * RTCP packets for sending Bye messages
23 | *
24 | * @author Arne Kepp
25 | */
26 | public class RtcpPktBYE extends RtcpPkt {
27 | /** SSRCs saying bye, 32xn bits, n<16 */
28 | protected long[] ssrcArray = null;
29 | /** Optional reason */
30 | protected byte[] reason = null;
31 |
32 | protected RtcpPktBYE(long[] ssrcs,byte[] aReason) {
33 | super.packetType = 203;
34 | // Fetch all the right stuff from the database
35 | reason = aReason;
36 | ssrcArray = ssrcs;
37 | if(ssrcs.length < 1) {
38 | System.out.println("RtcpBYE.RtcpPktBYE(long[] ssrcs, byte[] aReason) requires at least one SSRC!");
39 | }
40 | }
41 |
42 | protected RtcpPktBYE(byte[] aRawPkt, int start) {
43 | rawPkt = aRawPkt;
44 | if(!super.parseHeaders(start) || packetType != 203 ) {
45 | if(RTPSession.rtpDebugLevel > 2) {
46 | System.out.println(" <-> RtcpPktBYE.parseHeaders() etc. problem");
47 | }
48 | super.problem = -203;
49 | } else {
50 | ssrcArray = new long[super.itemCount];
51 |
52 | for(int i=0; i (super.itemCount + 1)) {
56 | int reasonLength = (int) aRawPkt[start + (super.itemCount+1)*4];
57 | //System.out.println("super.itemCount:"+super.itemCount+" reasonLength:"+reasonLength+" start:"+(super.itemCount*4 + 4 + 1));
58 | reason = new byte[reasonLength];
59 | System.arraycopy(aRawPkt, start + (super.itemCount + 1)*4 + 1, reason, 0, reasonLength);
60 | //System.out.println("test:" + new String(reason));
61 | }
62 | }
63 | }
64 |
65 | protected void encode() {
66 | itemCount = ssrcArray.length;
67 | length = 4*ssrcArray.length;
68 |
69 | if(reason != null) {
70 | length += (reason.length + 1)/4;
71 | if((reason.length + 1) % 4 != 0) {
72 | length +=1;
73 | }
74 | }
75 | rawPkt = new byte[length*4 + 4];
76 |
77 | int i;
78 | byte[] someBytes;
79 |
80 | // SSRCs
81 | for(i=0; i 1) {
50 | System.out.println("<-> AppCallerThread created");
51 | }
52 | }
53 |
54 | /**
55 | * The AppCallerThread will run in this loop until the RTPSession
56 | * is terminated.
57 | *
58 | * Whenever an RTP packet is received it will loop over the
59 | * participants to check for packet buffers that have available
60 | * frame.
61 | */
62 | public void run() {
63 | if(RTPSession.rtpDebugLevel > 3) {
64 | System.out.println("-> AppCallerThread.run()");
65 | }
66 |
67 | while(rtpSession.endSession == false) {
68 |
69 | rtpSession.pktBufLock.lock();
70 | try {
71 | if(RTPSession.rtpDebugLevel > 4) {
72 | System.out.println("<-> AppCallerThread going to Sleep");
73 | }
74 |
75 | try { rtpSession.pktBufDataReady.await(); }
76 | catch (Exception e) { System.out.println("AppCallerThread:" + e.getMessage());}
77 |
78 | // Next loop over all participants and check whether they have anything for us.
79 | Enumeration enu = rtpSession.partDb.getParticipants();
80 |
81 | while(enu.hasMoreElements()) {
82 | Participant p = enu.nextElement();
83 |
84 | boolean done = false;
85 | //System.out.println(p.ssrc + " " + !done +" " + p.rtpAddress
86 | // + " " + rtpSession.naiveReception + " " + p.pktBuffer);
87 | //System.out.println("done: " + done + " p.unexpected: " + p.unexpected);
88 | while(!done && (!p.unexpected || rtpSession.naiveReception)
89 | && p.pktBuffer != null && p.pktBuffer.length > 0) {
90 |
91 | DataFrame aFrame = p.pktBuffer.popOldestFrame();
92 | if(aFrame == null) {
93 | done = true;
94 | } else {
95 | appl.receiveData(aFrame, p);
96 | }
97 | }
98 | }
99 |
100 | } finally {
101 | rtpSession.pktBufLock.unlock();
102 | }
103 |
104 | }
105 | if(RTPSession.rtpDebugLevel > 3) {
106 | System.out.println("<- AppCallerThread.run() terminating");
107 | }
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/ValidatePktBuffer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.net.DatagramSocket;
22 |
23 |
24 | /**
25 | * Validates the PktBuffer and associated classes.
26 | *
27 | * @author Arne Kepp
28 | *
29 | */
30 | public class ValidatePktBuffer {
31 |
32 | /**
33 | * Instantiates a buffer, creates some packets, adds them and sorts them.
34 | * @param args
35 | */
36 | public static void main(String[] args) {
37 | // TODO Auto-generated method stub
38 | DatagramSocket rtpSocket = null;
39 | DatagramSocket rtcpSocket = null;
40 | try {
41 | rtpSocket = new DatagramSocket(6002);
42 | rtcpSocket = new DatagramSocket(6003);
43 | } catch (Exception e) {
44 | System.out.println("RTPSession failed to obtain port");
45 | }
46 | RTPSession rtpSession = new RTPSession(rtpSocket, rtcpSocket);
47 |
48 |
49 | String str1 = "ab";
50 | String str2 = "cd";
51 | String str3 = "ef";
52 | String str4 = "gh";
53 | String str5 = "ij";
54 | String str6 = "kl";
55 | //String str7 = "mn";
56 |
57 | long syncSource1 = 1;
58 | int seqNumber1 = 1;
59 | //int seqNumber2 = 1;
60 | RtpPkt pkt1 = new RtpPkt(10, syncSource1, 1, 0, str1.getBytes());
61 | RtpPkt pkt2 = new RtpPkt(20, syncSource1, 2, 0, str2.getBytes());
62 | RtpPkt pkt3 = new RtpPkt(30, syncSource1, 3, 0, str3.getBytes());
63 | RtpPkt pkt4 = new RtpPkt(40, syncSource1, 4, 0, str4.getBytes());
64 | RtpPkt pkt6 = new RtpPkt(60, syncSource1, 6, 0, str5.getBytes());
65 | RtpPkt pkt7 = new RtpPkt(70, syncSource1, 7, 0, str6.getBytes());
66 |
67 | Participant p = new Participant();
68 |
69 | PktBuffer pktBuf = new PktBuffer(rtpSession, p, pkt1);
70 | pktBuf.addPkt(pkt3); //2
71 | pktBuf.addPkt(pkt2); //3
72 | DataFrame aFrame = pktBuf.popOldestFrame();
73 | String outStr = new String(aFrame.getConcatenatedData());
74 | System.out.println("** 1 Data from first frame: " + outStr + ", should be ab");
75 | pktBuf.addPkt(pkt4); //3
76 | pktBuf.addPkt(pkt7); //4
77 | System.out.println("** 1.5 sixth");
78 | pktBuf.addPkt(pkt6); //5
79 | System.out.println("** 2 Duplicate, should be dropped");
80 | pktBuf.addPkt(pkt3); //5
81 | // Pop second frame
82 | aFrame = pktBuf.popOldestFrame(); //4
83 | outStr = new String(aFrame.getConcatenatedData());
84 | System.out.println("** 3 Data from second frame: " + outStr + ", should be cd");
85 |
86 | // Pop third frame
87 | aFrame = pktBuf.popOldestFrame(); //3
88 | outStr = new String(aFrame.getConcatenatedData());
89 | System.out.println("** 4 Data from third frame: " + outStr + ", should be ef");
90 | System.out.println("** 5 pktBuf.getLength is " + pktBuf.getLength() + ", should be 3");
91 |
92 | System.out.println("** 6 Late arrival, should be dropped");
93 | pktBuf.addPkt(pkt2);
94 |
95 | aFrame = pktBuf.popOldestFrame();
96 | outStr = new String(aFrame.getConcatenatedData());
97 | System.out.println("** 7 Data from fourth frame: " + outStr + ", should be gh");
98 |
99 | aFrame = pktBuf.popOldestFrame();
100 | outStr = new String(aFrame.getConcatenatedData());
101 | System.out.println("** 8 Data from fifth frame: " + outStr + ", should be ij");
102 |
103 | aFrame = pktBuf.popOldestFrame();
104 | outStr = new String(aFrame.getConcatenatedData());
105 | System.out.println("** 9 Data from fifth frame: " + outStr + ", should be kl");
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/ValidateStaticProcs.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 |
22 | /**
23 | * Validates the StaticProcs.
24 | *
25 | * @author Arne Kepp
26 | *
27 | */
28 | public class ValidateStaticProcs {
29 |
30 | /**
31 | * @param args
32 | */
33 | public static void main(String[] args) {
34 | // TODO Auto-generated method stub
35 | long one = 100;
36 | long two = 1;
37 | long three = 9999000;
38 |
39 | byte aByte = (byte) 7;
40 | System.out.println("aByte.hex: " + StaticProcs.hexOfByte(aByte));
41 |
42 | //byte[] oneb = StaticProcs.longToByteWord(one);
43 | byte[] twob = StaticProcs.uIntLongToByteWord(two);
44 | //byte[] threeb = StaticProcs.longToByteWord(three);
45 |
46 | for(int i = 0; i< 4; i++) {
47 | StaticProcs.printBits(twob[i]);
48 | }
49 | //one = StaticProcs.combineBytes(oneb[0], oneb[1], oneb[2], oneb[3]);
50 | two = StaticProcs.bytesToUIntLong(twob,0);
51 | //three = StaticProcs.combineBytes(threeb[0], threeb[1], threeb[2], threeb[3]);
52 |
53 | System.out.println(" one " + one + " two " + two + " three " + three);
54 |
55 | twob = StaticProcs.uIntLongToByteWord(two);
56 |
57 | for(int i = 0; i< 4; i++) {
58 | StaticProcs.printBits(twob[i]);
59 | }
60 |
61 | byte[] bytes = new byte[2];
62 | int check = 0;
63 | for(int i=0; i< 65536; i++) {
64 | bytes = StaticProcs.uIntIntToByteWord(i);
65 | check = StaticProcs.bytesToUIntInt(bytes, 0);
66 | if(check != i) {
67 | System.out.println(" oops:" + check +" != "+ i);
68 | StaticProcs.printBits(bytes[0]);
69 | StaticProcs.printBits(bytes[1]);
70 | }
71 | }
72 | int a = 65534;
73 | bytes = StaticProcs.uIntIntToByteWord(a);
74 | StaticProcs.printBits(bytes[0]);
75 | StaticProcs.printBits(bytes[1]);
76 | check = StaticProcs.bytesToUIntInt(bytes, 0);
77 | System.out.println(check);
78 |
79 | byte[] arbytes = new byte[22];
80 | arbytes[13] = -127;
81 | arbytes[14] = 127;
82 | arbytes[15] = -1;
83 | arbytes[16] = 127;
84 | arbytes[17] = -127;
85 | System.out.println("arbitrary length:");
86 | StaticProcs.printBits(arbytes[14]);
87 | StaticProcs.printBits(arbytes[15]);
88 | StaticProcs.printBits(arbytes[16]);
89 | //long arbTest = StaticProcs.bytesToUintLong(arbytes, 14, 16);
90 | //byte[] reArBytes = StaticProcs.uIntLongToByteWord(arbTest);
91 | //System.out.println("arbitrary length recode: " + Long.toString(arbTest));
92 | //StaticProcs.printBits(reArBytes[0]);
93 | //StaticProcs.printBits(reArBytes[1]);
94 | //StaticProcs.printBits(reArBytes[2]);
95 | //StaticProcs.printBits(reArBytes[3]);
96 |
97 | byte[] tmp = new byte[4];
98 | tmp[0] = -127;
99 | tmp[1] = 127;
100 | tmp[2] = -49;
101 | tmp[3] = -1;
102 |
103 | String str2 = "";
104 | for(int i=0; i
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPkt.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.net.InetAddress;
22 |
23 | /**
24 | * Common RTCP packet headers.
25 | *
26 | * @author Arne Kepp
27 | */
28 | public class RtcpPkt {
29 | /** Whether a problem has been encountered during parsing */
30 | protected int problem = 0;
31 | /** The version, always 2, 2 bits */
32 | protected int version = 2;
33 | /** Padding , 1 bit */
34 | protected int padding = 0;
35 | /** Number of items, e.g. receiver report blocks. Usage may vary. 5 bits */
36 | protected int itemCount = 0;
37 | /** The type of RTCP packet, 8 bits */
38 | protected int packetType = -1;
39 | /** The length of the RTCP packet, in 32 bit blocks minus 1. 16 bits*/
40 | protected int length = -1;
41 | /** The ssrc that sent this, usually dictated by RTP Session */
42 | protected long ssrc = -1;
43 |
44 | /** Contains the actual data (eventually) */
45 | protected byte[] rawPkt = null;
46 |
47 | /** Only used for feedback messages: Time message was generated */
48 | protected long time = -1;
49 | /** Only used for feedback message: Whether this packet was received */
50 | protected boolean received = false;
51 |
52 |
53 | /**
54 | * Parses the common header of an RTCP packet
55 | *
56 | * @param start where in this.rawPkt the headers start
57 | * @return true if parsing succeeded and header cheks
58 | */
59 | protected boolean parseHeaders(int start) {
60 | version = ((rawPkt[start+0] & 0xC0) >>> 6);
61 | padding = ((rawPkt[start+0] & 0x20) >>> 5);
62 | itemCount = (rawPkt[start+0] & 0x1F);
63 | packetType = (int) rawPkt[start+1];
64 | if(packetType < 0) {
65 | packetType += 256;
66 | }
67 | length = StaticProcs.bytesToUIntInt(rawPkt, start+2);
68 |
69 | if(RTPSession.rtpDebugLevel > 9) {
70 | System.out.println(" <-> RtcpPkt.parseHeaders() version:"+version+" padding:"+padding+" itemCount:"+itemCount
71 | +" packetType:"+packetType+" length:"+length);
72 | }
73 |
74 | if(packetType > 207 || packetType < 200)
75 | System.out.println("RtcpPkt.parseHeaders problem discovered, packetType " + packetType);
76 |
77 | if(version == 2 && length < 65536) {
78 | return true;
79 | } else {
80 | System.out.println("RtcpPkt.parseHeaders() failed header checks, check size and version");
81 | this.problem = -1;
82 | return false;
83 | }
84 | }
85 | /**
86 | * Writes the common header of RTCP packets.
87 | * The values should be filled in when the packet is initiliazed and this function
88 | * called at the very end of .encode()
89 | */
90 | protected void writeHeaders() {
91 | byte aByte = 0;
92 | aByte |=(version << 6);
93 | aByte |=(padding << 5);
94 | aByte |=(itemCount);
95 | rawPkt[0] = aByte;
96 | aByte = 0;
97 | aByte |= packetType;
98 | rawPkt[1] = aByte;
99 | if(rawPkt.length % 4 != 0)
100 | System.out.println("!!!! RtcpPkt.writeHeaders() rawPkt was not a multiple of 32 bits / 4 octets!");
101 | byte[] someBytes = StaticProcs.uIntIntToByteWord((rawPkt.length / 4) - 1);
102 | rawPkt[2] = someBytes[0];
103 | rawPkt[3] = someBytes[1];
104 | }
105 |
106 | /**
107 | * This is just a dummy to make Eclipse complain less.
108 | */
109 | protected void encode() {
110 | System.out.println("RtcpPkt.encode() should never be invoked!! " + this.packetType);
111 | }
112 |
113 | /**
114 | * Check whether this packet came from the source we expected.
115 | *
116 | * Not currently used!
117 | *
118 | * @param adr address that packet came from
119 | * @param partDb the participant database for the session
120 | * @return true if this packet came from the expected source
121 | */
122 | protected boolean check(InetAddress adr, ParticipantDatabase partDb) {
123 | //Multicast -> We have to be naive
124 | if (partDb.rtpSession.mcSession && adr.equals(partDb.rtpSession.mcGroup))
125 | return true;
126 |
127 | //See whether this participant is known
128 | Participant part = partDb.getParticipant(this.ssrc);
129 | if(part != null && part.rtcpAddress.getAddress().equals(adr))
130 | return true;
131 |
132 | //If not, we should look for someone without SSRC with his ip-address?
133 | return false;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/decode/NV21Convertor.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 com.xuchongyang.mediacodecdemo.decode;
22 |
23 | import android.media.MediaCodecInfo;
24 |
25 | import java.nio.ByteBuffer;
26 |
27 | /**
28 | * Converts from NV21 to YUV420 semi planar or planar.
29 | */
30 | public class NV21Convertor {
31 |
32 | private int mSliceHeight, mHeight;
33 |
34 | private int mStride, mWidth;
35 |
36 | private int mSize;
37 |
38 | private boolean mPlanar, mPanesReversed = false;
39 |
40 | private int mYPadding;
41 |
42 | private byte[] mBuffer;
43 |
44 | ByteBuffer mCopy;
45 |
46 | public void setSize(int width, int height) {
47 | mHeight = height;
48 | mWidth = width;
49 | mSliceHeight = height;
50 | mStride = width;
51 | mSize = mWidth * mHeight;
52 | }
53 |
54 | public void setStride(int width) {
55 | mStride = width;
56 | }
57 |
58 | public void setSliceHeigth(int height) {
59 | mSliceHeight = height;
60 | }
61 |
62 | public void setPlanar(boolean planar) {
63 | mPlanar = planar;
64 | }
65 |
66 | public void setYPadding(int padding) {
67 | mYPadding = padding;
68 | }
69 |
70 | public int getBufferSize() {
71 | return 3 * mSize / 2;
72 | }
73 |
74 | public void setEncoderColorFormat(int colorFormat) {
75 | switch (colorFormat) {
76 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
77 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
78 | case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
79 | setPlanar(false);
80 | break;
81 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
82 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
83 | setPlanar(true);
84 | break;
85 | }
86 | }
87 |
88 | public void setColorPanesReversed(boolean b) {
89 | mPanesReversed = b;
90 | }
91 |
92 | public int getStride() {
93 | return mStride;
94 | }
95 |
96 | public int getSliceHeigth() {
97 | return mSliceHeight;
98 | }
99 |
100 | public int getYPadding() {
101 | return mYPadding;
102 | }
103 |
104 | public boolean getPlanar() {
105 | return mPlanar;
106 | }
107 |
108 | public boolean getUVPanesReversed() {
109 | return mPanesReversed;
110 | }
111 |
112 | public void convert(byte[] data, ByteBuffer buffer) {
113 | byte[] result = convert(data);
114 | int min = buffer.capacity() < data.length ? buffer.capacity() : data.length;
115 | buffer.put(result, 0, min);
116 | }
117 |
118 | public byte[] convert(byte[] data) {
119 |
120 | // A buffer large enough for every case
121 | if (mBuffer == null || mBuffer.length != 3 * mSliceHeight * mStride / 2 + mYPadding) {
122 | mBuffer = new byte[3 * mSliceHeight * mStride / 2 + mYPadding];
123 | }
124 |
125 | if (!mPlanar) {
126 | if (mSliceHeight == mHeight && mStride == mWidth) {
127 | // Swaps U and V
128 | if (!mPanesReversed) {
129 | for (int i = mSize; i < mSize + mSize / 2; i += 2) {
130 | mBuffer[0] = data[i + 1];
131 | data[i + 1] = data[i];
132 | data[i] = mBuffer[0];
133 | }
134 | }
135 | if (mYPadding > 0) {
136 | System.arraycopy(data, 0, mBuffer, 0, mSize);
137 | System.arraycopy(data, mSize, mBuffer, mSize + mYPadding, mSize / 2);
138 | return mBuffer;
139 | }
140 | return data;
141 | }
142 | }
143 | else {
144 | if (mSliceHeight == mHeight && mStride == mWidth) {
145 | // De-interleave U and V
146 | if (!mPanesReversed) {
147 | for (int i = 0; i < mSize / 4; i += 1) {
148 | mBuffer[i] = data[mSize + 2 * i + 1];
149 | mBuffer[mSize / 4 + i] = data[mSize + 2 * i];
150 | }
151 | }
152 | else {
153 | for (int i = 0; i < mSize / 4; i += 1) {
154 | mBuffer[i] = data[mSize + 2 * i];
155 | mBuffer[mSize / 4 + i] = data[mSize + 2 * i + 1];
156 | }
157 | }
158 | if (mYPadding == 0) {
159 | System.arraycopy(mBuffer, 0, data, mSize, mSize / 2);
160 | }
161 | else {
162 | System.arraycopy(data, 0, mBuffer, 0, mSize);
163 | System.arraycopy(mBuffer, 0, mBuffer, mSize + mYPadding, mSize / 2);
164 | return mBuffer;
165 | }
166 | return data;
167 | }
168 | }
169 |
170 | return data;
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/ValidateRtcpPkt.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.net.DatagramSocket;
22 | import java.net.InetSocketAddress;
23 | import java.util.ListIterator;
24 |
25 | public class ValidateRtcpPkt {
26 |
27 | public static void main(String[] args) {
28 | DatagramSocket rtpSock = null;
29 | DatagramSocket rtcpSock = null;
30 |
31 | try {
32 | rtpSock = new DatagramSocket(1233);
33 | rtcpSock = new DatagramSocket(1234);
34 | } catch (Exception e) {
35 | //do nothing
36 | }
37 | RTPSession rtpSession = new RTPSession(rtpSock, rtcpSock);
38 |
39 | System.out.println("************************** SSRC: " + rtpSession.ssrc + " **************************");
40 | ParticipantDatabase partDb = new ParticipantDatabase(rtpSession);
41 | //InetAddress test = InetAddress.getByName("127.0.0.1");
42 | Participant part1 = new Participant("127.0.0.1",12, 34);
43 | Participant part2 = new Participant("127.0.0.2",56, 78);
44 |
45 | part1.ssrc = 123;
46 | part2.ssrc = 345;
47 |
48 | InetSocketAddress testadr = null;
49 |
50 | try {
51 | testadr = InetSocketAddress.createUnresolved("localhost", 12371);
52 | } catch (Exception e) {
53 | // Do nothing
54 | }
55 |
56 | part1.cname = "test3";
57 | part2.cname = "test2";
58 | part1.loc = "1231231231";
59 | part2.loc = "Asker";
60 | part1.phone = "+452 1231231";
61 | part2.phone = "aasdasda.asdasdas";
62 | part1.lastSeqNumber = 111;
63 | part2.lastSeqNumber = 222;
64 | part1.timeStampLSR = 111111;
65 | part2.timeStampLSR = 222222;
66 | partDb.addParticipant(0,part1);
67 | partDb.addParticipant(0,part2);
68 |
69 | Participant[] partArray = new Participant[2];
70 | partArray[0] = part1;
71 | partArray[1] = part2;
72 |
73 | RtcpPktRR rrpkt = new RtcpPktRR(partArray,123456789);
74 | RtcpPktSR srpkt = new RtcpPktSR(rtpSession.ssrc,12,21,rrpkt);
75 | //RtcpPktSR srpkt2 = new RtcpPktSR(rtpSession.ssrc,12,21,null);
76 | //rrpkt = new RtcpPktRR(partArray,1234512311);
77 |
78 | //srpkt.debugPrint();
79 | //rrpkt.debugPrint();
80 |
81 | CompRtcpPkt compkt = new CompRtcpPkt();
82 | compkt.addPacket(srpkt);
83 | compkt.addPacket(rrpkt);
84 | compkt.addPacket(rrpkt);
85 |
86 | byte[] test2 = compkt.encode();
87 | //System.out.print(StaticProcs.bitsOfBytes(test));
88 | System.out.println("****************************** DONE ENCODING *******************************");
89 | CompRtcpPkt decomppkt = new CompRtcpPkt(test2,test2.length,testadr,rtpSession);
90 | System.out.println("****************************** DONE DECODING *******************************");
91 | System.out.println("Problem code:" + decomppkt.problem);
92 |
93 | ListIterator iter = decomppkt.rtcpPkts.listIterator();
94 | int i = 0;
95 |
96 | while(iter.hasNext()) {
97 | System.out.println(" i:" + i + " ");
98 | i++;
99 |
100 | Object aPkt = iter.next();
101 | if( aPkt.getClass() == RtcpPktRR.class) {
102 | RtcpPktRR pkt = (RtcpPktRR) aPkt;
103 | pkt.debugPrint();
104 | } else if(aPkt.getClass() == RtcpPktSR.class) {
105 | RtcpPktSR pkt = (RtcpPktSR) aPkt;
106 | pkt.debugPrint();
107 | }
108 | }
109 |
110 | System.out.println("****************************** BYE *******************************");
111 | long[] tempArray = {rtpSession.ssrc};
112 | byte[] tempReason = "tas".getBytes();
113 | RtcpPktBYE byepkt = new RtcpPktBYE(tempArray,tempReason);
114 | //byepkt.debugPrint();
115 | byepkt.encode();
116 | byte[] rawpktbye = byepkt.rawPkt;
117 |
118 | RtcpPktBYE byepkt2 = new RtcpPktBYE(rawpktbye,0);
119 | byepkt2.debugPrint();
120 |
121 | System.out.println("****************************** SDES *******************************");
122 | RtcpPktSDES sdespkt = new RtcpPktSDES(true,rtpSession,null);
123 | rtpSession.cname = "cname123@localhost";
124 | //rtpSession.loc = "right here";
125 | sdespkt.encode();
126 | //rtpSession.cname = "cname124@localhost";
127 | //rtpSession.loc = "right hera";
128 | byte[] rawpktsdes = sdespkt.rawPkt;
129 | InetSocketAddress tmpAdr = (InetSocketAddress) rtpSock.getLocalSocketAddress();
130 | RtcpPktSDES decsdespkt = new RtcpPktSDES(rawpktsdes, 0, (InetSocketAddress) rtpSock.getLocalSocketAddress() , partDb);
131 | decsdespkt.debugPrint();
132 | //partDb.debugPrint();
133 |
134 | CompRtcpPkt compkt2 = new CompRtcpPkt();
135 | compkt2.addPacket(srpkt);
136 | compkt2.addPacket(sdespkt);
137 | byte[] compkt2Raw = compkt.encode();
138 |
139 | CompRtcpPkt compkt3 = new CompRtcpPkt(compkt2Raw,compkt2Raw.length,tmpAdr,rtpSession);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RTCPAppIntf.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | package com.xuchongyang.mediacodecdemo.jlibrtp;
21 |
22 |
23 | /**
24 | * This is the callback interface for RTCP packets.
25 | *
26 | * It is optional, you do not have to register it.
27 | *
28 | * If there are specific events you wish to ignore,
29 | * you can simply implement empty functions.
30 | *
31 | * These are all syncrhonous, make sure to return quickly
32 | * or do the handling in a new thread.
33 | *
34 | * @author Arne Kepp
35 | */
36 | public interface RTCPAppIntf {
37 |
38 | /**
39 | * This function is called whenever a Sender Report (SR) packet is received
40 | * and returns unmodified values.
41 | *
42 | * A sender report may optionally include Receiver Reports (RR),
43 | * which are returned as arrays. Index i corresponds to the same report
44 | * throughout all of the arrays.
45 | *
46 | * @param ssrc the (SR) SSRC of the sender
47 | * @param ntpHighOrder (SR) NTP high order
48 | * @param ntpLowOrder (SR) NTP low order
49 | * @param rtpTimestamp (SR) RTP timestamp corresponding to the NTP timestamp
50 | * @param packetCount (SR) Packets sent since start of session
51 | * @param octetCount (SR) Octets sent since start of session
52 | * @param reporteeSsrc (RR) SSRC of sender the receiver is reporting in
53 | * @param lossFraction (RR) Loss fraction, see RFC 3550
54 | * @param cumulPacketsLost (RR) Cumulative number of packets lost
55 | * @param extHighSeq (RR) Extended highest sequence RTP packet received
56 | * @param interArrivalJitter (RR) Interarrival jitter, see RFC 3550
57 | * @param lastSRTimeStamp (RR) RTP timestamp when last SR was received
58 | * @param delayLastSR (RR) Delay, in RTP, since last SR was received
59 | */
60 | public void SRPktReceived(long ssrc, long ntpHighOrder, long ntpLowOrder,
61 | long rtpTimestamp, long packetCount, long octetCount,
62 | // Get the receiver reports, if any
63 | long[] reporteeSsrc, int[] lossFraction, int[] cumulPacketsLost, long[] extHighSeq,
64 | long[] interArrivalJitter, long[] lastSRTimeStamp, long[] delayLastSR);
65 |
66 | /**
67 | * This function is called whenever a Receiver Report (SR) packet is received
68 | * and returns unmodified values.
69 | *
70 | * A receiver report may optionally include report blocks,
71 | * which are returned as arrays. Index i corresponds to the same report
72 | * throughout all of the arrays.
73 | *
74 | * @param reporterSsrc SSRC of the receiver reporting
75 | * @param reporteeSsrc (RR) SSRC of sender the receiver is reporting in
76 | * @param lossFraction (RR) Loss fraction, see RFC 3550
77 | * @param cumulPacketsLost (RR) Cumulative number of packets lost
78 | * @param extHighSeq (RR) Extended highest sequence RTP packet received
79 | * @param interArrivalJitter (RR) Interarrival jitter, see RFC 3550
80 | * @param lastSRTimeStamp (RR) RTP timestamp when last SR was received
81 | * @param delayLastSR (RR) Delay, in RTP, since last SR was received
82 | */
83 | public void RRPktReceived(long reporterSsrc, long[] reporteeSsrc,
84 | int[] lossFraction, int[] cumulPacketsLost, long[] extHighSeq,
85 | long[] interArrivalJitter, long[] lastSRTimeStamp, long[] delayLastSR);
86 |
87 | /**
88 | * This function is called whenever a Source Description (SDES) packet is received.
89 | *
90 | * It currently returns the updated participants AFTER they have been updated.
91 | *
92 | * @param relevantParticipants participants mentioned in the SDES packet
93 | */
94 | public void SDESPktReceived(Participant[] relevantParticipants);
95 |
96 | /**
97 | * This function is called whenever a Bye (BYE) packet is received.
98 | *
99 | * The participants will automatically be deleted from the participant
100 | * database after some time, but in the mean time the application may
101 | * still receive RTP packets from this source.
102 | *
103 | * @param relevantParticipants participants whose SSRC was in the packet
104 | * @param reason the reason provided in the packet
105 | */
106 | public void BYEPktReceived(Participant[] relevantParticipants, String reason);
107 |
108 |
109 | /**
110 | * This function is called whenever an Application (APP) packet is received.
111 | *
112 | * @param part the participant associated with the SSRC
113 | * @param subtype specified in the packet
114 | * @param name ASCII description of packet
115 | * @param data in the packet
116 | */
117 | public void APPPktReceived(Participant part, int subtype, byte[] name, byte[] data);
118 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPktRTPFB.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * RTCP packets for RTP Feedback Messages
23 | *
24 | * In line with RFC 4585, this packet currently only supports NACKs
25 | *
26 | * @author Arne Kepp
27 | */
28 | public class RtcpPktRTPFB extends RtcpPkt {
29 | /** If this packet was for a different SSRC */
30 | protected boolean notRelevant = false;
31 | /** SSRC we are sending feeback to */
32 | protected long ssrcMediaSource = -1;
33 | /** RTP sequence numbers of lost packets */
34 | protected int PID[];
35 | /** bitmask of following lost packets, shared index with PID */
36 | protected int BLP[];
37 |
38 | /**
39 | * Constructor for RTP Feedback Message
40 | *
41 | * @param ssrcPacketSender SSRC of sender, taken from RTPSession
42 | * @param ssrcMediaSource SSRC of recipient of this message
43 | * @param FMT the Feedback Message Subtype
44 | * @param PID RTP sequence numbers of lost packets
45 | * @param BLP bitmask of following lost packets, shared index with PID
46 | */
47 | protected RtcpPktRTPFB(long ssrcPacketSender, long ssrcMediaSource, int FMT, int[] PID, int[] BLP) {
48 | super.packetType = 205; //RTPFB
49 | super.itemCount = FMT;
50 | this.PID = PID;
51 | this.BLP = BLP;
52 | }
53 |
54 | /**
55 | * Constructor that parses a raw packet to retrieve information
56 | *
57 | * @param aRawPkt the raw packet to be parsed
58 | * @param start the start of the packet, in bytes
59 | * @param rtpSession the session on which the callback interface resides
60 | */
61 | protected RtcpPktRTPFB(byte[] aRawPkt, int start, RTPSession rtpSession) {
62 | if(RTPSession.rtpDebugLevel > 8) {
63 | System.out.println(" -> RtcpPktRTPFB(byte[], int start)");
64 | }
65 |
66 | rawPkt = aRawPkt;
67 |
68 | if(! super.parseHeaders(start) || packetType != 205 || super.length < 2) {
69 | if(RTPSession.rtpDebugLevel > 2) {
70 | System.out.println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem");
71 | }
72 | super.problem = -205;
73 | } else {
74 | //FMT = super.itemCount;
75 |
76 | ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt,8+start);
77 |
78 | if(ssrcMediaSource == rtpSession.ssrc) {
79 | super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt,4+start);
80 | int loopStop = super.length - 2;
81 | PID = new int[loopStop];
82 | BLP = new int[loopStop];
83 | int curStart = 12;
84 |
85 | // Loop over Feedback Control Information (FCI) fields
86 | for(int i=0; i< loopStop; i++) {
87 | PID[i] = StaticProcs.bytesToUIntInt(aRawPkt, curStart);
88 | BLP[i] = StaticProcs.bytesToUIntInt(aRawPkt, curStart + 2);
89 | curStart += 4;
90 | }
91 |
92 | rtpSession.rtcpAVPFIntf.RTPFBPktReceived(
93 | super.ssrc, super.itemCount, PID, BLP);
94 | }
95 | }
96 |
97 |
98 |
99 | if(RTPSession.rtpDebugLevel > 8) {
100 | System.out.println(" <- RtcpPktRTPFB()");
101 | }
102 | }
103 |
104 | /**
105 | * Encode the packet into a byte[], saved in .rawPkt
106 | *
107 | * CompRtcpPkt will call this automatically
108 | */
109 | protected void encode() {
110 | super.rawPkt = new byte[12 + this.PID.length*4];
111 |
112 | byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
113 | System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
114 | someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource);
115 | System.arraycopy(someBytes, 0, super.rawPkt, 8, 4);
116 |
117 | // Loop over Feedback Control Information (FCI) fields
118 | int curStart = 12;
119 | for(int i=0; i < this.PID.length; i++ ) {
120 | someBytes = StaticProcs.uIntIntToByteWord(PID[i]);
121 | super.rawPkt[curStart++] = someBytes[0];
122 | super.rawPkt[curStart++] = someBytes[1];
123 | someBytes = StaticProcs.uIntIntToByteWord(BLP[i]);
124 | super.rawPkt[curStart++] = someBytes[0];
125 | super.rawPkt[curStart++] = someBytes[1];
126 | }
127 | writeHeaders();
128 | }
129 |
130 | /**
131 | * Get the FMT (Feedback Message Type)
132 | * @return value stored in .itemcount, same field
133 | */
134 | protected int getFMT() {
135 | return this.itemCount;
136 | }
137 |
138 | /**
139 | * Debug purposes only
140 | */
141 | protected void debugPrint() {
142 | System.out.println("->RtcpPktRTPFB.debugPrint() ");
143 | System.out.println(" ssrcPacketSender: " + super.ssrc + " ssrcMediaSource: " + ssrcMediaSource);
144 |
145 | if(this.PID != null && this.PID.length < 1) {
146 | System.out.println(" No Feedback Control Information (FCI) fields");
147 | }
148 |
149 | for(int i=0; i < this.PID.length; i++ ) {
150 | System.out.println(" FCI -> PID: " + PID[i] + " BLP: " + BLP[i]);
151 | }
152 | System.out.println("<-RtcpPktRTPFB.debugPrint() ");
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/decode/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 com.xuchongyang.mediacodecdemo.decode;
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();
116 |
117 | // We loop through the decoders, apparently this can take up to a sec (testes on a GS3)
118 | for(int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--){
119 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
120 | if (codecInfo.isEncoder()) continue;
121 |
122 | String[] types = codecInfo.getSupportedTypes();
123 | for (int i = 0; i < types.length; i++) {
124 | if (types[i].equalsIgnoreCase(mimeType)) {
125 | try {
126 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
127 | Set formats = new HashSet();
128 |
129 | // And through the color formats supported
130 | for (int k = 0; k < capabilities.colorFormats.length; k++) {
131 | int format = capabilities.colorFormats[k];
132 |
133 | for (int l=0;l 1) {
42 | System.out.println("<-> RTPReceiverThread created");
43 | }
44 | }
45 |
46 | public void run() {
47 | if(RTPSession.rtpDebugLevel > 1) {
48 | if(rtpSession.mcSession) {
49 | System.out.println("-> RTPReceiverThread.run() starting on MC " + rtpSession.rtpMCSock.getLocalPort() );
50 | } else {
51 | System.out.println("-> RTPReceiverThread.run() starting on " + rtpSession.rtpSock.getLocalPort() );
52 | }
53 | }
54 |
55 | while(!rtpSession.endSession) {
56 | if(RTPSession.rtpDebugLevel > 6) {
57 | if(rtpSession.mcSession) {
58 | System.out.println("-> RTPReceiverThread.run() waiting for MC packet on " + rtpSession.rtpMCSock.getLocalPort() );
59 | } else {
60 | System.out.println("-> RTPReceiverThread.run() waiting for packet on " + rtpSession.rtpSock.getLocalPort() );
61 | }
62 | }
63 |
64 | // Prepare a packet
65 | byte[] rawPkt = new byte[1500];
66 | DatagramPacket packet = new DatagramPacket(rawPkt, rawPkt.length);
67 | // Wait for it to arrive
68 | if(! rtpSession.mcSession) {
69 | //Unicast
70 | try {
71 | rtpSession.rtpSock.receive(packet);
72 | } catch (IOException e) {
73 | if(!rtpSession.endSession) {
74 | e.printStackTrace();
75 | } else {
76 | continue;
77 | }
78 | }
79 | } else {
80 | //Multicast
81 | try {
82 | rtpSession.rtpMCSock.receive(packet);
83 | } catch (IOException e) {
84 | if(!rtpSession.endSession) {
85 | e.printStackTrace();
86 | } else {
87 | continue;
88 | }
89 | }
90 | }
91 |
92 | // Parse the received RTP (?) packet
93 | RtpPkt pkt = new RtpPkt(rawPkt, packet.getLength());
94 |
95 | // Check whether it was valid.
96 | if(pkt == null) {
97 | System.out.println("Received invalid RTP packet. Ignoring");
98 | continue;
99 | }
100 |
101 | long pktSsrc = pkt.getSsrc();
102 |
103 | // Check for loops and SSRC collisions
104 | if( rtpSession.ssrc == pktSsrc )
105 | rtpSession.resolveSsrcConflict();
106 |
107 | long[] csrcArray = pkt.getCsrcArray();
108 | if( csrcArray != null) {
109 | for(int i=0; i< csrcArray.length; i++) {
110 | if(csrcArray[i] == rtpSession.ssrc);
111 | rtpSession.resolveSsrcConflict();
112 | }
113 | }
114 |
115 | if(RTPSession.rtpDebugLevel > 17) {
116 | System.out.println("-> RTPReceiverThread.run() rcvd packet, seqNum " + pktSsrc );
117 | if(RTPSession.rtpDebugLevel > 10) {
118 | String str = new String(pkt.getPayload());
119 | System.out.println("-> RTPReceiverThread.run() payload is " + str );
120 | }
121 | }
122 |
123 | //Find the participant in the database based on SSRC
124 | Participant part = rtpSession.partDb.getParticipant(pktSsrc);
125 |
126 | if(part == null) {
127 | InetSocketAddress nullSocket = null;
128 | part = new Participant((InetSocketAddress) packet.getSocketAddress(), nullSocket, pkt.getSsrc());
129 | part.unexpected = true;
130 | rtpSession.partDb.addParticipant(1,part);
131 | }
132 |
133 | // Do checks on whether the datagram came from the expected source for that SSRC.
134 | if(part.rtpAddress == null || packet.getAddress().equals(part.rtpAddress.getAddress())) {
135 | PktBuffer pktBuffer = part.pktBuffer;
136 |
137 | if(pktBuffer != null) {
138 | //A buffer already exists, append to it
139 | pktBuffer.addPkt(pkt);
140 | } else {
141 | // Create a new packet/frame buffer
142 | pktBuffer = new PktBuffer(this.rtpSession, part,pkt);
143 | part.pktBuffer = pktBuffer;
144 | }
145 | } else {
146 | System.out.println("RTPReceiverThread: Got an unexpected packet from " + pkt.getSsrc()
147 | + " the sending ip-address was " + packet.getAddress().toString()
148 | + ", we expected from " + part.rtpAddress.toString());
149 | }
150 |
151 | // Statistics for receiver report.
152 | part.updateRRStats(packet.getLength(), pkt);
153 | // Upate liveness
154 | part.lastRtpPkt = System.currentTimeMillis();
155 |
156 | if(RTPSession.rtpDebugLevel > 5) {
157 | System.out.println("<-> RTPReceiverThread signalling pktBufDataReady");
158 | }
159 |
160 | // Signal the thread that pushes data to application
161 | rtpSession.pktBufLock.lock();
162 | try { rtpSession.pktBufDataReady.signalAll(); } finally {
163 | rtpSession.pktBufLock.unlock();
164 | }
165 |
166 | }
167 | }
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPktSR.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * RTCP packets for Sender Reports
23 | *
24 | * @author Arne Kepp
25 | */
26 | public class RtcpPktSR extends RtcpPkt {
27 | /** NTP timestamp, MSB */
28 | protected long ntpTs1 = -1; //32 bits
29 | /** NTP timestamp, LSB */
30 | protected long ntpTs2 = -1; //32 bits
31 | /** RTP timestamp */
32 | protected long rtpTs = -1; //32 bits
33 | /** Senders packet count */
34 | protected long sendersPktCount = -1; //32 bits
35 | /** Senders octet count */
36 | protected long sendersOctCount = -1; //32 bits
37 | /** RR packet with receiver reports that we can append */
38 | protected RtcpPktRR rReports = null;
39 |
40 | /**
41 | * Constructor for a new Sender Report packet
42 | *
43 | * @param ssrc the senders SSRC, presumably from RTPSession
44 | * @param pktCount packets sent in this session
45 | * @param octCount octets sent in this session
46 | * @param rReports receiver reports, as RR packets, to be included in this packet
47 | */
48 | protected RtcpPktSR(long ssrc, long pktCount, long octCount, RtcpPktRR rReports) {
49 | // Fetch all the right stuff from the database
50 | super.ssrc = ssrc;
51 | super.packetType = 200;
52 | sendersPktCount = pktCount;
53 | sendersOctCount = octCount;
54 | this.rReports = rReports;
55 | }
56 |
57 | /**
58 | * Constructor that parses a received packet
59 | *
60 | * @param aRawPkt the raw packet
61 | * @param start the position at which SR starts
62 | * @param length used to determine number of included receiver reports
63 | */
64 | protected RtcpPktSR(byte[] aRawPkt, int start, int length) {
65 | if(RTPSession.rtpDebugLevel > 9) {
66 | System.out.println(" -> RtcpPktSR(rawPkt)");
67 | }
68 |
69 | super.rawPkt = aRawPkt;
70 |
71 | if(!super.parseHeaders(start) || packetType != 200 ) {
72 | if(RTPSession.rtpDebugLevel > 2) {
73 | System.out.println(" <-> RtcpPktSR.parseHeaders() etc. problem: "+ (!super.parseHeaders(start) ) + " " + packetType + " " + super.length);
74 | }
75 | super.problem = -200;
76 | } else {
77 | super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt,4+start);
78 | if(length > 11)
79 | ntpTs1 = StaticProcs.bytesToUIntLong(aRawPkt,8+start);
80 | if(length > 15)
81 | ntpTs2 = StaticProcs.bytesToUIntLong(aRawPkt,12+start);
82 | if(length > 19)
83 | rtpTs = StaticProcs.bytesToUIntLong(aRawPkt,16+start);
84 | if(length > 23)
85 | sendersPktCount = StaticProcs.bytesToUIntLong(aRawPkt,20+start);
86 | if(length > 27)
87 | sendersOctCount = StaticProcs.bytesToUIntLong(aRawPkt,24+start);
88 |
89 | // RRs attached?
90 | if(itemCount > 0) {
91 | rReports = new RtcpPktRR(rawPkt,start,itemCount);
92 | }
93 | }
94 |
95 | if(RTPSession.rtpDebugLevel > 9) {
96 | System.out.println(" <- RtcpPktSR(rawPkt)");
97 | }
98 | }
99 |
100 | /**
101 | * Encode the packet into a byte[], saved in .rawPkt
102 | *
103 | * CompRtcpPkt will call this automatically
104 | */
105 | protected void encode() {
106 | if(RTPSession.rtpDebugLevel > 9) {
107 | if(this.rReports != null) {
108 | System.out.println(" -> RtcpPktSR.encode() receptionReports.length: " + this.rReports.length );
109 | } else {
110 | System.out.println(" -> RtcpPktSR.encode() receptionReports: null");
111 | }
112 | }
113 |
114 | if(this.rReports != null) {
115 | super.itemCount = this.rReports.reportees.length;
116 |
117 | byte[] tmp = this.rReports.encodeRR();
118 | super.rawPkt = new byte[tmp.length+28];
119 | //super.length = (super.rawPkt.length / 4) - 1;
120 |
121 | System.arraycopy(tmp, 0, super.rawPkt, 28, tmp.length);
122 |
123 | } else {
124 | super.itemCount = 0;
125 | super.rawPkt = new byte[28];
126 | //super.length = 6;
127 | }
128 | //Write the common header
129 | super.writeHeaders();
130 |
131 | // Convert to NTP and chop up
132 | long timeNow = System.currentTimeMillis();
133 | ntpTs1 = 2208988800L + (timeNow/1000);
134 | long ms = timeNow % 1000;
135 | double tmp = ((double)ms) / 1000.0;
136 | tmp = tmp * (double)4294967295L;
137 | ntpTs2 = (long) tmp;
138 | rtpTs = System.currentTimeMillis();
139 |
140 | //Write SR stuff
141 | byte[] someBytes;
142 | someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
143 | System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
144 | someBytes = StaticProcs.uIntLongToByteWord(ntpTs1);
145 | System.arraycopy(someBytes, 0, super.rawPkt, 8, 4);
146 | someBytes = StaticProcs.uIntLongToByteWord(ntpTs2);
147 | System.arraycopy(someBytes, 0, super.rawPkt, 12, 4);
148 | someBytes = StaticProcs.uIntLongToByteWord(rtpTs);
149 | System.arraycopy(someBytes, 0, super.rawPkt, 16, 4);
150 | someBytes = StaticProcs.uIntLongToByteWord(sendersPktCount);
151 | System.arraycopy(someBytes, 0, super.rawPkt, 20, 4);
152 | someBytes = StaticProcs.uIntLongToByteWord(sendersOctCount);
153 | System.arraycopy(someBytes, 0, super.rawPkt, 24, 4);
154 |
155 | if(RTPSession.rtpDebugLevel > 9) {
156 | System.out.println(" <- RtcpPktSR.encode() ntpTs1: "
157 | + Long.toString(ntpTs1) + " ntpTs2: " + Long.toString(ntpTs2));
158 | }
159 | }
160 |
161 | /**
162 | * Debug purposes only
163 | */
164 | public void debugPrint() {
165 | System.out.println("RtcpPktSR.debugPrint() ");
166 | System.out.println(" SSRC:"+Long.toString(super.ssrc) +" ntpTs1:"+Long.toString(ntpTs1)
167 | +" ntpTS2:"+Long.toString(ntpTs2)+" rtpTS:"+Long.toString(rtpTs)
168 | +" senderPktCount:"+Long.toString(sendersPktCount)+" sendersOctetCount:"
169 | +Long.toString(sendersOctCount));
170 | if(this.rReports != null) {
171 | System.out.print(" Part of Sender Report: ");
172 | this.rReports.debugPrint();
173 | System.out.println(" End Sender Report");
174 | } else {
175 | System.out.println("No Receiver Reports associated with this Sender Report.");
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/StaticProcs.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * Generic functions for converting between unsigned integers and byte[]s.
23 | *
24 | * @author Arne Kepp
25 | */
26 | public class StaticProcs {
27 |
28 | /**
29 | * Converts an integer into an array of bytes.
30 | * Primarily used for 16 bit unsigned integers, ignore the first two octets.
31 | *
32 | * @param i a 16 bit unsigned integer in an int
33 | * @return byte[2] representing the integer as unsigned, most significant bit first.
34 | */
35 | public static byte[] uIntIntToByteWord(int i) {
36 | byte[] byteWord = new byte[2];
37 | byteWord[0] = (byte) ((i >> 8) & 0x000000FF);
38 | byteWord[1] = (byte) (i & 0x00FF);
39 | return byteWord;
40 | }
41 |
42 | /**
43 | * Converts an unsigned 32 bit integer, stored in a long, into an array of bytes.
44 | *
45 | * @param j a long
46 | * @return byte[4] representing the unsigned integer, most significant bit first.
47 | */
48 | public static byte[] uIntLongToByteWord(long j) {
49 | int i = (int) j;
50 | byte[] byteWord = new byte[4];
51 | byteWord[0] = (byte) ((i >>> 24) & 0x000000FF);
52 | byteWord[1] = (byte) ((i >> 16) & 0x000000FF);
53 | byteWord[2] = (byte) ((i >> 8) & 0x000000FF);
54 | byteWord[3] = (byte) (i & 0x00FF);
55 | return byteWord;
56 | }
57 |
58 | /**
59 | * Combines two bytes (most significant bit first) into a 16 bit unsigned integer.
60 | *
61 | * @param index of most significant byte
62 | * @return int with the 16 bit unsigned integer
63 | */
64 | public static int bytesToUIntInt(byte[] bytes, int index) {
65 | int accum = 0;
66 | int i = 1;
67 | for (int shiftBy = 0; shiftBy < 16; shiftBy += 8 ) {
68 | accum |= ( (long)( bytes[index + i] & 0xff ) ) << shiftBy;
69 | i--;
70 | }
71 | return accum;
72 | }
73 |
74 | /**
75 | * Combines four bytes (most significant bit first) into a 32 bit unsigned integer.
76 | *
77 | * @param bytes
78 | * @param index of most significant byte
79 | * @return long with the 32 bit unsigned integer
80 | */
81 | public static long bytesToUIntLong(byte[] bytes, int index) {
82 | long accum = 0;
83 | int i = 3;
84 | for (int shiftBy = 0; shiftBy < 32; shiftBy += 8 ) {
85 | accum |= ( (long)( bytes[index + i] & 0xff ) ) << shiftBy;
86 | i--;
87 | }
88 | return accum;
89 | }
90 |
91 | /**
92 | * Converts an arbitrary number of bytes, assumed to represent an unsigned integer,
93 | * to a Java long
94 | */
95 | /*public static long bytesToUintLong(byte[] bytes, int firstByte, int lastByte) {
96 | long accum = 0;
97 | int i = lastByte - firstByte;
98 | if(i > 7) {
99 | System.out.println("!!!! StaticProcs.bytesToUintLong() Can't convert more than 63 bits!");
100 | return -1;
101 | }
102 | int stop = (i+1)*8;
103 |
104 | for (int shiftBy = 0; shiftBy < stop; shiftBy += 8 ) {
105 | accum |= ( (long)( bytes[firstByte + i] & 0xff ) ) << shiftBy;
106 | i--;
107 | }
108 | return accum;
109 | }*/
110 |
111 | /**
112 | * Converts an arbitrary number of bytes, assumed to represent an unsigned integer,
113 | * to a Java int
114 | */
115 | /* public static int bytesToUintInt(byte[] bytes, int firstByte, int lastByte) {
116 | int accum = 0;
117 | int i = lastByte - firstByte;
118 | if(i > 3) {
119 | System.out.println("!!!! StaticProcs.bytesToUintLong() Can't convert more than 31 bits!");
120 | return -1;
121 | }
122 | int stop = (i+1)*8;
123 |
124 | for (int shiftBy = 0; shiftBy < stop; shiftBy += 8 ) {
125 | accum |= ( (long)( bytes[firstByte + i] & 0xff ) ) << shiftBy;
126 | i--;
127 | }
128 | return accum;
129 | }*/
130 |
131 | /**
132 | * Recreates a UNIX timestamp based on the NTP representation used
133 | * in RTCP SR packets
134 | *
135 | * @param ntpTs1 from RTCP SR packet
136 | * @param ntpTs2 from RTCP SR packet
137 | * @return the UNIX timestamp
138 | */
139 | public static long undoNtpMess(long ntpTs1, long ntpTs2) {
140 | long timeVal = (ntpTs1 - 2208988800L)*1000;
141 |
142 | double tmp = (1000.0*(double)ntpTs2)/((double)4294967295L);
143 | long ms = (long) tmp;
144 | //System.out.println(" timeVal: " +Long.toString(timeVal)+ " ms " + Long.toString(ms));
145 | timeVal += ms;
146 |
147 | return timeVal;
148 | }
149 |
150 |
151 |
152 | /**
153 | * Get the bits of a byte
154 | *
155 | * @param aByte the byte you wish to convert
156 | * @return a String of 1's and 0's
157 | */
158 | public static String bitsOfByte(byte aByte) {
159 | int temp;
160 | String out = "";
161 | for(int i=7; i>=0; i--) {
162 | temp = (aByte >>> i);
163 | temp &= 0x0001;
164 | out += (""+temp);
165 | }
166 | return out;
167 | }
168 |
169 | /**
170 | * Get the hex representation of a byte
171 | *
172 | * @param aByte the byte you wish to convert
173 | * @return a String of two chars 0-1,A-F
174 | */
175 | public static String hexOfByte(byte aByte) {
176 | String out = "";
177 |
178 | for(int i=0; i<2; i++) {
179 | int temp = (int) aByte;
180 | if(temp < 0) {
181 | temp +=256;
182 | }
183 | if(i == 0) {
184 | temp = temp/16;
185 | } else {
186 | temp = temp%16;
187 | }
188 |
189 | if( temp > 9) {
190 | switch(temp) {
191 | case 10: out += "A"; break;
192 | case 11: out += "B"; break;
193 | case 12: out += "C"; break;
194 | case 13: out += "D"; break;
195 | case 14: out += "E"; break;
196 | case 15: out += "F"; break;
197 | }
198 | } else {
199 | out += Integer.toString(temp);
200 | }
201 | }
202 | return out;
203 | }
204 |
205 | /**
206 | * Get the hex representation of a byte
207 | *
208 | * @param hex 4 bytes the byte you wish to convert
209 | * @return a String of two chars 0-1,A-F
210 | */
211 | public static byte byteOfHex(byte[] hex) {
212 | byte retByte = 0;
213 | Byte tmp;
214 | int val = 0;
215 |
216 | // First 4 bits
217 | tmp = hex[0];
218 | val = tmp.intValue();
219 | if(val > 64) {
220 | // Letter
221 | val -= 55;
222 | } else {
223 | // Number
224 | val -= 48;
225 | }
226 | retByte = ((byte) (val << 4));
227 |
228 | // Last 4 bits
229 | tmp = hex[1];
230 | val = tmp.intValue();
231 | if(val > 64) {
232 | // Letter
233 | val -= 55;
234 | } else {
235 | // Number
236 | val -= 48;
237 | }
238 | retByte |= ((byte) val);
239 |
240 | return retByte;
241 | }
242 |
243 |
244 | /**
245 | * Print the bits of a byte to standard out. For debugging.
246 | *
247 | * @param aByte the byte you wish to print out.
248 | */
249 | public static void printBits(byte aByte) {
250 | int temp;
251 | for(int i=7; i>=0; i--) {
252 | temp = (aByte >>> i);
253 | temp &= 0x0001;
254 | System.out.print(""+temp);
255 | }
256 | System.out.println();
257 | }
258 |
259 | public static String bitsOfBytes(byte[] bytes) {
260 | String str = "";
261 | //Expensive, but who cares
262 | for(int i=0; i 2) {
75 | System.out.println(" <-> RtcpPktRR.parseHeaders() etc. problem: "+(!super.parseHeaders(start))+" "+packetType+" "+super.length);
76 | }
77 | super.problem = -201;
78 | }
79 |
80 | int base;
81 | if(rrCount > 0) {
82 | base = start + 28;
83 | } else {
84 | base = start + 8;
85 | rrCount = super.itemCount;
86 | super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, start + 4);
87 | }
88 |
89 | if(rrCount > 0) {
90 | reporteeSsrc = new long[rrCount];
91 | lossFraction = new int[rrCount];
92 | lostPktCount = new int[rrCount];
93 | extHighSeqRecv = new long[rrCount];
94 | interArvJitter = new long[rrCount];
95 | timeStampLSR = new long[rrCount];
96 | delaySR = new long[rrCount];
97 |
98 | for(int i=0; i 9) {
119 | System.out.println(" -> RtcpPktRR.encode()");
120 | }
121 |
122 | byte[] rRs = null;
123 | //Gather up the actual receiver reports
124 | if(this.reportees != null) {
125 | rRs = this.encodeRR();
126 | super.rawPkt = new byte[rRs.length + 8];
127 | System.arraycopy(rRs, 0, super.rawPkt, 8, rRs.length);
128 | super.itemCount = reportees.length;
129 | } else {
130 | super.rawPkt = new byte[8];
131 | super.itemCount = 0;
132 | }
133 |
134 | //Write the common header
135 | super.writeHeaders();
136 |
137 | //Add our SSRC (as sender)
138 | byte[] someBytes;
139 | someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
140 | System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
141 |
142 | if(RTPSession.rtpDebugLevel > 9) {
143 | System.out.println(" <- RtcpPktRR.encode()");
144 | }
145 |
146 | }
147 |
148 | /**
149 | * Encodes the individual Receiver Report blocks,
150 | *
151 | * so they can be used either in RR packets or appended to SR
152 | *
153 | * @return the encoded packets
154 | */
155 | protected byte[] encodeRR() {
156 | if(RTPSession.rtpDebugLevel > 10) {
157 | System.out.println(" -> RtcpPktRR.encodeRR()");
158 | }
159 | //assuming we will always create complete reports:
160 | byte[] ret = new byte[24*reportees.length];
161 |
162 | //Write SR stuff
163 | for(int i = 0; i= 0) {
182 | someBytes = StaticProcs.uIntLongToByteWord((long)reportees[i].interArrivalJitter);
183 | } else {
184 | someBytes = StaticProcs.uIntLongToByteWord((long) 0);
185 | }
186 | System.arraycopy(someBytes, 0, ret, 12 + offset, 4);
187 |
188 | // Timestamp last sender report received
189 | someBytes = StaticProcs.uIntLongToByteWord(reportees[i].timeStampLSR);
190 | System.arraycopy(someBytes, 0, ret, 16 + offset, 4);
191 |
192 | // Delay since last sender report received, in terms of 1/655536 s = 0.02 ms
193 | if(reportees[i].timeReceivedLSR > 0) {
194 | someBytes = StaticProcs.uIntLongToByteWord(reportees[i].delaySinceLastSR());
195 | } else {
196 | someBytes = StaticProcs.uIntLongToByteWord(0);
197 | }
198 | System.arraycopy(someBytes, 0, ret, 20 + offset, 4);
199 | }
200 | if(RTPSession.rtpDebugLevel > 10) {
201 | System.out.println(" <- RtcpPktRR.encodeRR()");
202 | }
203 | return ret;
204 | }
205 |
206 | /**
207 | * Debug purposes only
208 | */
209 | public void debugPrint() {
210 | System.out.println("RtcpPktRR.debugPrint() ");
211 | if(reportees != null) {
212 | for(int i= 0; i 8) {
66 | System.out.println(" -> RtcpPktSDES(byte[], ParticipantDabase)");
67 | }
68 | rawPkt = aRawPkt;
69 |
70 | if(! super.parseHeaders(start) || packetType != 202 ) {
71 | if(RTPSession.rtpDebugLevel > 2) {
72 | System.out.println(" <-> RtcpPktSDES.parseHeaders() etc. problem");
73 | }
74 | super.problem = -202;
75 | } else {
76 | //System.out.println(" DECODE SIZE: " + super.length + " itemcount " + itemCount );
77 |
78 | int curPos = 4 + start;
79 | int curLength;
80 | int curType;
81 | long ssrc;
82 | boolean endReached = false;
83 | boolean newPart;
84 | this.participants = new Participant[itemCount];
85 |
86 | // Loop over SSRC SDES chunks
87 | for(int i=0; i< itemCount; i++) {
88 | ssrc = StaticProcs.bytesToUIntLong(aRawPkt, curPos);
89 | Participant part = partDb.getParticipant(ssrc);
90 | if(part == null) {
91 | if(RTPSession.rtcpDebugLevel > 1) {
92 | System.out.println("RtcpPktSDES(byte[], ParticipantDabase) adding new participant, ssrc:"+ssrc+" "+socket);
93 | }
94 |
95 | part = new Participant(socket, socket , ssrc);
96 | newPart = true;
97 | } else {
98 | newPart = false;
99 | }
100 |
101 | curPos += 4;
102 |
103 | //System.out.println("PRE endReached " + endReached + " curPos: " + curPos + " length:" + this.length + (!endReached && (curPos/4) < this.length));
104 |
105 | while(!endReached && (curPos/4) <= this.length) {
106 | //System.out.println("endReached " + endReached + " curPos: " + curPos + " length:" + this.length);
107 | curType = (int) aRawPkt[curPos];
108 |
109 | if(curType == 0) {
110 | curPos += 4 - (curPos % 4);
111 | endReached = true;
112 | } else {
113 | curLength = (int) aRawPkt[curPos + 1];
114 | //System.out.println("curPos:"+curPos+" curType:"+curType+" curLength:"+curLength+" read from:"+(curPos + 1));
115 |
116 | if(curLength > 0) {
117 | byte[] item = new byte[curLength];
118 | //System.out.println("curPos:"+curPos+" arawPkt.length:"+aRawPkt.length+" curLength:"+curLength);
119 | System.arraycopy(aRawPkt, curPos + 2, item, 0, curLength);
120 |
121 | switch(curType) {
122 | case 1: part.cname = new String(item); break;
123 | case 2: part.name = new String(item); break;
124 | case 3: part.email = new String(item); break;
125 | case 4: part.phone = new String(item); break;
126 | case 5: part.loc = new String(item); break;
127 | case 6: part.tool = new String(item); break;
128 | case 7: part.note = new String(item); break;
129 | case 8: part.priv = new String(item); break;
130 | }
131 | //System.out.println("TYPE " + curType + " value:" + new String(item) );
132 |
133 | } else {
134 | switch(curType) {
135 | case 1: part.cname = null; break;
136 | case 2: part.name = null; break;
137 | case 3: part.email = null; break;
138 | case 4: part.phone = null; break;
139 | case 5: part.loc = null; break;
140 | case 6: part.tool = null; break;
141 | case 7: part.note = null; break;
142 | case 8: part.priv = null; break;
143 | }
144 |
145 | }
146 | curPos = curPos + curLength + 2;
147 | }
148 | }
149 |
150 | // Save the participant
151 | this.participants[i] = part;
152 | if(newPart)
153 | partDb.addParticipant(2,part);
154 |
155 | //System.out.println("HEPPPPPP " + participants[i].cname );
156 | }
157 | }
158 | if(RTPSession.rtcpDebugLevel > 8) {
159 | System.out.println(" <- RtcpPktSDES()");
160 | }
161 | }
162 |
163 | /**
164 | * Encode the packet into a byte[], saved in .rawPkt
165 | *
166 | * CompRtcpPkt will call this automatically
167 | */
168 | protected void encode() {
169 | byte[] temp = new byte[1450];
170 | byte[] someBytes = StaticProcs.uIntLongToByteWord(this.rtpSession.ssrc);
171 | System.arraycopy(someBytes, 0, temp, 4, 4);
172 | int pos = 8;
173 |
174 | String tmpString = null;
175 | for(int i=1; i<9;i++) {
176 | switch(i) {
177 | case 1: tmpString = this.rtpSession.cname; break;
178 | case 2: tmpString = this.rtpSession.name; break;
179 | case 3: tmpString = this.rtpSession.email; break;
180 | case 4: tmpString = this.rtpSession.phone; break;
181 | case 5: tmpString = this.rtpSession.loc; break;
182 | case 6: tmpString = this.rtpSession.tool; break;
183 | case 7: tmpString = this.rtpSession.note; break;
184 | case 8: tmpString = this.rtpSession.priv; break;
185 | }
186 |
187 | if(tmpString != null) {
188 | someBytes = tmpString.getBytes();
189 | temp[pos] = (byte) i;
190 | temp[pos+1] = (byte) someBytes.length;
191 | System.arraycopy(someBytes, 0, temp, pos + 2, someBytes.length);
192 | //System.out.println("i: "+i+" pos:"+pos+" someBytes.length:"+someBytes.length);
193 | pos = pos + someBytes.length + 2;
194 | //if(i == 1 ) {
195 | // System.out.println("trueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + tmpString);
196 | //}
197 | }
198 | }
199 | int leftover = pos % 4;
200 | if(leftover == 1) {
201 | temp[pos] = (byte) 0;
202 | temp[pos + 1] = (byte) 1;
203 | pos += 3;
204 | } else if(leftover == 2) {
205 | temp[pos] = (byte) 0;
206 | temp[pos + 1] = (byte) 0;
207 | pos += 2;
208 | } else if(leftover == 3) {
209 | temp[pos] = (byte) 0;
210 | temp[pos + 1] = (byte) 3;
211 | pos += 5;
212 | }
213 |
214 | // TODO Here we ought to loop over participants, if we're doing SDES for other participants.
215 |
216 | super.rawPkt = new byte[pos];
217 | itemCount = 1;
218 | //This looks wrong, but appears to be fine..
219 | System.arraycopy(temp, 0, super.rawPkt, 0, pos);
220 | writeHeaders();
221 | }
222 |
223 | /**
224 | * Debug purposes only
225 | */
226 | public void debugPrint() {
227 | System.out.println("RtcpPktSDES.debugPrint() ");
228 | if(participants != null) {
229 | for(int i= 0; i rtcpPkts = new LinkedList();
44 |
45 | /**
46 | * Instantiates an empty Compound RTCP packet to which you can add RTCP packets
47 | */
48 | protected CompRtcpPkt() {
49 | // Will have to add packets directly to rtcpPkts.
50 | if(RTPSession.rtpDebugLevel > 7) {
51 | System.out.println("<-> CompRtcpPkt()");
52 | }
53 | }
54 |
55 | /**
56 | * Add a RTCP packet to the compound packet. Pakcets are added in order,
57 | * so you have to ensure that a Sender Report or Receiver Report is
58 | * added first.
59 | *
60 | * @param aPkt the packet to be added
61 | */
62 | protected void addPacket(RtcpPkt aPkt) {
63 | if(RTPSession.rtpDebugLevel > 11) {
64 | System.out.println(" <-> CompRtcpPkt.addPacket( "+ aPkt.getClass() + " )");
65 | }
66 |
67 | if(aPkt.problem == 0) {
68 | rtcpPkts.add(aPkt);
69 | } else {
70 | this.problem = aPkt.problem;
71 | }
72 | }
73 |
74 | /**
75 | * Picks a received Compound RTCP packet apart.
76 | *
77 | * Only SDES packets are processed directly, other packets are
78 | * parsed and put into aComptRtcpPkt.rtcpPkts, but not
79 | *
80 | * Check the aComptRtcpPkt.problem , if the value is non-zero
81 | * the packets should probably be discarded.
82 | *
83 | * @param rawPkt the byte array received from the socket
84 | * @param packetSize the actual number of used bytes
85 | * @param adr the socket address from which the packet was received
86 | * @param rtpSession the RTPSession with the participant database
87 | */
88 | protected CompRtcpPkt(byte[] rawPkt, int packetSize, InetSocketAddress adr, RTPSession rtpSession) {
89 | if(RTPSession.rtcpDebugLevel > 7) {
90 | System.out.println("-> CompRtcpPkt(" + rawPkt.getClass() + ", size " + packetSize + ", from " + adr.toString() + ", " + rtpSession.getClass() + ")");
91 | }
92 | //System.out.println("rawPkt.length:" + rawPkt.length + " packetSize:" + packetSize);
93 |
94 | // Chop it up
95 | int start = 0;
96 |
97 | while(start < packetSize && problem == 0) {
98 | int length = (StaticProcs.bytesToUIntInt(rawPkt, start + 2)) + 1;
99 |
100 | if(length*4 + start > rawPkt.length) {
101 | System.out.println("!!!! CompRtcpPkt.(rawPkt,..,..) length ("+ (length*4+start)
102 | + ") exceeds size of raw packet ("+rawPkt.length+") !");
103 | this.problem = -3;
104 | }
105 |
106 | int pktType = (int) rawPkt[start + 1];
107 |
108 | if(pktType < 0) {
109 | pktType += 256;
110 | }
111 |
112 |
113 | if(start == 0) {
114 | // Compound packets need to start with SR or RR
115 | if(pktType != 200 && pktType != 201 ) {
116 | if(RTPSession.rtcpDebugLevel > 3) {
117 | System.out.println("!!!! CompRtcpPkt(rawPkt...) packet did not start with SR or RR");
118 | }
119 | this.problem = -1;
120 | }
121 |
122 | // Padding bit should be zero for the first packet
123 | if(((rawPkt[start] & 0x20) >>> 5) == 1) {
124 | if(RTPSession.rtcpDebugLevel > 3) {
125 | System.out.println("!!!! CompRtcpPkt(rawPkt...) first packet was padded");
126 | }
127 | this.problem = -2;
128 | }
129 | }
130 |
131 | //System.out.println("start: " + start + " pktType: " + pktType + " length:" + length );
132 | if(pktType == 200) {
133 | addPacket(new RtcpPktSR(rawPkt,start,length*4));
134 | } else if(pktType == 201 ) {
135 | addPacket(new RtcpPktRR(rawPkt,start, -1));
136 | } else if(pktType == 202) {
137 | addPacket(new RtcpPktSDES(rawPkt,start, adr, rtpSession.partDb));
138 | } else if(pktType == 203 ) {
139 | addPacket(new RtcpPktBYE(rawPkt,start));
140 | } else if(pktType == 204) {
141 | addPacket(new RtcpPktAPP(rawPkt,start));
142 | } else if(pktType == 205) {
143 | addPacket(new RtcpPktRTPFB(rawPkt,start, rtpSession));
144 | } else if(pktType == 206) {
145 | addPacket(new RtcpPktPSFB(rawPkt,start,rtpSession));
146 | } else {
147 | System.out.println("!!!! CompRtcpPkt(byte[] rawPkt, int packetSize...) "
148 | +"UNKNOWN RTCP PACKET TYPE:" + pktType);
149 | }
150 |
151 | //System.out.println(" start:" + start + " pktType:" + pktType + " length:" + length);
152 |
153 | start += length*4;
154 |
155 | if(RTPSession.rtcpDebugLevel > 12) {
156 | System.out.println(" start:"+start+" parsing pktType "+pktType+" length: "+length);
157 | }
158 | }
159 | if(RTPSession.rtcpDebugLevel > 7) {
160 | System.out.println("<- CompRtcpPkt(rawPkt....)");
161 | }
162 | }
163 |
164 | /**
165 | * Encode combines the RTCP packets in this.rtcpPkts into a byte[]
166 | * by calling the encode() function on each of them individually.
167 | *
168 | * The order of rtcpPkts is preserved, so a RR or SR packet must be first.
169 | *
170 | * @return the trimmed byte[] representation of the packet, ready to go into a UDP packet.
171 | */
172 | protected byte[] encode() {
173 | if(RTPSession.rtpDebugLevel > 9) {
174 | System.out.println(" <- CompRtcpPkt.encode()");
175 | }
176 |
177 | ListIterator iter = rtcpPkts.listIterator();
178 |
179 | byte[] rawPkt = new byte[1500];
180 | int index = 0;
181 |
182 | while(iter.hasNext()) {
183 | RtcpPkt aPkt = (RtcpPkt) iter.next();
184 |
185 | if(aPkt.packetType == 200) {
186 | RtcpPktSR pkt = (RtcpPktSR) aPkt;
187 | pkt.encode();
188 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
189 | index += pkt.rawPkt.length;
190 | } else if(aPkt.packetType == 201 ) {
191 | RtcpPktRR pkt = (RtcpPktRR) aPkt;
192 | pkt.encode();
193 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
194 | index += pkt.rawPkt.length;
195 | } else if(aPkt.packetType == 202) {
196 | RtcpPktSDES pkt = (RtcpPktSDES) aPkt;
197 | pkt.encode();
198 | //System.out.println(" ENCODE SIZE: " + pkt.rawPkt.length);
199 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
200 | index += pkt.rawPkt.length;
201 | } else if(aPkt.packetType == 203) {
202 | RtcpPktBYE pkt = (RtcpPktBYE) aPkt;
203 | pkt.encode();
204 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
205 | index += pkt.rawPkt.length;
206 | } else if(aPkt.packetType == 204) {
207 | RtcpPktAPP pkt = (RtcpPktAPP) aPkt;
208 | pkt.encode();
209 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
210 | index += pkt.rawPkt.length;
211 | } else if(aPkt.packetType == 205) {
212 | RtcpPktRTPFB pkt = (RtcpPktRTPFB) aPkt;
213 | pkt.encode();
214 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
215 | index += pkt.rawPkt.length;
216 | } else if(aPkt.packetType == 206) {
217 | RtcpPktPSFB pkt = (RtcpPktPSFB) aPkt;
218 | pkt.encode();
219 | System.arraycopy(pkt.rawPkt, 0, rawPkt, index, pkt.rawPkt.length);
220 | index += pkt.rawPkt.length;
221 | } else {
222 | System.out.println("CompRtcpPkt aPkt.packetType:" + aPkt.packetType);
223 | }
224 | //System.out.println(" packetType:" + aPkt.packetType + " length:" + aPkt.rawPkt.length + " index:" + index);
225 | }
226 |
227 | byte[] output = new byte[index];
228 |
229 | System.arraycopy(rawPkt, 0, output, 0, index);
230 |
231 | if(RTPSession.rtpDebugLevel > 9) {
232 | System.out.println(" -> CompRtcpPkt.encode()");
233 | }
234 | return output;
235 | }
236 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/DataFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 |
22 | /**
23 | * Data structure to hold a complete frame if frame reconstruction
24 | * is enabled, or the data from an individual packet if it is not
25 | *
26 | * It also contains most of the data from the individual packets
27 | * that it is based on.
28 | *
29 | * @author Arne Kepp
30 | */
31 | public class DataFrame {
32 | /** The share RTP timestamp */
33 | private long rtpTimestamp;
34 | /** The calculated UNIX timestamp, guessed after 2 Sender Reports */
35 | private long timestamp = -1;
36 | /** the SSRC from which this frame originated */
37 | private long SSRC;
38 | /** contributing CSRCs, only read from the first packet */
39 | private long[] CSRCs;
40 | /** RTP payload type */
41 | private int payloadType;
42 | /** The marks on individual packets, ordered */
43 | private boolean[] marks;
44 | /** Whether any packets were marked or not */
45 | private boolean anyMarked = false;
46 | /** Whether the frame contains the expected number of packets */
47 | private int isComplete = 0;
48 | //private int dataLength;
49 | /** The data from the individual packets, ordered */
50 | private byte[][] data;
51 | /** The sequence numbers of the individual packets, ordered */
52 | private int[] seqNum;
53 | /** The total amount of data bytes in this frame */
54 | private int totalLength = 0;
55 | /** The last sequence number in this frame */
56 | protected int lastSeqNum;
57 | /** The first sequence number in this frame */
58 | protected int firstSeqNum;
59 | /** The number of packets expected for a complete frame */
60 | protected int noPkts;
61 |
62 | /**
63 | * The usual way to construct a frame is by giving it a PktBufNode,
64 | * which contains links to all the other pkts that make it up.
65 | */
66 | protected DataFrame(PktBufNode aBufNode, Participant p, int noPkts) {
67 | if(RTPSession.rtpDebugLevel > 6) {
68 | System.out.println("-> DataFrame(PktBufNode, noPkts = " + noPkts +")");
69 | }
70 | this.noPkts = noPkts;
71 | RtpPkt aPkt = aBufNode.pkt;
72 | int pktCount = aBufNode.pktCount;
73 | firstSeqNum = aBufNode.pktCount;
74 |
75 | // All this data should be shared, so we just get it from the first one
76 | this.rtpTimestamp = aBufNode.timeStamp;
77 | SSRC = aPkt.getSsrc();
78 | CSRCs = aPkt.getCsrcArray();
79 |
80 | // Check whether we can compute an NTPish timestamp? Requires two SR reports
81 | if(p.ntpGradient > 0) {
82 | //System.out.print(Long.toString(p.ntpOffset)+" "
83 | timestamp = p.ntpOffset + (long) (p.ntpGradient*(double)(this.rtpTimestamp-p.lastSRRtpTs));
84 | }
85 |
86 | // Make data the right length
87 | int payloadLength = aPkt.getPayloadLength();
88 | //System.out.println("aBufNode.pktCount " + aBufNode.pktCount);
89 | data = new byte[aBufNode.pktCount][payloadLength];
90 | seqNum = new int[aBufNode.pktCount];
91 | marks = new boolean[aBufNode.pktCount];
92 |
93 | // Concatenate the data of the packets
94 | int i;
95 | for(i=0; i< pktCount; i++) {
96 | aPkt = aBufNode.pkt;
97 | byte[] temp = aPkt.getPayload();
98 | totalLength += temp.length;
99 | if(temp.length == payloadLength) {
100 | data[i] = temp;
101 | } else if(temp.length < payloadLength){
102 | System.arraycopy(temp, 0, data[i], 0, temp.length);
103 | } else {
104 | System.out.println("DataFrame() received node structure with increasing packet payload size.");
105 | }
106 | //System.out.println("i " + i + " seqNum[i] " + seqNum[i] + " aBufNode" + aBufNode);
107 | seqNum[i] = aBufNode.seqNum;
108 | marks[i] = aBufNode.pkt.isMarked();
109 | if(marks[i])
110 | anyMarked = true;
111 |
112 | // Get next node
113 | aBufNode = aBufNode.nextFrameNode;
114 | }
115 |
116 | lastSeqNum = seqNum[i - 1];
117 |
118 | if(noPkts > 0) {
119 | int seqDiff = firstSeqNum - lastSeqNum;
120 | if(seqDiff < 0)
121 | seqDiff = (Integer.MAX_VALUE - firstSeqNum) + lastSeqNum;
122 | if(seqDiff == pktCount && pktCount == noPkts)
123 | isComplete = 1;
124 | } else {
125 | isComplete = -1;
126 | }
127 |
128 | if(RTPSession.rtpDebugLevel > 6) {
129 | System.out.println("<- DataFrame(PktBufNode, noPkt), data length: " + data.length);
130 | }
131 | }
132 |
133 | /**
134 | * Returns a two dimensial array where the first dimension represents individual
135 | * packets, from which the frame is made up, in order of increasing sequence number.
136 | * These indeces can be matched to the sequence numbers returned by sequenceNumbers().
137 | *
138 | * @return 2-dim array with raw data from packets
139 | */
140 | public byte[][] getData() {
141 | return this.data;
142 | }
143 |
144 | /**
145 | * Returns a concatenated version of the data from getData()
146 | * It ignores missing sequence numbers, but then isComplete()
147 | * will return false provided that RTPAppIntf.frameSize()
148 | * provides a non-negative number for this payload type.
149 | *
150 | * @return byte[] with all the data concatenated
151 | */
152 | public byte[] getConcatenatedData() {
153 | if(this.noPkts < 2) {
154 | byte[] ret = new byte[this.totalLength];
155 | int pos = 0;
156 |
157 | for(int i=0; i totalLength)
162 | length = totalLength - pos;
163 |
164 | System.arraycopy(data[i], 0, ret, pos, length);
165 | pos += data[i].length;
166 | }
167 | return ret;
168 | } else {
169 | return data[0];
170 | }
171 | }
172 |
173 | /**
174 | * If two SR packet have been received jlibrtp will attempt to calculate
175 | * the local UNIX timestamp (in milliseconds) of all packets received.
176 | *
177 | * This value should ideally correspond to the local time when the
178 | * SSRC sent the packet. Note that the source may not be reliable.
179 | *
180 | * Returns -1 if less than two SRs have been received
181 | *
182 | * @return the UNIX timestamp, similar to System.currentTimeMillis() or -1;
183 | */
184 | public long timestamp() {
185 | return this.timestamp;
186 |
187 | }
188 |
189 | /**
190 | * Returns the RTP timestamp of all the packets in the frame.
191 | *
192 | * @return unmodified RTP timestamp
193 | */
194 | public long rtpTimestamp() {
195 | return this.rtpTimestamp;
196 | }
197 |
198 | /**
199 | * Returns the payload type of the packets
200 | *
201 | * @return the payload type of the packets
202 | */
203 | public int payloadType() {
204 | return this.payloadType;
205 | }
206 |
207 | /**
208 | * Returns an array whose values, for the same index, correpond to the
209 | * sequence number of the packet from which the data came.
210 | *
211 | * This information can be valuable in conjunction with getData(),
212 | * to identify what parts of a frame are missing.
213 | *
214 | * @return array with sequence numbers
215 | */
216 | public int[] sequenceNumbers() {
217 | return seqNum;
218 | }
219 |
220 | /**
221 | * Returns an array whose values, for the same index, correpond to
222 | * whether the data was marked or not.
223 | *
224 | * This information can be valuable in conjunction with getData().
225 | *
226 | * @return array of booleans
227 | */
228 | public boolean[] marks() {
229 | return this.marks;
230 | }
231 |
232 | /**
233 | * Returns true if any packet in the frame was marked.
234 | *
235 | * This function should be used if all your frames fit
236 | * into single packets.
237 | *
238 | * @return true if any packet was marked, false otherwise
239 | */
240 | public boolean marked() {
241 | return this.anyMarked;
242 | }
243 |
244 | /**
245 | * The SSRC associated with this frame.
246 | *
247 | * @return the ssrc that created this frame
248 | */
249 | public long ssrc() {
250 | return this.SSRC;
251 | }
252 |
253 | /**
254 | * The SSRCs that contributed to this frame
255 | *
256 | * @return an array of contributing SSRCs, or null
257 | */
258 | public long[] csrcs() {
259 | return this.CSRCs;
260 | }
261 |
262 | /**
263 | * Checks whether the difference in sequence numbers corresponds
264 | * to the number of packets received for the current timestamp,
265 | * and whether this value corresponds to the expected number of
266 | * packets.
267 | *
268 | * @return true if the right number of packets make up the frame
269 | */
270 | public int complete() {
271 | return this.isComplete;
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/ParticipantDatabase.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.util.Enumeration;
22 | import java.util.Iterator;
23 | import java.util.LinkedList;
24 | import java.util.concurrent.ConcurrentHashMap;
25 |
26 | /**
27 | * The participant database maintains three hashtables with participants.
28 | *
29 | * The key issue is to be fast for operations that happen every time an
30 | * RTP packet is sent or received. We allow linear searching in cases
31 | * where we need to update participants with information.
32 | *
33 | * The keying is therefore usually the SSRC. In cases where we have the
34 | * cname, but no SSRC is known (no SDES packet has been received), a
35 | * simple hash i calculated based on the CNAME. The RTCP code should,
36 | * when receiving SDES packets, check whether the participant is known
37 | * and update the copy in this database with SSRC if needed.
38 | *
39 | * @author Arne Kepp
40 | */
41 | public class ParticipantDatabase {
42 | /** The parent RTP Session */
43 | RTPSession rtpSession = null;
44 | /**
45 | * A linked list to hold participants explicitly added by the application
46 | * In unicast mode this is the list used for RTP and RTCP transmission,
47 | * in multicast it should not be in use.
48 | */
49 | LinkedList receivers = new LinkedList();
50 | /**
51 | * The hashtable holds participants added through received RTP and RTCP packets,
52 | * as well as participants that have been linked to an SSRC by ip address (in unicast mode).
53 | */
54 | ConcurrentHashMap ssrcTable = new ConcurrentHashMap();
55 |
56 | /**
57 | * Simple constructor
58 | *
59 | * @param parent parent RTPSession
60 | */
61 | protected ParticipantDatabase(RTPSession parent) {
62 | rtpSession = parent;
63 | }
64 |
65 | /**
66 | *
67 | * @param cameFrom 0: Application, 1: RTP packet, 2: RTCP
68 | * @param p the participant
69 | * @return 0 if okay, -1 if not
70 | */
71 | protected int addParticipant(int cameFrom, Participant p) {
72 | //Multicast or not?
73 | if(this.rtpSession.mcSession) {
74 | return this.addParticipantMulticast(cameFrom, p);
75 | } else {
76 | return this.addParticipantUnicast(cameFrom, p);
77 | }
78 |
79 | }
80 |
81 | /**
82 | * Add a multicast participant to the database
83 | *
84 | * @param cameFrom 0: Application, 1,2: discovered through RTP or RTCP
85 | * @param p the participant to add
86 | * @return 0 if okay, -2 if redundant, -1 if adding participant to multicast
87 | */
88 | private int addParticipantMulticast(int cameFrom, Participant p) {
89 | if( cameFrom == 0) {
90 | System.out.println("ParticipantDatabase.addParticipant() doesnt expect"
91 | + " application to add participants to multicast session.");
92 | return -1;
93 | } else {
94 | // Check this one is not redundant
95 | if(this.ssrcTable.contains(p.ssrc)) {
96 | System.out.println("ParticipantDatabase.addParticipant() SSRC "
97 | +"already known " + Long.toString(p.ssrc));
98 | return -2;
99 | } else {
100 | this.ssrcTable.put(p.ssrc, p);
101 | return 0;
102 | }
103 | }
104 | }
105 |
106 | /**
107 | * Add a unicast participant to the database
108 | *
109 | * Result will be reported back through tpSession.appIntf.userEvent
110 | *
111 | * @param cameFrom 0: Application, 1,2: discovered through RTP or RTCP
112 | * @param p the participant to add
113 | * @return 0 if new, 1 if
114 | */
115 | private int addParticipantUnicast(int cameFrom, Participant p) {
116 | if(cameFrom == 0) {
117 | //Check whether there is a match in the ssrcTable
118 | boolean notDone = true;
119 |
120 | Enumeration enu = this.ssrcTable.elements();
121 | while(notDone && enu.hasMoreElements()) {
122 | Participant part = enu.nextElement();
123 | if(part.unexpected &&
124 | (part.rtcpReceivedFromAddress.equals(part.rtcpAddress.getAddress())
125 | || part.rtpReceivedFromAddress.equals(part.rtpAddress.getAddress()))) {
126 |
127 | part.rtpAddress = p.rtpAddress;
128 | part.rtcpAddress = p.rtcpAddress;
129 | part.unexpected = false;
130 |
131 | //Report the match back to the application
132 | Participant[] partArray = {part};
133 | this.rtpSession.appIntf.userEvent(5, partArray);
134 |
135 | notDone = false;
136 | p = part;
137 | }
138 | }
139 |
140 | //Add to the table of people that we send packets to
141 | this.receivers.add(p);
142 | return 0;
143 |
144 | } else {
145 | //Check whether there's a match in the receivers table
146 | boolean notDone = true;
147 | //System.out.println("GOT " + p.cname);
148 | Iterator iter = this.receivers.iterator();
149 |
150 | while(notDone && iter.hasNext()) {
151 | Participant part = iter.next();
152 |
153 | //System.out.println(part.rtpAddress.getAddress().toString()
154 | // + " " + part.rtcpAddress.getAddress().toString()
155 | // + " " + p.rtpReceivedFromAddress.getAddress().toString()
156 | // + " " + p.rtcpReceivedFromAddress.getAddress().toString());
157 |
158 | //System.out.println(" HUUHHHH? " + p.rtcpReceivedFromAddress.getAddress().equals(part.rtcpAddress.getAddress()));
159 | if((cameFrom == 1 && p.rtpReceivedFromAddress.getAddress().equals(part.rtpAddress.getAddress()))
160 | || (cameFrom == 2 && p.rtcpReceivedFromAddress.getAddress().equals(part.rtcpAddress.getAddress()))) {
161 |
162 | part.rtpReceivedFromAddress = p.rtpReceivedFromAddress;
163 | part.rtcpReceivedFromAddress = p.rtcpReceivedFromAddress;
164 |
165 | // Move information
166 | part.ssrc = p.ssrc;
167 | part.cname = p.cname;
168 | part.name = p.name;
169 | part.loc = p.loc;
170 | part.phone = p.phone;
171 | part.email = p.email;
172 | part.note = p.note;
173 | part.tool = p.tool;
174 | part.priv = p.priv;
175 |
176 | this.ssrcTable.put(part.ssrc, part);
177 |
178 | //Report the match back to the application
179 | Participant[] partArray = {part};
180 | this.rtpSession.appIntf.userEvent(5, partArray);
181 | return 0;
182 | }
183 | }
184 |
185 | // No match? ok
186 | this.ssrcTable.put(p.ssrc, p);
187 | return 0;
188 | }
189 | }
190 |
191 | /**
192 | * Remove a participant from all tables
193 | *
194 | * @param p the participant to be removed
195 | */
196 | protected void removeParticipant(Participant p) {
197 | if(! this.rtpSession.mcSession)
198 | this.receivers.remove(p);
199 |
200 | this.ssrcTable.remove(p.ssrc, p);
201 | }
202 |
203 | /**
204 | * Find a participant based on the ssrc
205 | *
206 | * @param ssrc of the participant to be found
207 | * @return the participant, null if unknonw
208 | */
209 | protected Participant getParticipant(long ssrc) {
210 | Participant p = null;
211 | p = ssrcTable.get(ssrc);
212 | return p;
213 | }
214 |
215 | /**
216 | * Iterator for all the unicast receivers.
217 | *
218 | * This one is used by both RTP for sending packets, as well as RTCP.
219 | *
220 | * @return iterator for unicast participants
221 | */
222 | protected Iterator getUnicastReceivers() {
223 | if(! this.rtpSession.mcSession) {
224 | return this.receivers.iterator();
225 | } else {
226 | System.out.println("Request for ParticipantDatabase.getUnicastReceivers in multicast session");
227 | return null;
228 | }
229 | }
230 |
231 | /**
232 | * Enumeration of all the participants with known ssrcs.
233 | *
234 | * This is primarily used for sending packets in multicast sessions.
235 | *
236 | * @return enumerator with all the participants with known SSRCs
237 | */
238 | protected Enumeration getParticipants() {
239 | return this.ssrcTable.elements();
240 | }
241 |
242 | protected void debugPrint() {
243 | System.out.println(" ParticipantDatabase.debugPrint()");
244 | Participant p;
245 | Enumeration enu = ssrcTable.elements();
246 | while(enu.hasMoreElements()) {
247 | p = (Participant) enu.nextElement();
248 | System.out.println(" ssrcTable ssrc:"+p.ssrc+" cname:"+p.cname
249 | +" loc:"+p.loc+" rtpAddress:"+p.rtpAddress+" rtcpAddress:"+p.rtcpAddress);
250 | }
251 |
252 | Iterator iter = receivers.iterator();
253 | while(iter.hasNext()) {
254 | p = iter.next();
255 | System.out.println(" receivers: "+p.rtpAddress.toString());
256 | }
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 由于个人工作变更,本项目不再进行维护
2 |
3 |
4 |
5 | 公司的项目中视频通话模块计划采用的方案包括开源库 Linphone、MediaCodec 实现、WebRTC 等,由于目标设备硬件配置较差,采用软编软解的 Linphone 在性能上遇到了瓶颈。因此最近在尝试使用 MediaCodec 和 rtp 库 jlibrtp 来实现视频通话。
6 |
7 | 在本次实践中遇到的问题包括:
8 | 1、分包、合包问题
9 | 3、在 UI 线程发送数据的问题(sendData 的耗时操作解决)
10 | 4、Android 相机方向问题
11 | 5、视频通话性能优化问题
12 | 6、颜色模式问题(todo)
13 | 7、MediaCodec 的实现原理
14 |
15 | # 整体结构
16 |
17 | 
18 |
19 | # MediaCodec 的原理
20 |
21 | 使用 MediaCodec 实现视频通话,必须搞懂对 MediaCodec 的原理,不然编解码的代码都看不懂,更不用谈后面 rtp 包的相关问题了。关于 MediaCodec 的原理,我是多次阅读[卢俊老师的博客](http://ticktick.blog.51cto.com/823160/1760191)后,才完全搞明白的,在此摘取卢老师博客的精华部分,帮助理解:
22 |
23 | > 1、MediaCodec提供了一套访问 Android 底层多媒体模块的接口,主要是音视频的编解码接口
24 |
25 | > 2、Android 底层多媒体模块采用的是 OpenMax 框架,任何 Android 底层编解码模块的实现,都必须遵循 OpenMax 标准。Google 官方默认提供了一系列的软件编解码器:包括:OMX.google.h264.encoder,OMX.google.h264.decoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬件编解码功能,则需要由芯片厂商依照 OpenMax 框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的
26 |
27 | > 3、Android 应用层统一由 MediaCodec API 来提供各种音视频编解码功能,由参数配置来决定采用何种编解码算法、是否采用硬件编解码加速等等
28 |
29 | MediaCodec 的基本使用流程如下:
30 |
31 | ```plain
32 | - createEncoderByType/createDecoderByType
33 | - configure
34 | - start
35 | - while(1) {
36 | - dequeueInputBuffer
37 | - queueInputBuffer
38 | - dequeueOutputBuffer
39 | - releaseOutBuffer
40 | }
41 | - stop
42 | - release
43 | ```
44 |
45 | 下面是 Android 官方文档上的图,所有精华都在这图中了,再结合上面的使用流程就不难理解了。
46 |
47 | 
48 |
49 | 下面结合视频通话模块分编码和解码两个部分介绍:
50 |
51 | #### 编码
52 |
53 | 对于编码,就是 camera 采集镜头,传给 MediaCodec 进行编码,打包成 H.264 包后给 jlibrtp 通过 rtp 包发送出去。
54 |
55 | MediaCodec 对象(编码器)左右两侧各维护一个缓冲区队列。
56 |
57 | 左侧的为 input buffer 队列, client(camera) 负责生产 input data 数据,首先申请队列中空的 buffer(dequeueInputBuffer 方法)
58 | 然后 client 将采集的数据填入申请的空 buffer 中并将其放回队列(queueInputBuffer 方法)
59 | MediaCodec 进行编码,编码成功后将数据放入 output buffer 队列,将原始数据 buffer 置为空后再放入 input buffer 队列。
60 | 右侧的 client(可以理解为 rlibrtp)从 output buffer 队列申请编码好的 buffer,并发送(dequeueOutputBuffer 方法)
61 | 发送完成后,右侧 client 再将该 buffer 放回 output buffer 缓冲区队列(releaseOutBuffer 方法)
62 | 至此,一帧数据的编码工作已经完成。
63 |
64 | #### 解码
65 |
66 | 理解了编码的工作原理,解码就很简单了,MediaCodec 同样是维护两个缓冲区队列。
67 |
68 | 左侧的 client(可以理解为 rlibrtp)接收到对方传来的数据后,从 input 缓冲区申请空的 buffer(dequeueInputBuffer 方法)
69 | 将接收到的数据填入申请的空 buffer 中,并放回 input buffer 缓冲区队列(queueInputBuffer 方法)
70 | MediaCodec 进行解码,解码成功后将数据放入 output buffer 队列,将原始数据 buffer 置为空后再放回 input buffer 队列。
71 | 右侧的 client(可以理解为 APP 端)从 output buffer 队列中申请解码好的数据,并进行渲染(dequeueOutputBuffer 方法)
72 | 渲染完成后,右侧 client 再将其放回 output buffer 队列(releaseOutBuffer 方法)
73 | 这样,解码一帧的工作也就完成了,可以看到,真正的编解码工作 MediaCodec 都默默的为我们做了,我们需要实现的就是不停地塞数据、取数据。原理明白后,剩下的就是具体实现以及优化工作了。
74 |
75 | # 分包、合包问题
76 | #### 分包问题
77 |
78 | 实现 RTP 协议的开源库有 C 编写的 ORTP,C++编写的 JRTPLIB,以及 java 实现的 jlibrtp。我采用的是 jlibrtp,由于 jlibrtp 对于每一包的大小限制在1480字节,因此需要在发送数据前对编码好的 H.264数据进行分包。
79 |
80 | 一开始的分包代码是这样写的:
81 |
82 | ```java
83 | /**
84 | * 将每帧进行分包并发送数据
85 | * @param bytes
86 | */
87 | private void sendData(byte[] bytes) {
88 | int dataLength = (bytes.length - 1) / 1480 + 1;
89 | final byte[][] data = new byte[dataLength][];
90 | final boolean[] marks = new boolean[dataLength];
91 | marks[marks.length - 1] = true;
92 | long[] seqNumbers = new long[dataLength];
93 | for (int i = 0;i < dataLength;i++){
94 | seqNumbers[i] = seqNumber;
95 | try{
96 | seqNumber++;
97 | }catch (Throwable t){
98 | seqNumber = 0;
99 | }
100 | }
101 | int num = 0;
102 | do{
103 | int length = bytes.length > 1480 ? 1480 : bytes.length;
104 | data[num] = Arrays.copyOf(bytes,length);
105 | num++;
106 | byte[] b = new byte[bytes.length - length];
107 | for(int i = length; i < bytes.length; i++){
108 | b[i - length] = bytes[i];
109 | }
110 | bytes = b;
111 | } while (bytes.length > 0);
112 | mInitSession.rtpSession.sendData(data, null, marks, System.currentTimeMillis(), null);
113 | }
114 | ```
115 |
116 | 在低配置手机上进行测试后,发现分包这里的多重循环会对数据的发送效率产生影响,故对着手对此段代码进行优化。
117 |
118 | 优化后代码如下:
119 |
120 | ```java
121 | /**
122 | * 将每帧进行分包并发送数据
123 | * @param bytes
124 | */
125 | private void sendData(byte[] bytes) {
126 | int dataLength = (bytes.length - 1) / 1480 + 1;
127 | final byte[][] data = new byte[dataLength][];
128 | final boolean[] marks = new boolean[dataLength];
129 | marks[marks.length - 1] = true;
130 | int x = 0;
131 | int y = 0;
132 | int length = bytes.length;
133 | for (int i = 0; i < length; i++){
134 | if (y == 0){
135 | data[x] = new byte[length - i > 1480 ? 1480 : length - i];
136 | }
137 | data[x][y] = bytes[i];
138 | y++;
139 | if (y == data[x].length){
140 | y = 0;
141 | x++;
142 | }
143 | }
144 | mInitSession.rtpSession.sendData(data, null, marks, -1, null);
145 | }
146 | ```
147 |
148 | #### 合包问题
149 | 由于一开始未考虑到合包(o(╯□╰)o),导致视频接通后,一直都是上半部分画面可见,下半部分严重乱码。经过和同事探讨、分析,发现在接收到 rtp 包后,直接扔给 MediaCodec 进行解码了,并未对 rtp 包进行拼接。
150 |
151 | 正确的操作应为根据 rtp 包的 mark 标志位,判断该包是否为一帧的最后一包,是则一帧拼接完成,开始解码;否则继续拼接。将拼接好的一帧数据传给 MediaCodec 进行解码,顺利出现完整图像。
152 |
153 | rtp 标志位的解释由有效负载类型决定。对于视频流,它标志一帧的结束.而对音频,则表示一次谈话的开始。
154 |
155 | 合包代码如下:
156 |
157 | ```java
158 | @Override
159 | public void receiveData(DataFrame frame, Participant p){
160 | if (buf == null){
161 | buf = frame.getConcatenatedData();
162 | } else {
163 | buf = Util.merge(buf, frame.getConcatenatedData());
164 | }
165 | if (frame.marked()){
166 | decode(buf);
167 | buf = null;
168 | }
169 | }
170 | ```
171 |
172 | # 在 UI 线程发送数据的问题(sendData 的耗时操作解决)
173 | 最棘手的问题出现在 sendData 方法上。由于前期的测试设备为moto x 和小米5,两台手机系统均为 Android7.1.1,视频通话正常。在切换到目标设备(系统 Android4.2)后,发现一个棘手问题,RTPSession 实例的 sendData()方法会一直报 NetworkOnMainThreadException 的异常。经测试 Android4.4, 5.0.1, 5.1设备有同样的现象出现,不知道是不是 Android7.0以上对于主线程访问网络有所变更?暂未解。
174 |
175 | 解决方法只能为将 sendData() 的动作放到子线程操作。
176 |
177 | 对于从 output buffer 缓冲区队列拿取数据并发送的代码放到子线程执行,代码如下:
178 |
179 | ```java
180 | @Override
181 | public void run() {
182 | mInitSession = new InitSession(mSurface);
183 | while (isEncode) {
184 | synchronized (this) {
185 | try {
186 | wait();
187 | } catch (InterruptedException e) {
188 | e.printStackTrace();
189 | }
190 | }
191 | getEncodeData();
192 | }
193 | }
194 |
195 | /**
196 | * 获取编码后的数据并发送
197 | */
198 | private void getEncodeData(){
199 | // TODO: 17/4/28 移到循环外部
200 | ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers();
201 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
202 | int outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
203 | while (outputBufferIndex >= 0 && isEncode) {
204 | ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
205 | byte[] outData = new byte[bufferInfo.size];
206 | outputBuffer.get(outData);
207 | sendData(outData);
208 | mEncoder.releaseOutputBuffer(outputBufferIndex, false);
209 | outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
210 | }
211 | }
212 | ```
213 |
214 | 获取完一帧数据后,执行 wait(),在 camera 的下一次回调时进行唤醒:
215 |
216 | ```java
217 | ...
218 | try {
219 | int bufferIndex = mEncoder.dequeueInputBuffer(0);
220 | if (bufferIndex >= 0) {
221 | inputBuffers[bufferIndex].clear();
222 | inputBuffers[bufferIndex].put(dst);
223 | mEncoder.queueInputBuffer(bufferIndex, 0, inputBuffers[bufferIndex].position(), mCount * 1000000 / FRAME_RATE, 0);
224 | mCount++;
225 | synchronized (mEncodeThread) {
226 | mEncodeThread.notify();
227 | }
228 | } else {
229 | Log.e(TAG, "No buffer available !");
230 | }
231 | } catch (Exception e) {
232 | e.printStackTrace();
233 | }
234 | ...
235 | ```
236 |
237 | # Android 相机方向问题
238 | Android 的 Activity 设为竖屏时,SurfaceView 预览图像将会颠倒90度。我们在使用 Android 相机进行拍照的时候会发现,手机方向无论怎么转动,手机上显示的画面总是朝上的,这是因为 Android 底层 API 已经为我们做过相应的转换了。
239 |
240 | Android 相机的图像数据都是源于摄像头硬件的图像传感器,而这个传感器是有一个默认的取景方向的,它的取景方向并不会随着手机的转动而变,方向如图所示:
241 |
242 | 
243 |
244 | 可以看到取景方向相当于是将屏幕方向逆时针旋转90度了,而当竖着拿手机时,Android 的 camera 已经默认为我们将取到的图像旋转90度,这样显示就正确了。
245 |
246 | 我们在视频通话时,由于本地预览的 surfaceview 会经系统的 MediaServer 服务为我们进行旋转,所以显示正常。而对方过来的画面未经处理直接显示的话,在 Activity 竖屏时,就会出现图像旋转90度的问题。
247 |
248 | 在我第一次从网上找的 MediaCodec demo 中,作者采用了通过代码进行旋转的方法,方法如下:
249 |
250 | ```java
251 | /**
252 | * 将YUV420SP数据顺时针旋转90度
253 | *
254 | * @param data 要旋转的数据
255 | * @param imageWidth 要旋转的图片宽度
256 | * @param imageHeight 要旋转的图片高度
257 | * @return 旋转后的数据
258 | */
259 | public static byte[] rotateNV21Degree90(byte[] data, int imageWidth, int imageHeight) {
260 | byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
261 | // Rotate the Y luma
262 | int i = 0;
263 | for (int x = 0; x < imageWidth; x++) {
264 | for (int y = imageHeight - 1; y >= 0; y--) {
265 | yuv[i] = data[y * imageWidth + x];
266 | i++;
267 | }
268 | }
269 | // Rotate the U and V color components
270 | i = imageWidth * imageHeight * 3 / 2 - 1;
271 | for (int x = imageWidth - 1; x > 0; x = x - 2) {
272 | for (int y = 0; y < imageHeight / 2; y++) {
273 | yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
274 | i--;
275 | yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
276 | i--;
277 | }
278 | }
279 | return yuv;
280 | }
281 | ```
282 |
283 | 这样,对方的数据是可以正常显示了,但也引出了下面的一个性能问题,在视频初步调通之后,我们发现 Android 设备的 cpu 使用率一直维持在20%左右,在硬编硬解的情况下,这么高的数值显然是不正常的。经多重排查,查明 rotateNV21Degree90()方法中的嵌套循环也是导致 cpu 使用率高德原因之一。
284 |
285 | 故而只能尝试通过硬件进行旋转视频数据。
286 | # 颜色模式问题(todo)
287 | # 视频性能优化
288 | 性能问题一直是这个项目中的痛点,由于我们的目标设备如楼宇门口机、电梯广告机这些设备的配置都是很低的。所以视频的流畅度的优化就很重要了,这也是为什么放弃 Linphone 的原因,因为软编软解实在无法在低配置下流畅通话。
289 |
290 | 这次的视频流畅度优化问题主要从以下几方面入手:
291 |
292 | 分包、发包的速度
293 | 颜色模式转换的资源占用
294 | 视频方向转换的资源占用
295 | 1、分包、发包的速度很关键,跟不上的话会直接导致对方接收到的包很慢,显得卡顿。参考[日本开发者的这个 demo](https://github.com/pingu342/android-app-mediacodectest/blob/master/src/jp/saka/mediacodectest/MediaCodecTest.java),这个 demo 使用了一个环形队列对于采集到的每一帧数据进行处理,之后再给 MediaCodec 进行编码、发包。需要的朋友也可以进行尝试。
296 |
297 | 2、一开始的颜色模式转换、视频方向转换我都是参考网上的例子采用代码进行转换,这两段代码中有大量的嵌套循环,是导致 cpu 使用率上升的重要原因,也是解决问题的关键点所在,大家可以关注下。
298 |
299 |
300 |
301 |
302 | Android 开发资源共享群,只为共同的进步!
303 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtpPkt.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * RtpPkt is the basic class for creating and parsing RTP packets.
23 | *
24 | * There are two ways of instantiating an RtpPkt. One is for packets that you wish to send,
25 | * which requires that you provide basic information about the packet and a payload. Upon calling
26 | * encode() the fields of the structure are written into a bytebuffer, in the form that it would
27 | * sent across the network, excluding the UDP headers.
28 | *
29 | * The other way is by passing a bytebuffer. The assumption is that this is a packet
30 | * that has been received from the network, excluding UDP headers, and the bytebuffer will
31 | * be parsed into the correct fields.
32 | *
33 | * The class keeps track of changes. Therefore, modifications are possible after calling encode(),
34 | * if necessary, the raw version of the packet will be regenerated on subsequent requests.
35 | *
36 | * @author Arne Kepp
37 | */
38 | public class RtpPkt {
39 | /** Whether the packet has been changed since encode() */
40 | private boolean rawPktCurrent = false;
41 | /** The version, always 2, 2 bits */
42 | private int version = 2; //2 bits
43 | /** Whether the packet is padded, 1 bit */
44 | private int padding; //1 bit
45 | /** Whether and extension is used, 1 bit */
46 | private int extension = 0; //1 bit
47 | /** Whether the packet is marked, 1 bit */
48 | private int marker = 0; //1 bit
49 | /** What payload type is used, 7 bits */
50 | private int payloadType; //
51 | /** The sequence number, taken from RTP Session, 16 bits */
52 | private int seqNumber; //16 bits
53 | /** The RTP timestamp, 32bits */
54 | private long timeStamp; //32 bits
55 | /** The SSRC of the packet sender, 32 bits*/
56 | private long ssrc; //32 bits
57 | /** SSRCs of contributing sources, 32xn bits, n<16 */
58 | private long[] csrcArray = null;//
59 |
60 | /** Contains the actual data (eventually) */
61 | private byte[] rawPkt = null;
62 | /** The actual data, without any RTP stuff */
63 | private byte[] payload = null;
64 |
65 | /**
66 | * Construct a packet-instance. The ByteBuffer required for UDP transmission can afterwards be obtained from getRawPkt(). If you need to set additional parameters, such as the marker bit or contributing sources, you should do so before calling getRawPkt;
67 | *
68 | * @param aTimeStamp RTP timestamp for data
69 | * @param syncSource the SSRC, usually taken from RTPSession
70 | * @param seqNum Sequency number
71 | * @param plt Type of payload
72 | * @param pl Payload, the actual data
73 | */
74 | protected RtpPkt(long aTimeStamp, long syncSource, int seqNum, int plt, byte[] pl){
75 | int test = 0;
76 | test += setTimeStamp(aTimeStamp);
77 | test += setSsrc(syncSource);
78 | test += setSeqNumber(seqNum);
79 | test += setPayloadType(plt);
80 | test += setPayload(pl);
81 | if(test != 0) {
82 | System.out.println("RtpPkt() failed, check with checkPkt()");
83 | }
84 | rawPktCurrent = true;
85 | if( RTPSession.rtpDebugLevel > 5) {
86 | System.out.println("<--> RtpPkt(aTimeStamp, syncSource, seqNum, plt, pl)");
87 | }
88 | }
89 | /**
90 | * Construct a packet-instance from an raw packet (believed to be RTP). The UDP-headers must be removed before invoking this method. Call checkPkt on the instance to verify that it was successfully parsed.
91 | *
92 | * @param aRawPkt The data-part of a UDP-packet believed to be RTP
93 | * @param packetSize the number of valid octets in the packet, should be aRawPkt.length
94 | */
95 | protected RtpPkt(byte[] aRawPkt, int packetSize){
96 | if( RTPSession.rtpDebugLevel > 5) {
97 | System.out.println("-> RtpPkt(aRawPkt)");
98 | }
99 | //Check size, need to have at least a complete header
100 | if(aRawPkt == null) {
101 | System.out.println("RtpPkt(byte[]) Packet null");
102 | }
103 |
104 | int remOct = packetSize - 12;
105 | if(remOct >= 0) {
106 | rawPkt = aRawPkt; //Store it
107 | //Interrogate the packet
108 | sliceFirstLine();
109 | if(version == 2) {
110 | sliceTimeStamp();
111 | sliceSSRC();
112 | if(remOct > 4 && getCsrcCount() > 0) {
113 | sliceCSRCs();
114 | remOct -= csrcArray.length * 4; //4 octets per CSRC
115 | }
116 | // TODO Extension
117 | if(remOct > 0) {
118 | slicePayload(remOct);
119 | }
120 |
121 | //Sanity checks
122 | checkPkt();
123 |
124 | //Mark the buffer as current
125 | rawPktCurrent = true;
126 | } else {
127 | System.out.println("RtpPkt(byte[]) Packet is not version 2, giving up.");
128 | }
129 | } else {
130 | System.out.println("RtpPkt(byte[]) Packet too small to be sliced");
131 | }
132 | rawPktCurrent = true;
133 | if( RTPSession.rtpDebugLevel > 5) {
134 | System.out.println("<- RtpPkt(aRawPkt)");
135 | }
136 | }
137 |
138 | /*********************************************************************************************************
139 | * Reading stuff
140 | *********************************************************************************************************/
141 | protected int checkPkt() {
142 | //TODO, check for version 2 etc
143 | return 0;
144 | }
145 | protected int getHeaderLength() {
146 | //TODO include extension
147 | return 12 + 4*getCsrcCount();
148 | }
149 | protected int getPayloadLength() {
150 | return payload.length;
151 | }
152 | //public int getPaddingLength() {
153 | // return lenPadding;
154 | //}
155 | protected int getVersion() {
156 | return version;
157 | }
158 | //public boolean isPadded() {
159 | // if(lenPadding > 0) {
160 | // return true;
161 | // }else {
162 | // return false;
163 | // }
164 | //}
165 | //public int getHeaderExtension() {
166 | //TODO
167 | //}
168 | protected boolean isMarked() {
169 | return (marker != 0);
170 | }
171 | protected int getPayloadType() {
172 | return payloadType;
173 | }
174 |
175 | protected int getSeqNumber() {
176 | return seqNumber;
177 | }
178 | protected long getTimeStamp() {
179 | return timeStamp;
180 | }
181 | protected long getSsrc() {
182 | return ssrc;
183 | }
184 |
185 | protected int getCsrcCount() {
186 | if(csrcArray != null) {
187 | return csrcArray.length;
188 | }else{
189 | return 0;
190 | }
191 | }
192 | protected long[] getCsrcArray() {
193 | return csrcArray;
194 | }
195 |
196 | /**
197 | * Encodes the a
198 | */
199 | protected byte[] encode() {
200 | if(! rawPktCurrent || rawPkt == null) {
201 | writePkt();
202 | }
203 | return rawPkt;
204 | }
205 |
206 | /* For debugging purposes */
207 | protected void printPkt() {
208 | System.out.print("V:" + version + " P:" + padding + " EXT:" + extension);
209 | System.out.println(" CC:" + getCsrcCount() + " M:"+ marker +" PT:" + payloadType + " SN: "+ seqNumber);
210 | System.out.println("Timestamp:" + timeStamp + "(long output as int, may be 2s complement)");
211 | System.out.println("SSRC:" + ssrc + "(long output as int, may be 2s complement)");
212 | for(int i=0;i= 0) {
245 | rawPktCurrent = false;
246 | seqNumber = number;
247 | return 0;
248 | } else {
249 | System.out.println("RtpPkt.setSeqNumber: invalid number");
250 | return -1;
251 | }
252 | }
253 |
254 | protected int setTimeStamp(long time) {
255 | rawPktCurrent = false;
256 | timeStamp = time;
257 | return 0; //Naive for now
258 | }
259 |
260 | protected int setSsrc(long source) {
261 | rawPktCurrent = false;
262 | ssrc = source;
263 | return 0; //Naive for now
264 | }
265 |
266 | protected int setCsrcs(long[] contributors) {
267 | if(contributors.length <= 16) {
268 | csrcArray = contributors;
269 | return 0;
270 | } else {
271 | System.out.println("RtpPkt.setCsrcs: Cannot have more than 16 CSRCs");
272 | return -1;
273 | }
274 | }
275 |
276 | protected int setPayload(byte[] data) {
277 | // TODO Padding
278 | if(data.length < (1500 - 12)) {
279 | rawPktCurrent = false;
280 | payload = data;
281 | return 0;
282 | } else {
283 | System.out.println("RtpPkt.setPayload: Cannot carry more than 1480 bytes for now.");
284 | return -1;
285 | }
286 | }
287 | protected byte[] getPayload() {
288 | return payload;
289 | }
290 |
291 | /*********************************************************************************************************
292 | * Private functions
293 | *********************************************************************************************************/
294 | //Generate a bytebyffer representing the packet, store it.
295 | private void writePkt() {
296 | int bytes = getPayloadLength();
297 | int headerLen = getHeaderLength();
298 | int csrcLen = getCsrcCount();
299 | rawPkt = new byte[headerLen + bytes];
300 |
301 | // The first line contains, version and various bits
302 | writeFirstLine();
303 | byte[] someBytes = StaticProcs.uIntLongToByteWord(timeStamp);
304 | for(int i=0;i<4;i++) {
305 | rawPkt[i + 4] = someBytes[i];
306 | }
307 | //System.out.println("writePkt timeStamp:" + rawPkt[7]);
308 |
309 | someBytes = StaticProcs.uIntLongToByteWord(ssrc);
310 | System.arraycopy(someBytes, 0, rawPkt, 8, 4);
311 | //System.out.println("writePkt ssrc:" + rawPkt[11]);
312 |
313 | for(int i=0; i>> 6);
342 | padding = ((rawPkt[0] & 0x20) >>> 5);
343 | extension = ((rawPkt[0] & 0x10) >>> 4);
344 | csrcArray = new long[(rawPkt[0] & 0x0F)];
345 | marker = ((rawPkt[1] & 0x80) >> 7);
346 | payloadType = (rawPkt[1] & 0x7F);
347 | seqNumber = StaticProcs.bytesToUIntInt(rawPkt, 2);
348 | }
349 | //Takes the 4 octets representing the timestamp
350 | private void sliceTimeStamp() {
351 | timeStamp = StaticProcs.bytesToUIntLong(rawPkt, 4);
352 | }
353 | //Takes the 4 octets representing the SSRC
354 | private void sliceSSRC() {
355 | ssrc = StaticProcs.bytesToUIntLong(rawPkt,8);
356 | }
357 | //Check the length of the csrcArray (set during sliceFirstLine)
358 | private void sliceCSRCs() {
359 | for(int i=0; i< csrcArray.length; i++) {
360 | ssrc = StaticProcs.bytesToUIntLong(rawPkt, i*4 + 12);
361 | }
362 | }
363 | //Extensions //TODO
364 | private void slicePayload(int bytes) {
365 | payload = new byte[bytes];
366 | int headerLen = getHeaderLength();
367 |
368 | System.arraycopy(rawPkt, headerLen, payload, 0, bytes);
369 | }
370 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RtcpPktPSFB.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | /**
22 | * RTCP packets for Payload-Specific Feedback Messages
23 | *
24 | * @author Arne Kepp
25 | */
26 | public class RtcpPktPSFB extends RtcpPkt {
27 | /** If this packet was for a different SSRC */
28 | protected boolean notRelevant = false;
29 | /** Parent RTP Session */
30 | private RTPSession rtpSession;
31 | /** SSRC we are sending feeback to */
32 | protected long ssrcMediaSource = -1;
33 |
34 | /** SLI macroblock (MB) address of the first lost macroblock number */
35 | protected int[] sliFirst;
36 | /** SLI number of lost macroblocks */
37 | protected int[] sliNumber;
38 | /** SLI six least significant bits of the codec-specific identifier */
39 | protected int[] sliPictureId;
40 |
41 | // Picture loss indication
42 | /** RPSI number of padded bits at end of bitString */
43 | protected int rpsiPadding = -1;
44 | /** RPSI payloadType RTP payload type */
45 | protected int rpsiPayloadType = -1;
46 | /** RPSI information as natively defined by the video codec */
47 | protected byte[] rpsiBitString;
48 |
49 | /** Application Layer Feedback Message */
50 | protected byte[] alfBitString;
51 |
52 | /**
53 | * Generic constructor, then call make
54 | *
55 | * @param ssrcPacketSender
56 | * @param ssrcMediaSource
57 | */
58 | protected RtcpPktPSFB(long ssrcPacketSender, long ssrcMediaSource) {
59 | super.ssrc = ssrcPacketSender;
60 | this.ssrcMediaSource = ssrcMediaSource;
61 | super.packetType = 206; //PSFB
62 | }
63 |
64 | /**
65 | * Make this packet a Picture loss indication
66 | */
67 | protected void makePictureLossIndication() {
68 | super.itemCount = 1; //FMT
69 | }
70 |
71 | /**
72 | * Make this packet a Slice Loss Indication
73 | *
74 | * @param sliFirst macroblock (MB) address of the first lost macroblock
75 | * @param sliNumber number of lost macroblocks
76 | * @param sliPictureId six least significant bits of the codec-specific identifier
77 | */
78 | protected void makeSliceLossIndication(int[] sliFirst, int[] sliNumber, int[] sliPictureId) {
79 | super.itemCount = 2; //FMT
80 | this.sliFirst = sliFirst;
81 | this.sliNumber = sliNumber;
82 | this.sliPictureId = sliPictureId;
83 | }
84 |
85 | /**
86 | * Make this packet a Reference Picture Selection Indication
87 | *
88 | * @param bitPadding number of padded bits at end of bitString
89 | * @param payloadType RTP payload type for codec
90 | * @param bitString RPSI information as natively defined by the video codec
91 | */
92 | protected void makeRefPictureSelIndic(int bitPadding, int payloadType, byte[] bitString) {
93 | super.itemCount = 3; //FMT
94 | this.rpsiPadding = bitPadding;
95 | this.rpsiPayloadType = payloadType;
96 | this.rpsiBitString = bitString;
97 | }
98 |
99 | /**
100 | * Make this packet an Application specific feedback message
101 | *
102 | * @param bitString the original application message
103 | */
104 | protected void makeAppLayerFeedback(byte[] bitString) {
105 | super.itemCount = 15; //FMT
106 | this.alfBitString = bitString;
107 | }
108 |
109 | /**
110 | * Constructor that parses a raw packet to retrieve information
111 | *
112 | * @param aRawPkt the raw packet to be parsed
113 | * @param start the start of the packet, in bytes
114 | * @param rtpSession the session on which the callback interface resides
115 | */
116 | protected RtcpPktPSFB(byte[] aRawPkt, int start, RTPSession rtpSession) {
117 | if(RTPSession.rtpDebugLevel > 8) {
118 | System.out.println(" -> RtcpPktPSFB(byte[], int start)");
119 | }
120 | this.rtpSession = rtpSession;
121 |
122 | rawPkt = aRawPkt;
123 |
124 | if(! super.parseHeaders(start) || packetType != 206 || super.length < 2 ) {
125 | if(RTPSession.rtpDebugLevel > 2) {
126 | System.out.println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem");
127 | }
128 | super.problem = -206;
129 | } else {
130 | //FMT = super.itemCount;
131 | ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt,8+start);
132 |
133 | if(ssrcMediaSource == rtpSession.ssrc) {
134 | super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt,4+start);
135 |
136 | switch(super.itemCount) {
137 | case 1: // Picture Loss Indication
138 | decPictureLossIndic();
139 | break;
140 | case 2: // Slice Loss Indication
141 | decSliceLossIndic(aRawPkt, start + 12);
142 | break;
143 | case 3: // Reference Picture Selection Indication
144 | decRefPictureSelIndic(aRawPkt, start + 12);
145 | break;
146 | case 15: // Application Layer Feedback Messages
147 | decAppLayerFB(aRawPkt, start + 12);
148 | break;
149 | default:
150 | System.out.println("!!!! RtcpPktPSFB(byte[], int start) unexpected FMT " + super.itemCount);
151 | }
152 | } else {
153 | this.notRelevant = true;
154 | }
155 | }
156 | if(RTPSession.rtpDebugLevel > 8) {
157 | System.out.println(" <- RtcpPktPSFB()");
158 | }
159 | }
160 |
161 | /**
162 | * Decode Picture Loss indication
163 | *
164 | */
165 | private void decPictureLossIndic() {
166 | if(this.rtpSession.rtcpAVPFIntf != null) {
167 | this.rtpSession.rtcpAVPFIntf.PSFBPktPictureLossReceived(
168 | super.ssrc);
169 | }
170 | }
171 |
172 | /**
173 | * Decode Slice Loss Indication
174 | *
175 | * @param aRawPkt
176 | * @param start
177 | */
178 | private void decSliceLossIndic(byte[] aRawPkt, int start) {
179 | // 13 bit off-boundary numbers? That's rather cruel
180 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
181 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
182 | // | First | Number | PictureID |
183 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184 |
185 | int count = super.length - 2;
186 |
187 | sliFirst = new int[count];
188 | sliNumber = new int[count];
189 | sliPictureId = new int[count];
190 |
191 | // Loop over the FCI lines
192 | for(int i=0; i < count; i++) {
193 | sliFirst[i] = StaticProcs.bytesToUIntInt(aRawPkt, start) >> 3;
194 | sliNumber[i] = (int) (StaticProcs.bytesToUIntInt(aRawPkt, start) & 0x0007FFC0) >> 6;
195 | sliPictureId[i] = (StaticProcs.bytesToUIntInt(aRawPkt, start + 2) & 0x003F);
196 | start += 4;
197 | }
198 |
199 | if(this.rtpSession.rtcpAVPFIntf != null) {
200 | this.rtpSession.rtcpAVPFIntf.PSFBPktSliceLossIndic(
201 | super.ssrc,
202 | sliFirst, sliNumber, sliPictureId);
203 | }
204 | }
205 |
206 | /**
207 | * Decode Reference Picture Selection Indication
208 | *
209 | * @param aRawPkt
210 | * @param start
211 | */
212 | private void decRefPictureSelIndic(byte[] aRawPkt, int start) {
213 | // 0 1 2 3
214 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
215 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
216 | // | PB |0| Payload Type| Native RPSI bit string |
217 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
218 | // | defined per codec ... | Padding (0) |
219 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
220 |
221 | rpsiPadding = aRawPkt[start];
222 |
223 | if(rpsiPadding > 32) {
224 | System.out.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic paddingBits: "
225 | + rpsiPadding);
226 | }
227 |
228 | rpsiPayloadType = (int) rawPkt[start];
229 | if(rpsiPayloadType < 0) {
230 | System.out.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic 8th bit not zero: "
231 | + rpsiPayloadType);
232 | }
233 |
234 | rpsiBitString = new byte[(super.length - 2)*4 - 2];
235 | System.arraycopy(aRawPkt, start + 2, rpsiBitString, 0, rpsiBitString.length);
236 |
237 | if(this.rtpSession.rtcpAVPFIntf != null) {
238 | this.rtpSession.rtcpAVPFIntf.PSFBPktRefPictureSelIndic(
239 | super.ssrc,
240 | rpsiPayloadType, rpsiBitString, rpsiPadding);
241 | }
242 |
243 | }
244 |
245 | /**
246 | * Decode Application specific feedback message
247 | *
248 | * @param aRawPkt
249 | * @param start
250 | */
251 | private void decAppLayerFB(byte[] aRawPkt, int start) {
252 | //Application Message (FCI): variable length
253 | int stringLength = (super.length - 2)*4;
254 |
255 | alfBitString = new byte[stringLength];
256 |
257 | System.arraycopy(aRawPkt, start, alfBitString, 0, stringLength);
258 |
259 | if(this.rtpSession.rtcpAVPFIntf != null) {
260 | this.rtpSession.rtcpAVPFIntf.PSFBPktAppLayerFBReceived(
261 | super.ssrc, alfBitString);
262 | }
263 | }
264 |
265 |
266 |
267 | /**
268 | * Encode a Slice Loss Indication
269 | */
270 | private void encSliceLossIndic() {
271 | byte[] firstBytes;
272 | byte[] numbBytes;
273 | byte[] picBytes;
274 |
275 | int offset = 8;
276 | // Loop over the FCI lines
277 | for(int i=0; i < sliFirst.length; i++) {
278 | offset = 8 + 8*i;
279 | firstBytes = StaticProcs.uIntLongToByteWord(sliFirst[i] << 3);
280 | numbBytes = StaticProcs.uIntLongToByteWord(sliNumber[i] << 2);
281 | picBytes = StaticProcs.uIntIntToByteWord(sliPictureId[i]);
282 |
283 | super.rawPkt[offset] = firstBytes[2];
284 | super.rawPkt[offset+1] = (byte) (firstBytes[3] | numbBytes[2]);
285 | super.rawPkt[offset+2] = numbBytes[3];
286 | super.rawPkt[offset+3] = (byte) (numbBytes[3] | picBytes[1]);
287 | }
288 | }
289 |
290 | /**
291 | * Encode a Reference Picture Selection Indication
292 | *
293 | */
294 | private void encRefPictureSelIndic() {
295 | byte[] someBytes;
296 | someBytes = StaticProcs.uIntIntToByteWord(rpsiPadding);
297 | super.rawPkt[8] = someBytes[1];
298 | someBytes = StaticProcs.uIntIntToByteWord(rpsiPayloadType);
299 | super.rawPkt[9] = someBytes[1];
300 |
301 | System.arraycopy(rpsiBitString, 0, super.rawPkt, 10, rpsiBitString.length);
302 | }
303 |
304 |
305 | /**
306 | * Encode Application Layer Feedback
307 | *
308 | */
309 | private void encAppLayerFB() {
310 | //Application Message (FCI): variable length
311 | System.arraycopy(alfBitString, 0, super.rawPkt, 8, alfBitString.length);
312 | }
313 |
314 | /**
315 | * Get the FMT (Feedback Message Type)
316 | * @return value stored in .itemcount, same field
317 | */
318 | protected int getFMT() {
319 | return this.itemCount;
320 | }
321 |
322 | /**
323 | * Encode the packet into a byte[], saved in .rawPkt
324 | *
325 | * CompRtcpPkt will call this automatically
326 | */
327 | protected void encode() {
328 | switch(super.itemCount) {
329 | case 1: // Picture Loss Indication
330 | //Nothing to do really
331 | super.rawPkt = new byte[24];
332 | break;
333 | case 2: // Slice Loss Indication
334 | super.rawPkt = new byte[24 + 4*this.sliFirst.length];
335 | encSliceLossIndic();
336 | break;
337 | case 3: // Reference Picture Selection Indication
338 | super.rawPkt = new byte[24 + 2 + this.rpsiBitString.length/4];
339 | encRefPictureSelIndic();
340 | break;
341 | case 15: // Application Layer Feedback Messages
342 | super.rawPkt = new byte[24 + this.alfBitString.length/4];
343 | encAppLayerFB();
344 | break;
345 | }
346 |
347 | byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
348 | System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
349 | someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource);
350 | System.arraycopy(someBytes, 0, super.rawPkt, 8, 4);
351 |
352 | writeHeaders();
353 | }
354 |
355 | /**
356 | * Debug purposes only
357 | */
358 | public void debugPrint() {
359 | System.out.println("->RtcpPktPSFB.debugPrint() ");
360 |
361 | String str;
362 | switch(super.itemCount) {
363 | case 1: // Picture Loss Indication
364 | System.out.println(" FMT: Picture Loss Indication");
365 | break;
366 | case 2: // Slice Loss Indication
367 | if(sliFirst != null) {
368 | str = "sliFirst[].length: " + sliFirst.length;
369 | } else {
370 | str = "sliFirst[] is null";
371 | }
372 | System.out.println(" FMT: Slice Loss Indication, " + str);
373 | break;
374 | case 3: // Reference Picture Selection Indication
375 | if(rpsiBitString != null) {
376 | str = "rpsiBitString[].length: " + rpsiBitString.length;
377 | } else {
378 | str = "rpsiBitString[] is null";
379 | }
380 | System.out.println(" FMT: Reference Picture Selection Indication, "
381 | + str + " payloadType: " + this.rpsiPayloadType);
382 | break;
383 | case 15: // Application Layer Feedback Messages
384 | if(alfBitString != null) {
385 | str = "alfBitString[].length: " + alfBitString.length;
386 | } else {
387 | str = "alfBitString[] is null";
388 | }
389 | System.out.println(" FMT: Application Layer Feedback Messages, " + str);
390 | break;
391 | }
392 |
393 | System.out.println("<-RtcpPktPSFB.debugPrint() ");
394 | }
395 | }
396 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xuchongyang/mediacodecdemo/jlibrtp/RTCPReceiverThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java RTP Library (jlibrtp)
3 | * Copyright (C) 2006 Arne Kepp
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | package com.xuchongyang.mediacodecdemo.jlibrtp;
20 |
21 | import java.io.IOException;
22 | import java.net.DatagramPacket;
23 | import java.net.InetSocketAddress;
24 | import java.util.Enumeration;
25 | import java.util.Iterator;
26 |
27 | /**
28 | * This thread hangs on the RTCP socket and waits for new packets
29 | *
30 | * @author Arne Kepp
31 | *
32 | */
33 | public class RTCPReceiverThread extends Thread {
34 | /** Parent RTP Session */
35 | private RTPSession rtpSession = null;
36 | /** Parent RTCP Session */
37 | private RTCPSession rtcpSession = null;
38 |
39 | /**
40 | * Constructor for new thread
41 | * @param rtcpSession parent RTCP session
42 | * @param rtpSession parent RTP session
43 | */
44 | RTCPReceiverThread(RTCPSession rtcpSession, RTPSession rtpSession) {
45 | this.rtpSession = rtpSession;
46 | this.rtcpSession = rtcpSession;
47 |
48 | if(RTPSession.rtpDebugLevel > 1) {
49 | System.out.println("<-> RTCPReceiverThread created");
50 | }
51 |
52 | }
53 |
54 | /**
55 | * Find out whether a participant with this SSRC is known.
56 | *
57 | * If the user is unknown, and the system is operating in unicast mode,
58 | * try to match the ip-address of the sender to the ip address of a
59 | * previously unmatched target
60 | *
61 | * @param ssrc the SSRC of the participant
62 | * @param packet the packet that notified us
63 | * @return the relevant participant, possibly newly created
64 | */
65 | private Participant findParticipant(long ssrc, DatagramPacket packet) {
66 | Participant p = rtpSession.partDb.getParticipant(ssrc);
67 | if(p == null) {
68 | Enumeration enu = rtpSession.partDb.getParticipants();
69 | while(enu.hasMoreElements()) {
70 | Participant tmp = (Participant) enu.nextElement();
71 | if(tmp.ssrc < 0 &&
72 | (tmp.rtcpAddress.getAddress().equals(packet.getAddress())
73 | || tmp.rtpAddress.getAddress().equals(packet.getAddress()))) {
74 |
75 | // Best guess
76 | System.out.println("RTCPReceiverThread: Got an unexpected packet from SSRC:"
77 | + ssrc + " @" + packet.getAddress().toString() + ", WAS able to match it." );
78 |
79 | tmp.ssrc = ssrc;
80 | return tmp;
81 | }
82 | }
83 | // Create an unknown sender
84 | System.out.println("RTCPReceiverThread: Got an unexpected packet from SSRC:"
85 | + ssrc + " @" + packet.getAddress().toString() + ", was NOT able to match it." );
86 | p = new Participant((InetSocketAddress) null, (InetSocketAddress) packet.getSocketAddress(), ssrc);
87 | rtpSession.partDb.addParticipant(2,p);
88 | }
89 | return p;
90 | }
91 |
92 |
93 | /**
94 | * Parse a received UDP packet
95 | *
96 | * Perform the header checks and extract the RTCP packets in it
97 | *
98 | * @param packet the packet to be parsed
99 | * @return -1 if there was a problem, 0 if successfully parsed
100 | */
101 | private int parsePacket(DatagramPacket packet) {
102 |
103 | if(packet.getLength() % 4 != 0) {
104 | if(RTPSession.rtcpDebugLevel > 2) {
105 | System.out.println("RTCPReceiverThread.parsePacket got packet that had length " + packet.getLength());
106 | }
107 | return -1;
108 | } else {
109 | byte[] rawPkt = packet.getData();
110 |
111 | // Parse the received compound RTCP (?) packet
112 | CompRtcpPkt compPkt = new CompRtcpPkt(rawPkt, packet.getLength(),
113 | (InetSocketAddress) packet.getSocketAddress(), rtpSession);
114 |
115 | if(this.rtpSession.debugAppIntf != null) {
116 | String intfStr;
117 |
118 | if(rtpSession.mcSession) {
119 | intfStr = this.rtcpSession.rtcpMCSock.getLocalSocketAddress().toString();
120 | } else {
121 | intfStr = this.rtpSession.rtpSock.getLocalSocketAddress().toString();
122 | }
123 |
124 | if( compPkt.problem == 0) {
125 | String str = new String("Received compound RTCP packet of size " + packet.getLength() +
126 | " from " + packet.getSocketAddress().toString() + " via " + intfStr
127 | + " containing " + compPkt.rtcpPkts.size() + " packets" );
128 |
129 | this.rtpSession.debugAppIntf.packetReceived(1,
130 | (InetSocketAddress) packet.getSocketAddress(), str);
131 | } else {
132 | String str = new String("Received invalid RTCP packet of size " + packet.getLength() +
133 | " from " + packet.getSocketAddress().toString() + " via " + intfStr
134 | + ": " + this.debugErrorString(compPkt.problem) );
135 |
136 | this.rtpSession.debugAppIntf.packetReceived(-2,
137 | (InetSocketAddress) packet.getSocketAddress(), str);
138 | }
139 | }
140 |
141 | if(RTPSession.rtcpDebugLevel > 5) {
142 | Iterator iter = compPkt.rtcpPkts.iterator();
143 | String str = " ";
144 | while(iter.hasNext()) {
145 | RtcpPkt aPkt = iter.next();
146 | str += (aPkt.getClass().toString() + ":"+aPkt.itemCount+ ", ");
147 | }
148 | System.out.println("<-> RTCPReceiverThread.parsePacket() from " + packet.getSocketAddress().toString() + str);
149 | }
150 |
151 |
152 | //Loop over the information
153 | Iterator iter = compPkt.rtcpPkts.iterator();
154 |
155 | long curTime = System.currentTimeMillis();
156 |
157 | while(iter.hasNext()) {
158 | RtcpPkt aPkt = (RtcpPkt) iter.next();
159 |
160 | // Our own packets should already have been filtered out.
161 | if(aPkt.ssrc == rtpSession.ssrc) {
162 | System.out.println("RTCPReceiverThread() received RTCP packet"
163 | + " with conflicting SSRC from " + packet.getSocketAddress().toString());
164 | rtpSession.resolveSsrcConflict();
165 | return -1;
166 | }
167 |
168 | /** Receiver Reports **/
169 | if( aPkt.getClass() == RtcpPktRR.class) {
170 | RtcpPktRR rrPkt = (RtcpPktRR) aPkt;
171 |
172 | Participant p = findParticipant(rrPkt.ssrc, packet);
173 | p.lastRtcpPkt = curTime;
174 |
175 | if(rtpSession.rtcpAppIntf != null) {
176 | rtpSession.rtcpAppIntf.RRPktReceived(rrPkt.ssrc, rrPkt.reporteeSsrc,
177 | rrPkt.lossFraction, rrPkt.lostPktCount, rrPkt.extHighSeqRecv,
178 | rrPkt.interArvJitter, rrPkt.timeStampLSR, rrPkt.delaySR);
179 | }
180 |
181 | /** Sender Reports **/
182 | } else if(aPkt.getClass() == RtcpPktSR.class) {
183 | RtcpPktSR srPkt = (RtcpPktSR) aPkt;
184 |
185 | Participant p = findParticipant(srPkt.ssrc, packet);
186 | p.lastRtcpPkt = curTime;
187 |
188 | if(p != null) {
189 |
190 | if(p.ntpGradient < 0 && p.lastNtpTs1 > -1) {
191 | //Calculate gradient NTP vs RTP
192 | long newTime = StaticProcs.undoNtpMess(srPkt.ntpTs1, srPkt.ntpTs2);
193 | p.ntpGradient = ((double) (newTime - p.ntpOffset))/((double) srPkt.rtpTs - p.lastSRRtpTs);
194 | if(RTPSession.rtcpDebugLevel > 4) {
195 | System.out.println("RTCPReceiverThread calculated NTP vs RTP gradient: " + Double.toString(p.ntpGradient));
196 | }
197 | } else {
198 | // Calculate sum of ntpTs1 and ntpTs2 in milliseconds
199 | p.ntpOffset = StaticProcs.undoNtpMess(srPkt.ntpTs1, srPkt.ntpTs2);
200 | p.lastNtpTs1 = srPkt.ntpTs1;
201 | p.lastNtpTs2 = srPkt.ntpTs2;
202 | p.lastSRRtpTs = srPkt.rtpTs;
203 | }
204 |
205 | // For the next RR
206 | p.timeReceivedLSR = curTime;
207 | p.setTimeStampLSR(srPkt.ntpTs1,srPkt.ntpTs2);
208 |
209 | }
210 |
211 |
212 | if(rtpSession.rtcpAppIntf != null) {
213 | if(srPkt.rReports != null) {
214 | rtpSession.rtcpAppIntf.SRPktReceived(srPkt.ssrc, srPkt.ntpTs1, srPkt.ntpTs2,
215 | srPkt.rtpTs, srPkt.sendersPktCount, srPkt.sendersPktCount,
216 | srPkt.rReports.reporteeSsrc, srPkt.rReports.lossFraction, srPkt.rReports.lostPktCount,
217 | srPkt.rReports.extHighSeqRecv, srPkt.rReports.interArvJitter, srPkt.rReports.timeStampLSR,
218 | srPkt.rReports.delaySR);
219 | } else {
220 | rtpSession.rtcpAppIntf.SRPktReceived(srPkt.ssrc, srPkt.ntpTs1, srPkt.ntpTs2,
221 | srPkt.rtpTs, srPkt.sendersPktCount, srPkt.sendersPktCount,
222 | null, null, null,
223 | null, null, null,
224 | null);
225 | }
226 | }
227 |
228 | /** Source Descriptions **/
229 | } else if(aPkt.getClass() == RtcpPktSDES.class) {
230 | RtcpPktSDES sdesPkt = (RtcpPktSDES) aPkt;
231 |
232 | // The the participant database is updated
233 | // when the SDES packet is reconstructed by CompRtcpPkt
234 | if(rtpSession.rtcpAppIntf != null) {
235 | rtpSession.rtcpAppIntf.SDESPktReceived(sdesPkt.participants);
236 | }
237 |
238 | /** Bye Packets **/
239 | } else if(aPkt.getClass() == RtcpPktBYE.class) {
240 | RtcpPktBYE byePkt = (RtcpPktBYE) aPkt;
241 |
242 | long time = System.currentTimeMillis();
243 | Participant[] partArray = new Participant[byePkt.ssrcArray.length];
244 |
245 | for(int i=0; i 1) {
309 | if(rtpSession.mcSession) {
310 | System.out.println("-> RTCPReceiverThread.run() starting on MC " + rtcpSession.rtcpMCSock.getLocalPort() );
311 | } else {
312 | System.out.println("-> RTCPReceiverThread.run() starting on " + rtcpSession.rtcpSock.getLocalPort() );
313 | }
314 | }
315 |
316 | while(!rtpSession.endSession) {
317 |
318 | if(RTPSession.rtcpDebugLevel > 4) {
319 | if(rtpSession.mcSession) {
320 | System.out.println("-> RTCPReceiverThread.run() waiting for packet on MC " + rtcpSession.rtcpMCSock.getLocalPort() );
321 | } else {
322 | System.out.println("-> RTCPReceiverThread.run() waiting for packet on " + rtcpSession.rtcpSock.getLocalPort() );
323 | }
324 | }
325 |
326 | // Prepare a packet
327 | byte[] rawPkt = new byte[1500];
328 | DatagramPacket packet = new DatagramPacket(rawPkt, rawPkt.length);
329 |
330 | // Wait for it to arrive
331 | if(! rtpSession.mcSession) {
332 | //Unicast
333 | try {
334 | rtcpSession.rtcpSock.receive(packet);
335 | } catch (IOException e) {
336 | if(!rtpSession.endSession) {
337 | e.printStackTrace();
338 | } else {
339 | continue;
340 | }
341 | }
342 | } else {
343 | //Multicast
344 | try {
345 | rtcpSession.rtcpMCSock.receive(packet);
346 | } catch (IOException e) {
347 | if(!rtpSession.endSession) {
348 | e.printStackTrace();
349 | } else {
350 | continue;
351 | }
352 | }
353 | }
354 |
355 | // Check whether this is one of our own
356 | if( (rtpSession.mcSession && ! packet.getSocketAddress().equals(rtcpSession.rtcpMCSock) )
357 | || ! packet.getSocketAddress().equals(rtcpSession.rtcpSock) ) {
358 | //System.out.println("Packet received from: " + packet.getSocketAddress().toString());
359 | parsePacket(packet);
360 | //rtpSession.partDb.debugPrint();
361 | }
362 | }
363 |
364 | if(RTPSession.rtcpDebugLevel > 1) {
365 | System.out.println("<-> RTCPReceiverThread terminating");
366 | }
367 | }
368 |
369 | }
370 |
--------------------------------------------------------------------------------