├── .gitignore
├── AndroidManifest.xml
├── README.md
├── app
├── app.iml
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── schriek
│ │ └── rtmpdump
│ │ ├── AppendingAdapter.java
│ │ ├── CommandFragment.java
│ │ ├── HelpFragment.java
│ │ ├── ListAdapter.java
│ │ ├── LogFragment.java
│ │ ├── MainActivity.java
│ │ ├── Parser.java
│ │ ├── Rtmpdump.java
│ │ └── callBack.java
│ ├── jni
│ ├── dump
│ │ ├── librtmp
│ │ │ ├── amf.c
│ │ │ ├── amf.h
│ │ │ ├── bytes.h
│ │ │ ├── dh.h
│ │ │ ├── dhgroups.h
│ │ │ ├── handshake.h
│ │ │ ├── hashswf.c
│ │ │ ├── http.h
│ │ │ ├── log.c
│ │ │ ├── log.h
│ │ │ ├── parseurl.c
│ │ │ ├── rtmp.c
│ │ │ ├── rtmp.h
│ │ │ └── rtmp_sys.h
│ │ └── rtmpdump.c
│ └── jniinterface.cpp
│ └── res
│ ├── drawable-hdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
│ ├── drawable-ldpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
│ ├── layout
│ ├── activity_main.xml
│ ├── commandfragment.xml
│ └── helpfragment.xml
│ ├── menu
│ └── activity_main.xml
│ ├── values-v11
│ └── styles.xml
│ ├── values-v14
│ └── styles.xml
│ └── values
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard-project.txt
├── project.properties
├── rtmpdump-android.iml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | .idea/
16 |
17 | # Gradle files
18 | .gradle/
19 | build/
20 |
21 | # Local configuration file (sdk path, etc)
22 | local.properties
23 |
24 | # Proguard folder generated by Eclipse
25 | proguard/
26 |
27 | # Log Files
28 | *.log
29 |
30 | # Android Studio Navigation editor temp files
31 | .navigation/
32 |
33 | # Android Studio captures folder
34 | captures/
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | rtmpdump-android
2 | ================
3 |
4 | Rtmpdump utility ported to android (With a little UI)
5 |
6 | All credits go to the original developer of this piece of software
7 |
8 | http://rtmpdump.mplayerhq.hu/
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.model.application'
2 |
3 | model {
4 |
5 | android {
6 | compileSdkVersion = 23
7 | buildToolsVersion = "23.0.2"
8 |
9 | defaultConfig.with {
10 | applicationId = "com.schriek.rtmpdump"
11 | minSdkVersion.apiLevel = 19
12 | targetSdkVersion.apiLevel = 23
13 | }
14 | }
15 |
16 | android.ndk {
17 | moduleName = "rtmpdump"
18 | toolchain = "gcc"
19 | toolchainVersion = "4.9"
20 | abiFilters.addAll(["armeabi-v7a"])
21 | ldLibs.addAll(["log"])
22 | CFlags.addAll(["-DNO_CRYPTO", "-DRTMPDUMP_VERSION=\"v2.4\"", "-D__STDC_CONSTANT_MACROS", "-DLOG_TO_LIST"])
23 | cppFlags.addAll([""])
24 | }
25 |
26 | android.buildTypes {
27 | release {
28 | minifyEnabled = false
29 | }
30 | }
31 |
32 | }
33 |
34 |
35 | dependencies {
36 |
37 | compile fileTree(dir: 'libs', include: ['*.jar'])
38 | compile 'com.android.support:appcompat-v7:23.+'
39 | compile 'com.android.support:design:23.+'
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/AppendingAdapter.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import java.util.Vector;
4 |
5 | import android.content.Context;
6 | import android.graphics.Color;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.BaseAdapter;
11 | import android.widget.TextView;
12 |
13 | public class AppendingAdapter extends BaseAdapter {
14 |
15 | private Context mContext;
16 | private Vector items = new Vector();
17 |
18 | public void add(String s) {
19 | items.add(s);
20 |
21 | }
22 |
23 | public AppendingAdapter(Context con) {
24 | mContext = con;
25 | }
26 |
27 | public int getCount() {
28 | // TODO Auto-generated method stub
29 | return items.size();
30 | }
31 |
32 | public Object getItem(int position) {
33 | // TODO Auto-generated method stub
34 | return items.get(position);
35 | }
36 |
37 | public long getItemId(int position) {
38 | // TODO Auto-generated method stub
39 | return 0;
40 | }
41 |
42 | public View getView(int position, View convertView, ViewGroup parent) {
43 | // TODO Auto-generated method stub
44 |
45 | TextView text = new TextView(mContext);
46 | text.setTextColor(Color.BLACK);
47 | text.setText(items.get(position));
48 |
49 | return text;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/CommandFragment.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import android.support.v4.app.Fragment;
4 |
5 | public class CommandFragment extends Fragment {
6 |
7 | /* Just a placeholder for the possible future */
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/HelpFragment.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import java.util.List;
4 |
5 | import android.os.Bundle;
6 | import android.support.v4.app.Fragment;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ListView;
11 |
12 | public class HelpFragment extends Fragment {
13 |
14 | private ListView list;
15 |
16 | private static String[] usage = {
17 | "--help|-h Prints this help screen.\n",
18 | "--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n",
19 | "--host|-n hostname Overrides the hostname in the rtmp url\n",
20 | "--port|-c port Overrides the port in the rtmp url\n",
21 | "--socks|-S host:port Use the specified SOCKS proxy\n",
22 | "--protocol|-l num Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n",
23 | "--playpath|-y path Overrides the playpath parsed from rtmp url\n",
24 | "--playlist|-Y Set playlist before playing\n",
25 | "--swfUrl|-s url URL to player swf file\n",
26 | "--tcUrl|-t url URL to played stream (default: \"rtmp://host[:port]/app\")\n",
27 | "--pageUrl|-p url Web URL of played programme\n",
28 | "--app|-a app Name of target app on server\n",
29 | "--swfhash|-w hexstring SHA256 hash of the decompressed SWF file (32 bytes)\n",
30 | "--swfsize|-x num Size of the decompressed SWF file, required for SWFVerification\n",
31 | "--swfVfy|-W url URL to player swf file, compute hash/size automatically\n",
32 | "--swfAge|-X days Number of days to use cached SWF hash before refreshing\n",
33 | "--auth|-u string Authentication string to be appended to the connect string\n",
34 | "--conn|-C type:data Arbitrary AMF data to be appended to the connect string\n B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n Z:(null), NB:name:boolean, NS:name:string, NN:name:number\n",
35 | "--flashVer|-f string Flash version string (default: \"%s\")\n",
36 | "--live|-v Save a live stream, no --resume (seeking) of live streams possible\n",
37 | "--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n",
38 | "--realtime|-R Don't attempt to speed up download via the Pause/Unpause BUFX hack\n",
39 | "--flv|-o string FLV output file name, if the file name is - print stream to stdout\n",
40 | "--resume|-e Resume a partial RTMP download\n",
41 | "--timeout|-m num Timeout connection num seconds (default: %u)\n",
42 | "--start|-A num Start at num seconds into stream (not valid when using --live)\n",
43 | "--stop|-B num Stop at num seconds into stream\n",
44 | "--token|-T key Key for SecureToken response\n",
45 | "--jtv|-j JSON Authentication token for Justin.tv legacy servers\n",
46 | "--hashes|-# Display progress with hashes, not with the byte counter\n",
47 | "--buffer|-b Buffer time in milliseconds (default: %u)\n",
48 | "--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
49 | "--quiet|-q Suppresses all command output.\n",
50 | "--verbose|-V Verbose command output.\n",
51 | "--debug|-z Debug level command output.\n",
52 | "If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect " };
53 |
54 | @Override
55 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
56 | Bundle savedInstanceState) {
57 | if (container == null) {
58 | return null;
59 | }
60 |
61 | View helpfragment = inflater.inflate(R.layout.helpfragment, container,
62 | false);
63 |
64 | list = (ListView) helpfragment.findViewById(R.id.listView1);
65 |
66 | list.setAdapter(new ListAdapter(getActivity(), usage));
67 |
68 | return helpfragment;
69 | }
70 |
71 | public void onResult(List result) {
72 | // TODO Auto-generated method stub
73 | // System.out.println(result.size());
74 | // ]\list.setAdapter(new ListAdapter(getActivity(), result));
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/ListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import java.util.List;
4 |
5 | import android.content.Context;
6 | import android.graphics.Color;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.BaseAdapter;
10 | import android.widget.TextView;
11 |
12 | public class ListAdapter extends BaseAdapter {
13 |
14 | private Context mContext;
15 | private String[] items;
16 |
17 | public ListAdapter(Context con, String[] items) {
18 | mContext = con;
19 | this.items = items;
20 | }
21 |
22 | public int getCount() {
23 | // TODO Auto-generated method stub
24 | return items.length;
25 | }
26 |
27 | public Object getItem(int position) {
28 | // TODO Auto-generated method stub
29 | return items[position];
30 | }
31 |
32 | public long getItemId(int position) {
33 | // TODO Auto-generated method stub
34 | return 0;
35 | }
36 |
37 | public View getView(int position, View convertView, ViewGroup parent) {
38 | // TODO Auto-generated method stub
39 |
40 | TextView text = new TextView(mContext);
41 | text.setText(items[position]);
42 |
43 | return text;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/LogFragment.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.os.Environment;
6 | import android.support.v4.app.Fragment;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ListView;
11 |
12 | public class LogFragment extends Fragment {
13 |
14 | private ListView list;
15 | private static AppendingAdapter adapter;
16 |
17 | public LogFragment() {}
18 |
19 | public LogFragment(Context con) {
20 | adapter = new AppendingAdapter(con);
21 | }
22 |
23 | public static void Append(String s) {
24 | if(adapter == null) {
25 | return;
26 | }
27 |
28 | adapter.add(s);
29 | adapter.notifyDataSetChanged();
30 | }
31 |
32 | @Override
33 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
34 | Bundle savedInstanceState) {
35 | if (container == null) {
36 | return null;
37 | }
38 | String dir = Environment.getExternalStorageDirectory()
39 | .getAbsolutePath();
40 |
41 | Rtmpdump dump = new Rtmpdump();
42 | dump.parseString("rtmpdump -r rtmp://owned.fc.llnwd.net:1935/own3duslive-live -a owned -f WIN 11,1,102,62 -W http://static.ec.own3d.tv/player/Own3dPlayerV3_07.swf -p http://www.own3d.tv/live/10588 --live -y 2dd-dota_10588 -o "+ dir + "/test.flv");
43 |
44 | View commandfragment = inflater.inflate(R.layout.commandfragment,
45 | container, false);
46 |
47 | list = (ListView) commandfragment.findViewById(R.id.commandLog);
48 |
49 | list.setAdapter(adapter);
50 |
51 | return commandfragment;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import android.os.Bundle;
4 | import android.os.Environment;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentActivity;
7 | import android.support.v4.app.FragmentManager;
8 | import android.support.v4.app.FragmentPagerAdapter;
9 | import android.support.v4.view.ViewPager;
10 | import android.view.Gravity;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.TextView;
15 |
16 | public class MainActivity extends FragmentActivity {
17 |
18 | /**
19 | * The {@link android.support.v4.view.PagerAdapter} that will provide
20 | * fragments for each of the sections. We use a
21 | * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
22 | * will keep every loaded fragment in memory. If this becomes too memory
23 | * intensive, it may be best to switch to a
24 | * {@link android.support.v4.app.FragmentStatePagerAdapter}.
25 | */
26 | SectionsPagerAdapter mSectionsPagerAdapter;
27 |
28 | /**
29 | * The {@link ViewPager} that will host the section contents.
30 | */
31 | ViewPager mViewPager;
32 |
33 | @Override
34 | public void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_main);
37 | // Create the adapter that will return a fragment for each of the three
38 | // primary sections
39 | // of the app.
40 | mSectionsPagerAdapter = new SectionsPagerAdapter(
41 | getSupportFragmentManager());
42 |
43 | // Set up the ViewPager with the sections adapter.
44 | mViewPager = (ViewPager) findViewById(R.id.pager);
45 | mViewPager.setAdapter(mSectionsPagerAdapter);
46 |
47 |
48 |
49 |
50 | // dump.parseString("rtmpdump -r rtmp://owned.fc.llnwd.net:1935/own3duslive-live -a owned -f WIN 11,1,102,62 -W http://static.ec.own3d.tv/player/Own3dPlayerV3_07.swf -p http://www.own3d.tv/live/10588 --live -y 2dd-dota_10588 -o "
51 | // + dir + "/test.flv");
52 | // dump.testHelpOutput();
53 |
54 | }
55 |
56 | /*
57 | * @Override public boolean onCreateOptionsMenu(Menu menu) {
58 | * getMenuInflater().inflate(R.menu.activity_main, menu); return true; }
59 | */
60 |
61 | /**
62 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
63 | * one of the primary sections of the app.
64 | */
65 | public class SectionsPagerAdapter extends FragmentPagerAdapter {
66 |
67 | public SectionsPagerAdapter(FragmentManager fm) {
68 | super(fm);
69 | }
70 |
71 | @Override
72 | public Fragment getItem(int i) {
73 | Fragment fragment = null;
74 |
75 | switch (i) {
76 | case 0:
77 | return new CommandFragment();
78 | case 1:
79 | return new LogFragment(getApplicationContext());
80 | case 2:
81 | return new HelpFragment();
82 | }
83 |
84 | /*
85 | * Fragment fragment = new DummySectionFragment(); Bundle args = new
86 | * Bundle(); args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, i
87 | * + 1); fragment.setArguments(args);
88 | */
89 | return fragment;
90 | }
91 |
92 | @Override
93 | public int getCount() {
94 | return 3;
95 | }
96 |
97 | @Override
98 | public CharSequence getPageTitle(int position) {
99 | switch (position) {
100 | case 0:
101 | return getString(R.string.title_section1).toUpperCase();
102 | case 1:
103 | return "LOG";
104 | case 2:
105 | return getString(R.string.title_section2).toUpperCase();
106 | }
107 | return null;
108 | }
109 | }
110 |
111 | /**
112 | * A dummy fragment representing a section of the app, but that simply
113 | * displays dummy text.
114 | */
115 | public static class DummySectionFragment extends Fragment {
116 | public DummySectionFragment() {
117 | }
118 |
119 | public static final String ARG_SECTION_NUMBER = "section_number";
120 |
121 | @Override
122 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
123 | Bundle savedInstanceState) {
124 | TextView textView = new TextView(getActivity());
125 | textView.setGravity(Gravity.CENTER);
126 | Bundle args = getArguments();
127 | textView.setText(Integer.toString(args.getInt(ARG_SECTION_NUMBER)));
128 | return textView;
129 | }
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/Parser.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.lang.reflect.Method;
5 |
6 | public class Parser {
7 |
8 | public Parser(Class c) {
9 | Method[] clazz = c.getMethods();
10 |
11 | for(Method e : clazz) {
12 | if(e.getAnnotations() != null) {
13 | for(Annotation a : e.getAnnotations()) {
14 | System.out.println(a);
15 | }
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/Rtmpdump.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | import android.util.Log;
6 |
7 | public class Rtmpdump {
8 |
9 | private final String TAG = "Rtmpdump";
10 |
11 | public Rtmpdump() {
12 | LoadLib();
13 | }
14 |
15 | public void parseString(String str) {
16 | String[] split = str.split(" ");
17 |
18 | run(split);
19 | }
20 |
21 | public void testHelpOutput() {
22 | run(new String[] { "rtmpdump", "-h" });
23 | }
24 |
25 | protected void LoadLib() {
26 |
27 | try {
28 | System.loadLibrary("rtmpdump");
29 | } catch (UnsatisfiedLinkError e) {
30 | Log.e(TAG, e.getMessage());
31 | }
32 | }
33 |
34 | private native void testNative();
35 |
36 | public native void run(String[] args);
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/schriek/rtmpdump/callBack.java:
--------------------------------------------------------------------------------
1 | package com.schriek.rtmpdump;
2 |
3 | import android.util.Log;
4 |
5 | public class callBack {
6 |
7 | public static void testCallback(String s) {
8 | Log.i("Test", s);
9 |
10 | LogFragment.Append(s);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/amf.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2005-2008 Team XBMC
3 | * http://www.xbmc.org
4 | * Copyright (C) 2008-2009 Andrej Stepanchuk
5 | * Copyright (C) 2009-2010 Howard Chu
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | #include
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "amf.h"
32 | #include "log.h"
33 | #include "bytes.h"
34 |
35 | static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID };
36 | static const AVal AV_empty = { 0, 0 };
37 |
38 | /* Data is Big-Endian */
39 | unsigned short
40 | AMF_DecodeInt16(const char *data)
41 | {
42 | unsigned char *c = (unsigned char *) data;
43 | unsigned short val;
44 | val = (c[0] << 8) | c[1];
45 | return val;
46 | }
47 |
48 | unsigned int
49 | AMF_DecodeInt24(const char *data)
50 | {
51 | unsigned char *c = (unsigned char *) data;
52 | unsigned int val;
53 | val = (c[0] << 16) | (c[1] << 8) | c[2];
54 | return val;
55 | }
56 |
57 | unsigned int
58 | AMF_DecodeInt32(const char *data)
59 | {
60 | unsigned char *c = (unsigned char *)data;
61 | unsigned int val;
62 | val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
63 | return val;
64 | }
65 |
66 | void
67 | AMF_DecodeString(const char *data, AVal *bv)
68 | {
69 | bv->av_len = AMF_DecodeInt16(data);
70 | bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
71 | }
72 |
73 | void
74 | AMF_DecodeLongString(const char *data, AVal *bv)
75 | {
76 | bv->av_len = AMF_DecodeInt32(data);
77 | bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
78 | }
79 |
80 | double
81 | AMF_DecodeNumber(const char *data)
82 | {
83 | double dVal;
84 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER
85 | #if __BYTE_ORDER == __BIG_ENDIAN
86 | memcpy(&dVal, data, 8);
87 | #elif __BYTE_ORDER == __LITTLE_ENDIAN
88 | unsigned char *ci, *co;
89 | ci = (unsigned char *)data;
90 | co = (unsigned char *)&dVal;
91 | co[0] = ci[7];
92 | co[1] = ci[6];
93 | co[2] = ci[5];
94 | co[3] = ci[4];
95 | co[4] = ci[3];
96 | co[5] = ci[2];
97 | co[6] = ci[1];
98 | co[7] = ci[0];
99 | #endif
100 | #else
101 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
102 | unsigned char *ci, *co;
103 | ci = (unsigned char *)data;
104 | co = (unsigned char *)&dVal;
105 | co[0] = ci[3];
106 | co[1] = ci[2];
107 | co[2] = ci[1];
108 | co[3] = ci[0];
109 | co[4] = ci[7];
110 | co[5] = ci[6];
111 | co[6] = ci[5];
112 | co[7] = ci[4];
113 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
114 | unsigned char *ci, *co;
115 | ci = (unsigned char *)data;
116 | co = (unsigned char *)&dVal;
117 | co[0] = ci[4];
118 | co[1] = ci[5];
119 | co[2] = ci[6];
120 | co[3] = ci[7];
121 | co[4] = ci[0];
122 | co[5] = ci[1];
123 | co[6] = ci[2];
124 | co[7] = ci[3];
125 | #endif
126 | #endif
127 | return dVal;
128 | }
129 |
130 | int
131 | AMF_DecodeBoolean(const char *data)
132 | {
133 | return *data != 0;
134 | }
135 |
136 | char *
137 | AMF_EncodeInt16(char *output, char *outend, short nVal)
138 | {
139 | if (output+2 > outend)
140 | return NULL;
141 |
142 | output[1] = nVal & 0xff;
143 | output[0] = nVal >> 8;
144 | return output+2;
145 | }
146 |
147 | char *
148 | AMF_EncodeInt24(char *output, char *outend, int nVal)
149 | {
150 | if (output+3 > outend)
151 | return NULL;
152 |
153 | output[2] = nVal & 0xff;
154 | output[1] = nVal >> 8;
155 | output[0] = nVal >> 16;
156 | return output+3;
157 | }
158 |
159 | char *
160 | AMF_EncodeInt32(char *output, char *outend, int nVal)
161 | {
162 | if (output+4 > outend)
163 | return NULL;
164 |
165 | output[3] = nVal & 0xff;
166 | output[2] = nVal >> 8;
167 | output[1] = nVal >> 16;
168 | output[0] = nVal >> 24;
169 | return output+4;
170 | }
171 |
172 | char *
173 | AMF_EncodeString(char *output, char *outend, const AVal *bv)
174 | {
175 | if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) ||
176 | output + 1 + 4 + bv->av_len > outend)
177 | return NULL;
178 |
179 | if (bv->av_len < 65536)
180 | {
181 | *output++ = AMF_STRING;
182 |
183 | output = AMF_EncodeInt16(output, outend, bv->av_len);
184 | }
185 | else
186 | {
187 | *output++ = AMF_LONG_STRING;
188 |
189 | output = AMF_EncodeInt32(output, outend, bv->av_len);
190 | }
191 | memcpy(output, bv->av_val, bv->av_len);
192 | output += bv->av_len;
193 |
194 | return output;
195 | }
196 |
197 | char *
198 | AMF_EncodeNumber(char *output, char *outend, double dVal)
199 | {
200 | if (output+1+8 > outend)
201 | return NULL;
202 |
203 | *output++ = AMF_NUMBER; /* type: Number */
204 |
205 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER
206 | #if __BYTE_ORDER == __BIG_ENDIAN
207 | memcpy(output, &dVal, 8);
208 | #elif __BYTE_ORDER == __LITTLE_ENDIAN
209 | {
210 | unsigned char *ci, *co;
211 | ci = (unsigned char *)&dVal;
212 | co = (unsigned char *)output;
213 | co[0] = ci[7];
214 | co[1] = ci[6];
215 | co[2] = ci[5];
216 | co[3] = ci[4];
217 | co[4] = ci[3];
218 | co[5] = ci[2];
219 | co[6] = ci[1];
220 | co[7] = ci[0];
221 | }
222 | #endif
223 | #else
224 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
225 | {
226 | unsigned char *ci, *co;
227 | ci = (unsigned char *)&dVal;
228 | co = (unsigned char *)output;
229 | co[0] = ci[3];
230 | co[1] = ci[2];
231 | co[2] = ci[1];
232 | co[3] = ci[0];
233 | co[4] = ci[7];
234 | co[5] = ci[6];
235 | co[6] = ci[5];
236 | co[7] = ci[4];
237 | }
238 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
239 | {
240 | unsigned char *ci, *co;
241 | ci = (unsigned char *)&dVal;
242 | co = (unsigned char *)output;
243 | co[0] = ci[4];
244 | co[1] = ci[5];
245 | co[2] = ci[6];
246 | co[3] = ci[7];
247 | co[4] = ci[0];
248 | co[5] = ci[1];
249 | co[6] = ci[2];
250 | co[7] = ci[3];
251 | }
252 | #endif
253 | #endif
254 |
255 | return output+8;
256 | }
257 |
258 | char *
259 | AMF_EncodeBoolean(char *output, char *outend, int bVal)
260 | {
261 | if (output+2 > outend)
262 | return NULL;
263 |
264 | *output++ = AMF_BOOLEAN;
265 |
266 | *output++ = bVal ? 0x01 : 0x00;
267 |
268 | return output;
269 | }
270 |
271 | char *
272 | AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
273 | {
274 | if (output+2+strName->av_len > outend)
275 | return NULL;
276 | output = AMF_EncodeInt16(output, outend, strName->av_len);
277 |
278 | memcpy(output, strName->av_val, strName->av_len);
279 | output += strName->av_len;
280 |
281 | return AMF_EncodeString(output, outend, strValue);
282 | }
283 |
284 | char *
285 | AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
286 | {
287 | if (output+2+strName->av_len > outend)
288 | return NULL;
289 | output = AMF_EncodeInt16(output, outend, strName->av_len);
290 |
291 | memcpy(output, strName->av_val, strName->av_len);
292 | output += strName->av_len;
293 |
294 | return AMF_EncodeNumber(output, outend, dVal);
295 | }
296 |
297 | char *
298 | AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
299 | {
300 | if (output+2+strName->av_len > outend)
301 | return NULL;
302 | output = AMF_EncodeInt16(output, outend, strName->av_len);
303 |
304 | memcpy(output, strName->av_val, strName->av_len);
305 | output += strName->av_len;
306 |
307 | return AMF_EncodeBoolean(output, outend, bVal);
308 | }
309 |
310 | void
311 | AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
312 | {
313 | *name = prop->p_name;
314 | }
315 |
316 | void
317 | AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
318 | {
319 | prop->p_name = *name;
320 | }
321 |
322 | AMFDataType
323 | AMFProp_GetType(AMFObjectProperty *prop)
324 | {
325 | return prop->p_type;
326 | }
327 |
328 | double
329 | AMFProp_GetNumber(AMFObjectProperty *prop)
330 | {
331 | return prop->p_vu.p_number;
332 | }
333 |
334 | int
335 | AMFProp_GetBoolean(AMFObjectProperty *prop)
336 | {
337 | return prop->p_vu.p_number != 0;
338 | }
339 |
340 | void
341 | AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
342 | {
343 | *str = prop->p_vu.p_aval;
344 | }
345 |
346 | void
347 | AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
348 | {
349 | *obj = prop->p_vu.p_object;
350 | }
351 |
352 | int
353 | AMFProp_IsValid(AMFObjectProperty *prop)
354 | {
355 | return prop->p_type != AMF_INVALID;
356 | }
357 |
358 | char *
359 | AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
360 | {
361 | if (prop->p_type == AMF_INVALID)
362 | return NULL;
363 |
364 | if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
365 | return NULL;
366 |
367 | if (prop->p_type != AMF_NULL && prop->p_name.av_len)
368 | {
369 | *pBuffer++ = prop->p_name.av_len >> 8;
370 | *pBuffer++ = prop->p_name.av_len & 0xff;
371 | memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len);
372 | pBuffer += prop->p_name.av_len;
373 | }
374 |
375 | switch (prop->p_type)
376 | {
377 | case AMF_NUMBER:
378 | pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
379 | break;
380 |
381 | case AMF_BOOLEAN:
382 | pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
383 | break;
384 |
385 | case AMF_STRING:
386 | pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
387 | break;
388 |
389 | case AMF_NULL:
390 | if (pBuffer+1 >= pBufEnd)
391 | return NULL;
392 | *pBuffer++ = AMF_NULL;
393 | break;
394 |
395 | case AMF_OBJECT:
396 | pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
397 | break;
398 |
399 | default:
400 | RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
401 | pBuffer = NULL;
402 | };
403 |
404 | return pBuffer;
405 | }
406 |
407 | #define AMF3_INTEGER_MAX 268435455
408 | #define AMF3_INTEGER_MIN -268435456
409 |
410 | int
411 | AMF3ReadInteger(const char *data, int32_t *valp)
412 | {
413 | int i = 0;
414 | int32_t val = 0;
415 |
416 | while (i <= 2)
417 | { /* handle first 3 bytes */
418 | if (data[i] & 0x80)
419 | { /* byte used */
420 | val <<= 7; /* shift up */
421 | val |= (data[i] & 0x7f); /* add bits */
422 | i++;
423 | }
424 | else
425 | {
426 | break;
427 | }
428 | }
429 |
430 | if (i > 2)
431 | { /* use 4th byte, all 8bits */
432 | val <<= 8;
433 | val |= data[3];
434 |
435 | /* range check */
436 | if (val > AMF3_INTEGER_MAX)
437 | val -= (1 << 29);
438 | }
439 | else
440 | { /* use 7bits of last unparsed byte (0xxxxxxx) */
441 | val <<= 7;
442 | val |= data[i];
443 | }
444 |
445 | *valp = val;
446 |
447 | return i > 2 ? 4 : i + 1;
448 | }
449 |
450 | int
451 | AMF3ReadString(const char *data, AVal *str)
452 | {
453 | int32_t ref = 0;
454 | int len;
455 | assert(str != 0);
456 |
457 | len = AMF3ReadInteger(data, &ref);
458 | data += len;
459 |
460 | if ((ref & 0x1) == 0)
461 | { /* reference: 0xxx */
462 | uint32_t refIndex = (ref >> 1);
463 | RTMP_Log(RTMP_LOGDEBUG,
464 | "%s, string reference, index: %d, not supported, ignoring!",
465 | __FUNCTION__, refIndex);
466 | return len;
467 | }
468 | else
469 | {
470 | uint32_t nSize = (ref >> 1);
471 |
472 | str->av_val = (char *)data;
473 | str->av_len = nSize;
474 |
475 | return len + nSize;
476 | }
477 | return len;
478 | }
479 |
480 | int
481 | AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
482 | int bDecodeName)
483 | {
484 | int nOriginalSize = nSize;
485 | AMF3DataType type;
486 |
487 | prop->p_name.av_len = 0;
488 | prop->p_name.av_val = NULL;
489 |
490 | if (nSize == 0 || !pBuffer)
491 | {
492 | RTMP_Log(RTMP_LOGDEBUG, "empty buffer/no buffer pointer!");
493 | return -1;
494 | }
495 |
496 | /* decode name */
497 | if (bDecodeName)
498 | {
499 | AVal name;
500 | int nRes = AMF3ReadString(pBuffer, &name);
501 |
502 | if (name.av_len <= 0)
503 | return nRes;
504 |
505 | prop->p_name = name;
506 | pBuffer += nRes;
507 | nSize -= nRes;
508 | }
509 |
510 | /* decode */
511 | type = *pBuffer++;
512 | nSize--;
513 |
514 | switch (type)
515 | {
516 | case AMF3_UNDEFINED:
517 | case AMF3_NULL:
518 | prop->p_type = AMF_NULL;
519 | break;
520 | case AMF3_FALSE:
521 | prop->p_type = AMF_BOOLEAN;
522 | prop->p_vu.p_number = 0.0;
523 | break;
524 | case AMF3_TRUE:
525 | prop->p_type = AMF_BOOLEAN;
526 | prop->p_vu.p_number = 1.0;
527 | break;
528 | case AMF3_INTEGER:
529 | {
530 | int32_t res = 0;
531 | int len = AMF3ReadInteger(pBuffer, &res);
532 | prop->p_vu.p_number = (double)res;
533 | prop->p_type = AMF_NUMBER;
534 | nSize -= len;
535 | break;
536 | }
537 | case AMF3_DOUBLE:
538 | if (nSize < 8)
539 | return -1;
540 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
541 | prop->p_type = AMF_NUMBER;
542 | nSize -= 8;
543 | break;
544 | case AMF3_STRING:
545 | case AMF3_XML_DOC:
546 | case AMF3_XML:
547 | {
548 | int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval);
549 | prop->p_type = AMF_STRING;
550 | nSize -= len;
551 | break;
552 | }
553 | case AMF3_DATE:
554 | {
555 | int32_t res = 0;
556 | int len = AMF3ReadInteger(pBuffer, &res);
557 |
558 | nSize -= len;
559 | pBuffer += len;
560 |
561 | if ((res & 0x1) == 0)
562 | { /* reference */
563 | uint32_t nIndex = (res >> 1);
564 | RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex);
565 | }
566 | else
567 | {
568 | if (nSize < 8)
569 | return -1;
570 |
571 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
572 | nSize -= 8;
573 | prop->p_type = AMF_NUMBER;
574 | }
575 | break;
576 | }
577 | case AMF3_OBJECT:
578 | {
579 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
580 | if (nRes == -1)
581 | return -1;
582 | nSize -= nRes;
583 | prop->p_type = AMF_OBJECT;
584 | break;
585 | }
586 | case AMF3_ARRAY:
587 | case AMF3_BYTE_ARRAY:
588 | default:
589 | RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
590 | __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
591 | return -1;
592 | }
593 |
594 | return nOriginalSize - nSize;
595 | }
596 |
597 | int
598 | AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
599 | int bDecodeName)
600 | {
601 | int nOriginalSize = nSize;
602 | int nRes;
603 |
604 | prop->p_name.av_len = 0;
605 | prop->p_name.av_val = NULL;
606 |
607 | if (nSize == 0 || !pBuffer)
608 | {
609 | RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
610 | return -1;
611 | }
612 |
613 | if (bDecodeName && nSize < 4)
614 | { /* at least name (length + at least 1 byte) and 1 byte of data */
615 | RTMP_Log(RTMP_LOGDEBUG,
616 | "%s: Not enough data for decoding with name, less than 4 bytes!",
617 | __FUNCTION__);
618 | return -1;
619 | }
620 |
621 | if (bDecodeName)
622 | {
623 | unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
624 | if (nNameSize > nSize - 2)
625 | {
626 | RTMP_Log(RTMP_LOGDEBUG,
627 | "%s: Name size out of range: namesize (%d) > len (%d) - 2",
628 | __FUNCTION__, nNameSize, nSize);
629 | return -1;
630 | }
631 |
632 | AMF_DecodeString(pBuffer, &prop->p_name);
633 | nSize -= 2 + nNameSize;
634 | pBuffer += 2 + nNameSize;
635 | }
636 |
637 | if (nSize == 0)
638 | {
639 | return -1;
640 | }
641 |
642 | nSize--;
643 |
644 | prop->p_type = *pBuffer++;
645 | switch (prop->p_type)
646 | {
647 | case AMF_NUMBER:
648 | if (nSize < 8)
649 | return -1;
650 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
651 | nSize -= 8;
652 | break;
653 | case AMF_BOOLEAN:
654 | if (nSize < 1)
655 | return -1;
656 | prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
657 | nSize--;
658 | break;
659 | case AMF_STRING:
660 | {
661 | unsigned short nStringSize = AMF_DecodeInt16(pBuffer);
662 |
663 | if (nSize < (long)nStringSize + 2)
664 | return -1;
665 | AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
666 | nSize -= (2 + nStringSize);
667 | break;
668 | }
669 | case AMF_OBJECT:
670 | {
671 | int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
672 | if (nRes == -1)
673 | return -1;
674 | nSize -= nRes;
675 | break;
676 | }
677 | case AMF_MOVIECLIP:
678 | {
679 | RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
680 | return -1;
681 | break;
682 | }
683 | case AMF_NULL:
684 | case AMF_UNDEFINED:
685 | case AMF_UNSUPPORTED:
686 | prop->p_type = AMF_NULL;
687 | break;
688 | case AMF_REFERENCE:
689 | {
690 | RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
691 | return -1;
692 | break;
693 | }
694 | case AMF_ECMA_ARRAY:
695 | {
696 | nSize -= 4;
697 |
698 | /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
699 | nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE);
700 | if (nRes == -1)
701 | return -1;
702 | nSize -= nRes;
703 | prop->p_type = AMF_OBJECT;
704 | break;
705 | }
706 | case AMF_OBJECT_END:
707 | {
708 | return -1;
709 | break;
710 | }
711 | case AMF_STRICT_ARRAY:
712 | {
713 | unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
714 | nSize -= 4;
715 |
716 | nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
717 | nArrayLen, FALSE);
718 | if (nRes == -1)
719 | return -1;
720 | nSize -= nRes;
721 | prop->p_type = AMF_OBJECT;
722 | break;
723 | }
724 | case AMF_DATE:
725 | {
726 | RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
727 |
728 | if (nSize < 10)
729 | return -1;
730 |
731 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
732 | prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
733 |
734 | nSize -= 10;
735 | break;
736 | }
737 | case AMF_LONG_STRING:
738 | case AMF_XML_DOC:
739 | {
740 | unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
741 | if (nSize < (long)nStringSize + 4)
742 | return -1;
743 | AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
744 | nSize -= (4 + nStringSize);
745 | if (prop->p_type == AMF_LONG_STRING)
746 | prop->p_type = AMF_STRING;
747 | break;
748 | }
749 | case AMF_RECORDSET:
750 | {
751 | RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
752 | return -1;
753 | break;
754 | }
755 | case AMF_TYPED_OBJECT:
756 | {
757 | RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
758 | return -1;
759 | break;
760 | }
761 | case AMF_AVMPLUS:
762 | {
763 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
764 | if (nRes == -1)
765 | return -1;
766 | nSize -= nRes;
767 | prop->p_type = AMF_OBJECT;
768 | break;
769 | }
770 | default:
771 | RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
772 | prop->p_type, pBuffer - 1);
773 | return -1;
774 | }
775 |
776 | return nOriginalSize - nSize;
777 | }
778 |
779 | void
780 | AMFProp_Dump(AMFObjectProperty *prop)
781 | {
782 | char strRes[256];
783 | char str[256];
784 | AVal name;
785 |
786 | if (prop->p_type == AMF_INVALID)
787 | {
788 | RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID");
789 | return;
790 | }
791 |
792 | if (prop->p_type == AMF_NULL)
793 | {
794 | RTMP_Log(RTMP_LOGDEBUG, "Property: NULL");
795 | return;
796 | }
797 |
798 | if (prop->p_name.av_len)
799 | {
800 | name = prop->p_name;
801 | }
802 | else
803 | {
804 | name.av_val = "no-name.";
805 | name.av_len = sizeof("no-name.") - 1;
806 | }
807 | if (name.av_len > 18)
808 | name.av_len = 18;
809 |
810 | snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val);
811 |
812 | if (prop->p_type == AMF_OBJECT)
813 | {
814 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes);
815 | AMF_Dump(&prop->p_vu.p_object);
816 | return;
817 | }
818 |
819 | switch (prop->p_type)
820 | {
821 | case AMF_NUMBER:
822 | snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
823 | break;
824 | case AMF_BOOLEAN:
825 | snprintf(str, 255, "BOOLEAN:\t%s",
826 | prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
827 | break;
828 | case AMF_STRING:
829 | snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
830 | prop->p_vu.p_aval.av_val);
831 | break;
832 | case AMF_DATE:
833 | snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
834 | prop->p_vu.p_number, prop->p_UTCoffset);
835 | break;
836 | default:
837 | snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type);
838 | }
839 |
840 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str);
841 | }
842 |
843 | void
844 | AMFProp_Reset(AMFObjectProperty *prop)
845 | {
846 | if (prop->p_type == AMF_OBJECT)
847 | AMF_Reset(&prop->p_vu.p_object);
848 | else
849 | {
850 | prop->p_vu.p_aval.av_len = 0;
851 | prop->p_vu.p_aval.av_val = NULL;
852 | }
853 | prop->p_type = AMF_INVALID;
854 | }
855 |
856 | /* AMFObject */
857 |
858 | char *
859 | AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
860 | {
861 | int i;
862 |
863 | if (pBuffer+4 >= pBufEnd)
864 | return NULL;
865 |
866 | *pBuffer++ = AMF_OBJECT;
867 |
868 | for (i = 0; i < obj->o_num; i++)
869 | {
870 | char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
871 | if (res == NULL)
872 | {
873 | RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
874 | i);
875 | break;
876 | }
877 | else
878 | {
879 | pBuffer = res;
880 | }
881 | }
882 |
883 | if (pBuffer + 3 >= pBufEnd)
884 | return NULL; /* no room for the end marker */
885 |
886 | pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
887 |
888 | return pBuffer;
889 | }
890 |
891 | int
892 | AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
893 | int nArrayLen, int bDecodeName)
894 | {
895 | int nOriginalSize = nSize;
896 | int bError = FALSE;
897 |
898 | obj->o_num = 0;
899 | obj->o_props = NULL;
900 | while (nArrayLen > 0)
901 | {
902 | AMFObjectProperty prop;
903 | int nRes;
904 | nArrayLen--;
905 |
906 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
907 | if (nRes == -1)
908 | bError = TRUE;
909 | else
910 | {
911 | nSize -= nRes;
912 | pBuffer += nRes;
913 | AMF_AddProp(obj, &prop);
914 | }
915 | }
916 | if (bError)
917 | return -1;
918 |
919 | return nOriginalSize - nSize;
920 | }
921 |
922 | int
923 | AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
924 | {
925 | int nOriginalSize = nSize;
926 | int32_t ref;
927 | int len;
928 |
929 | obj->o_num = 0;
930 | obj->o_props = NULL;
931 | if (bAMFData)
932 | {
933 | if (*pBuffer != AMF3_OBJECT)
934 | RTMP_Log(RTMP_LOGERROR,
935 | "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
936 | pBuffer++;
937 | nSize--;
938 | }
939 |
940 | ref = 0;
941 | len = AMF3ReadInteger(pBuffer, &ref);
942 | pBuffer += len;
943 | nSize -= len;
944 |
945 | if ((ref & 1) == 0)
946 | { /* object reference, 0xxx */
947 | uint32_t objectIndex = (ref >> 1);
948 |
949 | RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex);
950 | }
951 | else /* object instance */
952 | {
953 | int32_t classRef = (ref >> 1);
954 |
955 | AMF3ClassDef cd = { {0, 0}
956 | };
957 | AMFObjectProperty prop;
958 |
959 | if ((classRef & 0x1) == 0)
960 | { /* class reference */
961 | uint32_t classIndex = (classRef >> 1);
962 | RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex);
963 | }
964 | else
965 | {
966 | int32_t classExtRef = (classRef >> 1);
967 | int i;
968 |
969 | cd.cd_externalizable = (classExtRef & 0x1) == 1;
970 | cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
971 |
972 | cd.cd_num = classExtRef >> 2;
973 |
974 | /* class name */
975 |
976 | len = AMF3ReadString(pBuffer, &cd.cd_name);
977 | nSize -= len;
978 | pBuffer += len;
979 |
980 | /*std::string str = className; */
981 |
982 | RTMP_Log(RTMP_LOGDEBUG,
983 | "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
984 | cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
985 | cd.cd_num);
986 |
987 | for (i = 0; i < cd.cd_num; i++)
988 | {
989 | AVal memberName;
990 | len = AMF3ReadString(pBuffer, &memberName);
991 | RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
992 | AMF3CD_AddProp(&cd, &memberName);
993 | nSize -= len;
994 | pBuffer += len;
995 | }
996 | }
997 |
998 | /* add as referencable object */
999 |
1000 | if (cd.cd_externalizable)
1001 | {
1002 | int nRes;
1003 | AVal name = AVC("DEFAULT_ATTRIBUTE");
1004 |
1005 | RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check");
1006 |
1007 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1008 | if (nRes == -1)
1009 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1010 | __FUNCTION__);
1011 | else
1012 | {
1013 | nSize -= nRes;
1014 | pBuffer += nRes;
1015 | }
1016 |
1017 | AMFProp_SetName(&prop, &name);
1018 | AMF_AddProp(obj, &prop);
1019 | }
1020 | else
1021 | {
1022 | int nRes, i;
1023 | for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
1024 | {
1025 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1026 | if (nRes == -1)
1027 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1028 | __FUNCTION__);
1029 |
1030 | AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
1031 | AMF_AddProp(obj, &prop);
1032 |
1033 | pBuffer += nRes;
1034 | nSize -= nRes;
1035 | }
1036 | if (cd.cd_dynamic)
1037 | {
1038 | int len = 0;
1039 |
1040 | do
1041 | {
1042 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
1043 | AMF_AddProp(obj, &prop);
1044 |
1045 | pBuffer += nRes;
1046 | nSize -= nRes;
1047 |
1048 | len = prop.p_name.av_len;
1049 | }
1050 | while (len > 0);
1051 | }
1052 | }
1053 | RTMP_Log(RTMP_LOGDEBUG, "class object!");
1054 | }
1055 | return nOriginalSize - nSize;
1056 | }
1057 |
1058 | int
1059 | AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName)
1060 | {
1061 | int nOriginalSize = nSize;
1062 | int bError = FALSE; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1063 |
1064 | obj->o_num = 0;
1065 | obj->o_props = NULL;
1066 | while (nSize > 0)
1067 | {
1068 | AMFObjectProperty prop;
1069 | int nRes;
1070 |
1071 | if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
1072 | {
1073 | nSize -= 3;
1074 | bError = FALSE;
1075 | break;
1076 | }
1077 |
1078 | if (bError)
1079 | {
1080 | RTMP_Log(RTMP_LOGERROR,
1081 | "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1082 | nSize--;
1083 | pBuffer++;
1084 | continue;
1085 | }
1086 |
1087 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1088 | if (nRes == -1)
1089 | bError = TRUE;
1090 | else
1091 | {
1092 | nSize -= nRes;
1093 | pBuffer += nRes;
1094 | AMF_AddProp(obj, &prop);
1095 | }
1096 | }
1097 |
1098 | if (bError)
1099 | return -1;
1100 |
1101 | return nOriginalSize - nSize;
1102 | }
1103 |
1104 | void
1105 | AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
1106 | {
1107 | if (!(obj->o_num & 0x0f))
1108 | obj->o_props =
1109 | realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
1110 | memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty));
1111 | }
1112 |
1113 | int
1114 | AMF_CountProp(AMFObject *obj)
1115 | {
1116 | return obj->o_num;
1117 | }
1118 |
1119 | AMFObjectProperty *
1120 | AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
1121 | {
1122 | if (nIndex >= 0)
1123 | {
1124 | if (nIndex < obj->o_num)
1125 | return &obj->o_props[nIndex];
1126 | }
1127 | else
1128 | {
1129 | int n;
1130 | for (n = 0; n < obj->o_num; n++)
1131 | {
1132 | if (AVMATCH(&obj->o_props[n].p_name, name))
1133 | return &obj->o_props[n];
1134 | }
1135 | }
1136 |
1137 | return (AMFObjectProperty *)&AMFProp_Invalid;
1138 | }
1139 |
1140 | void
1141 | AMF_Dump(AMFObject *obj)
1142 | {
1143 | int n;
1144 | RTMP_Log(RTMP_LOGDEBUG, "(object begin)");
1145 | for (n = 0; n < obj->o_num; n++)
1146 | {
1147 | AMFProp_Dump(&obj->o_props[n]);
1148 | }
1149 | RTMP_Log(RTMP_LOGDEBUG, "(object end)");
1150 | }
1151 |
1152 | void
1153 | AMF_Reset(AMFObject *obj)
1154 | {
1155 | int n;
1156 | for (n = 0; n < obj->o_num; n++)
1157 | {
1158 | AMFProp_Reset(&obj->o_props[n]);
1159 | }
1160 | free(obj->o_props);
1161 | obj->o_props = NULL;
1162 | obj->o_num = 0;
1163 | }
1164 |
1165 |
1166 | /* AMF3ClassDefinition */
1167 |
1168 | void
1169 | AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop)
1170 | {
1171 | if (!(cd->cd_num & 0x0f))
1172 | cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
1173 | cd->cd_props[cd->cd_num++] = *prop;
1174 | }
1175 |
1176 | AVal *
1177 | AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex)
1178 | {
1179 | if (nIndex >= cd->cd_num)
1180 | return (AVal *)&AV_empty;
1181 | return &cd->cd_props[nIndex];
1182 | }
1183 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/amf.h:
--------------------------------------------------------------------------------
1 | #ifndef __AMF_H__
2 | #define __AMF_H__
3 | /*
4 | * Copyright (C) 2005-2008 Team XBMC
5 | * http://www.xbmc.org
6 | * Copyright (C) 2008-2009 Andrej Stepanchuk
7 | * Copyright (C) 2009-2010 Howard Chu
8 | *
9 | * This file is part of librtmp.
10 | *
11 | * librtmp is free software; you can redistribute it and/or modify
12 | * it under the terms of the GNU Lesser General Public License as
13 | * published by the Free Software Foundation; either version 2.1,
14 | * or (at your option) any later version.
15 | *
16 | * librtmp is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Lesser General Public License
22 | * along with librtmp see the file COPYING. If not, write to
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 | * Boston, MA 02110-1301, USA.
25 | * http://www.gnu.org/copyleft/lgpl.html
26 | */
27 |
28 | #include
29 |
30 | #ifndef TRUE
31 | #define TRUE 1
32 | #define FALSE 0
33 | #endif
34 |
35 | #ifdef __cplusplus
36 | extern "C"
37 | {
38 | #endif
39 |
40 | typedef enum
41 | { AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT,
42 | AMF_MOVIECLIP, /* reserved, not used */
43 | AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END,
44 | AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED,
45 | AMF_RECORDSET, /* reserved, not used */
46 | AMF_XML_DOC, AMF_TYPED_OBJECT,
47 | AMF_AVMPLUS, /* switch to AMF3 */
48 | AMF_INVALID = 0xff
49 | } AMFDataType;
50 |
51 | typedef enum
52 | { AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE,
53 | AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE,
54 | AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY
55 | } AMF3DataType;
56 |
57 | typedef struct AVal
58 | {
59 | char *av_val;
60 | int av_len;
61 | } AVal;
62 | #define AVC(str) {str,sizeof(str)-1}
63 | #define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
64 |
65 | struct AMFObjectProperty;
66 |
67 | typedef struct AMFObject
68 | {
69 | int o_num;
70 | struct AMFObjectProperty *o_props;
71 | } AMFObject;
72 |
73 | typedef struct AMFObjectProperty
74 | {
75 | AVal p_name;
76 | AMFDataType p_type;
77 | union
78 | {
79 | double p_number;
80 | AVal p_aval;
81 | AMFObject p_object;
82 | } p_vu;
83 | int16_t p_UTCoffset;
84 | } AMFObjectProperty;
85 |
86 | char *AMF_EncodeString(char *output, char *outend, const AVal * str);
87 | char *AMF_EncodeNumber(char *output, char *outend, double dVal);
88 | char *AMF_EncodeInt16(char *output, char *outend, short nVal);
89 | char *AMF_EncodeInt24(char *output, char *outend, int nVal);
90 | char *AMF_EncodeInt32(char *output, char *outend, int nVal);
91 | char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
92 |
93 | /* Shortcuts for AMFProp_Encode */
94 | char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
95 | char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
96 | char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
97 |
98 | unsigned short AMF_DecodeInt16(const char *data);
99 | unsigned int AMF_DecodeInt24(const char *data);
100 | unsigned int AMF_DecodeInt32(const char *data);
101 | void AMF_DecodeString(const char *data, AVal * str);
102 | void AMF_DecodeLongString(const char *data, AVal * str);
103 | int AMF_DecodeBoolean(const char *data);
104 | double AMF_DecodeNumber(const char *data);
105 |
106 | char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
107 | int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
108 | int bDecodeName);
109 | int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
110 | int nArrayLen, int bDecodeName);
111 | int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize,
112 | int bDecodeName);
113 | void AMF_Dump(AMFObject * obj);
114 | void AMF_Reset(AMFObject * obj);
115 |
116 | void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
117 | int AMF_CountProp(AMFObject * obj);
118 | AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name,
119 | int nIndex);
120 |
121 | AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
122 | void AMFProp_SetNumber(AMFObjectProperty * prop, double dval);
123 | void AMFProp_SetBoolean(AMFObjectProperty * prop, int bflag);
124 | void AMFProp_SetString(AMFObjectProperty * prop, AVal * str);
125 | void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj);
126 |
127 | void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
128 | void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
129 | double AMFProp_GetNumber(AMFObjectProperty * prop);
130 | int AMFProp_GetBoolean(AMFObjectProperty * prop);
131 | void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
132 | void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
133 |
134 | int AMFProp_IsValid(AMFObjectProperty * prop);
135 |
136 | char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
137 | int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer,
138 | int nSize, int bDecodeName);
139 | int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer,
140 | int nSize, int bDecodeName);
141 |
142 | void AMFProp_Dump(AMFObjectProperty * prop);
143 | void AMFProp_Reset(AMFObjectProperty * prop);
144 |
145 | typedef struct AMF3ClassDef
146 | {
147 | AVal cd_name;
148 | char cd_externalizable;
149 | char cd_dynamic;
150 | int cd_num;
151 | AVal *cd_props;
152 | } AMF3ClassDef;
153 |
154 | void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop);
155 | AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx);
156 |
157 | #ifdef __cplusplus
158 | }
159 | #endif
160 |
161 | #endif /* __AMF_H__ */
162 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/bytes.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2005-2008 Team XBMC
3 | * http://www.xbmc.org
4 | * Copyright (C) 2008-2009 Andrej Stepanchuk
5 | * Copyright (C) 2009-2010 Howard Chu
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | #ifndef __BYTES_H__
27 | #define __BYTES_H__
28 |
29 | #include
30 |
31 | #ifdef _WIN32
32 | /* Windows is little endian only */
33 | #define __LITTLE_ENDIAN 1234
34 | #define __BIG_ENDIAN 4321
35 | #define __BYTE_ORDER __LITTLE_ENDIAN
36 | #define __FLOAT_WORD_ORDER __BYTE_ORDER
37 |
38 | typedef unsigned char uint8_t;
39 |
40 | #else /* !_WIN32 */
41 |
42 | #include
43 |
44 | #if defined(BYTE_ORDER) && !defined(__BYTE_ORDER)
45 | #define __BYTE_ORDER BYTE_ORDER
46 | #endif
47 |
48 | #if defined(BIG_ENDIAN) && !defined(__BIG_ENDIAN)
49 | #define __BIG_ENDIAN BIG_ENDIAN
50 | #endif
51 |
52 | #if defined(LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN)
53 | #define __LITTLE_ENDIAN LITTLE_ENDIAN
54 | #endif
55 |
56 | #endif /* !_WIN32 */
57 |
58 | /* define default endianness */
59 | #ifndef __LITTLE_ENDIAN
60 | #define __LITTLE_ENDIAN 1234
61 | #endif
62 |
63 | #ifndef __BIG_ENDIAN
64 | #define __BIG_ENDIAN 4321
65 | #endif
66 |
67 | #ifndef __BYTE_ORDER
68 | #warning "Byte order not defined on your system, assuming little endian!"
69 | #define __BYTE_ORDER __LITTLE_ENDIAN
70 | #endif
71 |
72 | /* ok, we assume to have the same float word order and byte order if float word order is not defined */
73 | #ifndef __FLOAT_WORD_ORDER
74 | #warning "Float word order not defined, assuming the same as byte order!"
75 | #define __FLOAT_WORD_ORDER __BYTE_ORDER
76 | #endif
77 |
78 | #if !defined(__BYTE_ORDER) || !defined(__FLOAT_WORD_ORDER)
79 | #error "Undefined byte or float word order!"
80 | #endif
81 |
82 | #if __FLOAT_WORD_ORDER != __BIG_ENDIAN && __FLOAT_WORD_ORDER != __LITTLE_ENDIAN
83 | #error "Unknown/unsupported float word order!"
84 | #endif
85 |
86 | #if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
87 | #error "Unknown/unsupported byte order!"
88 | #endif
89 |
90 | #endif
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/dh.h:
--------------------------------------------------------------------------------
1 | /* RTMPDump - Diffie-Hellmann Key Exchange
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #ifdef USE_POLARSSL
31 | #include
32 | typedef mpi * MP_t;
33 | #define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m)
34 | #define MP_set_w(mpi, w) mpi_lset(mpi, w)
35 | #define MP_cmp(u, v) mpi_cmp_mpi(u, v)
36 | #define MP_set(u, v) mpi_copy(u, v)
37 | #define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w)
38 | #define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1)
39 | #define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL)
40 | #define MP_free(mpi) mpi_free(mpi); free(mpi)
41 | #define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0
42 | #define MP_bytes(u) mpi_size(u)
43 | #define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len)
44 | #define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len)
45 |
46 | typedef struct MDH {
47 | MP_t p;
48 | MP_t g;
49 | MP_t pub_key;
50 | MP_t priv_key;
51 | long length;
52 | dhm_context ctx;
53 | } MDH;
54 |
55 | #define MDH_new() calloc(1,sizeof(MDH))
56 | #define MDH_free(vp) {MDH *_dh = vp; dhm_free(&_dh->ctx); MP_free(_dh->p); MP_free(_dh->g); MP_free(_dh->pub_key); MP_free(_dh->priv_key); free(_dh);}
57 |
58 | static int MDH_generate_key(MDH *dh)
59 | {
60 | unsigned char out[2];
61 | MP_set(&dh->ctx.P, dh->p);
62 | MP_set(&dh->ctx.G, dh->g);
63 | dh->ctx.len = 128;
64 | dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
65 | MP_new(dh->pub_key);
66 | MP_new(dh->priv_key);
67 | MP_set(dh->pub_key, &dh->ctx.GX);
68 | MP_set(dh->priv_key, &dh->ctx.X);
69 | return 1;
70 | }
71 |
72 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
73 | {
74 | MP_set(&dh->ctx.GY, pub);
75 | dhm_calc_secret(&dh->ctx, secret, &len);
76 | return 0;
77 | }
78 |
79 | #elif defined(USE_GNUTLS)
80 | #include
81 | #include
82 | typedef mpz_ptr MP_t;
83 | #define MP_new(m) m = malloc(sizeof(*m)); mpz_init2(m, 1)
84 | #define MP_set_w(mpi, w) mpz_set_ui(mpi, w)
85 | #define MP_cmp(u, v) mpz_cmp(u, v)
86 | #define MP_set(u, v) mpz_set(u, v)
87 | #define MP_sub_w(mpi, w) mpz_sub_ui(mpi, mpi, w)
88 | #define MP_cmp_1(mpi) mpz_cmp_ui(mpi, 1)
89 | #define MP_modexp(r, y, q, p) mpz_powm(r, y, q, p)
90 | #define MP_free(mpi) mpz_clear(mpi); free(mpi)
91 | #define MP_gethex(u, hex, res) u = malloc(sizeof(*u)); mpz_init2(u, 1); res = (mpz_set_str(u, hex, 16) == 0)
92 | #define MP_bytes(u) (mpz_sizeinbase(u, 2) + 7) / 8
93 | #define MP_setbin(u,buf,len) nettle_mpz_get_str_256(len,buf,u)
94 | #define MP_getbin(u,buf,len) u = malloc(sizeof(*u)); mpz_init2(u, 1); nettle_mpz_set_str_256_u(u,len,buf)
95 |
96 | typedef struct MDH {
97 | MP_t p;
98 | MP_t g;
99 | MP_t pub_key;
100 | MP_t priv_key;
101 | long length;
102 | } MDH;
103 |
104 | #define MDH_new() calloc(1,sizeof(MDH))
105 | #define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0)
106 |
107 | extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p);
108 | extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p);
109 |
110 | #define MDH_generate_key(dh) (dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p))
111 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
112 | {
113 | MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p);
114 | if (sec)
115 | {
116 | MP_setbin(sec, secret, len);
117 | MP_free(sec);
118 | return 0;
119 | }
120 | else
121 | return -1;
122 | }
123 |
124 | #else /* USE_OPENSSL */
125 | #include
126 | #include
127 |
128 | typedef BIGNUM * MP_t;
129 | #define MP_new(m) m = BN_new()
130 | #define MP_set_w(mpi, w) BN_set_word(mpi, w)
131 | #define MP_cmp(u, v) BN_cmp(u, v)
132 | #define MP_set(u, v) BN_copy(u, v)
133 | #define MP_sub_w(mpi, w) BN_sub_word(mpi, w)
134 | #define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one())
135 | #define MP_modexp(r, y, q, p) do {BN_CTX *ctx = BN_CTX_new(); BN_mod_exp(r, y, q, p, ctx); BN_CTX_free(ctx);} while(0)
136 | #define MP_free(mpi) BN_free(mpi)
137 | #define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex)
138 | #define MP_bytes(u) BN_num_bytes(u)
139 | #define MP_setbin(u,buf,len) BN_bn2bin(u,buf)
140 | #define MP_getbin(u,buf,len) u = BN_bin2bn(buf,len,0)
141 |
142 | #define MDH DH
143 | #define MDH_new() DH_new()
144 | #define MDH_free(dh) DH_free(dh)
145 | #define MDH_generate_key(dh) DH_generate_key(dh)
146 | #define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh)
147 |
148 | #endif
149 |
150 | #include "log.h"
151 | #include "dhgroups.h"
152 |
153 | /* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */
154 | static int
155 | isValidPublicKey(MP_t y, MP_t p, MP_t q)
156 | {
157 | int ret = TRUE;
158 | MP_t bn;
159 | assert(y);
160 |
161 | MP_new(bn);
162 | assert(bn);
163 |
164 | /* y must lie in [2,p-1] */
165 | MP_set_w(bn, 1);
166 | if (MP_cmp(y, bn) < 0)
167 | {
168 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at least 2");
169 | ret = FALSE;
170 | goto failed;
171 | }
172 |
173 | /* bn = p-2 */
174 | MP_set(bn, p);
175 | MP_sub_w(bn, 1);
176 | if (MP_cmp(y, bn) > 0)
177 | {
178 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at most p-2");
179 | ret = FALSE;
180 | goto failed;
181 | }
182 |
183 | /* Verify with Sophie-Germain prime
184 | *
185 | * This is a nice test to make sure the public key position is calculated
186 | * correctly. This test will fail in about 50% of the cases if applied to
187 | * random data.
188 | */
189 | if (q)
190 | {
191 | /* y must fulfill y^q mod p = 1 */
192 | MP_modexp(bn, y, q, p);
193 |
194 | if (MP_cmp_1(bn) != 0)
195 | {
196 | RTMP_Log(RTMP_LOGWARNING, "DH public key does not fulfill y^q mod p = 1");
197 | }
198 | }
199 |
200 | failed:
201 | MP_free(bn);
202 | return ret;
203 | }
204 |
205 | static MDH *
206 | DHInit(int nKeyBits)
207 | {
208 | size_t res;
209 | MDH *dh = MDH_new();
210 |
211 | if (!dh)
212 | goto failed;
213 |
214 | MP_new(dh->g);
215 |
216 | if (!dh->g)
217 | goto failed;
218 |
219 | MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */
220 | if (!res)
221 | {
222 | goto failed;
223 | }
224 |
225 | MP_set_w(dh->g, 2); /* base 2 */
226 |
227 | dh->length = nKeyBits;
228 | return dh;
229 |
230 | failed:
231 | if (dh)
232 | MDH_free(dh);
233 |
234 | return 0;
235 | }
236 |
237 | static int
238 | DHGenerateKey(MDH *dh)
239 | {
240 | size_t res = 0;
241 | if (!dh)
242 | return 0;
243 |
244 | while (!res)
245 | {
246 | MP_t q1 = NULL;
247 |
248 | if (!MDH_generate_key(dh))
249 | return 0;
250 |
251 | MP_gethex(q1, Q1024, res);
252 | assert(res);
253 |
254 | res = isValidPublicKey(dh->pub_key, dh->p, q1);
255 | if (!res)
256 | {
257 | MP_free(dh->pub_key);
258 | MP_free(dh->priv_key);
259 | dh->pub_key = dh->priv_key = 0;
260 | }
261 |
262 | MP_free(q1);
263 | }
264 | return 1;
265 | }
266 |
267 | /* fill pubkey with the public key in BIG ENDIAN order
268 | * 00 00 00 00 00 x1 x2 x3 .....
269 | */
270 |
271 | static int
272 | DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen)
273 | {
274 | int len;
275 | if (!dh || !dh->pub_key)
276 | return 0;
277 |
278 | len = MP_bytes(dh->pub_key);
279 | if (len <= 0 || len > (int) nPubkeyLen)
280 | return 0;
281 |
282 | memset(pubkey, 0, nPubkeyLen);
283 | MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len);
284 | return 1;
285 | }
286 |
287 | #if 0 /* unused */
288 | static int
289 | DHGetPrivateKey(MDH *dh, uint8_t *privkey, size_t nPrivkeyLen)
290 | {
291 | if (!dh || !dh->priv_key)
292 | return 0;
293 |
294 | int len = MP_bytes(dh->priv_key);
295 | if (len <= 0 || len > (int) nPrivkeyLen)
296 | return 0;
297 |
298 | memset(privkey, 0, nPrivkeyLen);
299 | MP_setbin(dh->priv_key, privkey + (nPrivkeyLen - len), len);
300 | return 1;
301 | }
302 | #endif
303 |
304 | /* computes the shared secret key from the private MDH value and the
305 | * other party's public key (pubkey)
306 | */
307 | static int
308 | DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen,
309 | uint8_t *secret)
310 | {
311 | MP_t q1 = NULL, pubkeyBn = NULL;
312 | size_t len;
313 | int res;
314 |
315 | if (!dh || !secret || nPubkeyLen >= INT_MAX)
316 | return -1;
317 |
318 | MP_getbin(pubkeyBn, pubkey, nPubkeyLen);
319 | if (!pubkeyBn)
320 | return -1;
321 |
322 | MP_gethex(q1, Q1024, len);
323 | assert(len);
324 |
325 | if (isValidPublicKey(pubkeyBn, dh->p, q1))
326 | res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh);
327 | else
328 | res = -1;
329 |
330 | MP_free(q1);
331 | MP_free(pubkeyBn);
332 |
333 | return res;
334 | }
335 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/dhgroups.h:
--------------------------------------------------------------------------------
1 | /* librtmp - Diffie-Hellmann Key Exchange
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | *
4 | * This file is part of librtmp.
5 | *
6 | * librtmp is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as
8 | * published by the Free Software Foundation; either version 2.1,
9 | * or (at your option) any later version.
10 | *
11 | * librtmp 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 Lesser General Public License
17 | * along with librtmp see the file COPYING. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | * http://www.gnu.org/copyleft/lgpl.html
21 | */
22 |
23 | /* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */
24 |
25 | /* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */
26 | #define P768 \
27 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
28 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
29 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
30 | "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
31 |
32 | /* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */
33 | #define P1024 \
34 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
35 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
36 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
37 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
38 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
39 | "FFFFFFFFFFFFFFFF"
40 |
41 | /* Group morder largest prime factor: */
42 | #define Q1024 \
43 | "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
44 | "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
45 | "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
46 | "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
47 | "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
48 | "FFFFFFFFFFFFFFFF"
49 |
50 | /* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */
51 | #define P1536 \
52 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
53 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
54 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
55 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
56 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
57 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
58 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
59 | "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
60 |
61 | /* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */
62 | #define P2048 \
63 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
64 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
65 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
66 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
67 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
68 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
69 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
70 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
71 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
72 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
73 | "15728E5A8AACAA68FFFFFFFFFFFFFFFF"
74 |
75 | /* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */
76 | #define P3072 \
77 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
78 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
79 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
80 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
81 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
82 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
83 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
84 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
85 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
86 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
87 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
88 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
89 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
90 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
91 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
92 | "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
93 |
94 | /* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */
95 | #define P4096 \
96 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
97 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
98 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
99 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
100 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
101 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
102 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
103 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
104 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
105 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
106 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
107 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
108 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
109 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
110 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
111 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
112 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
113 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
114 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
115 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
116 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \
117 | "FFFFFFFFFFFFFFFF"
118 |
119 | /* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */
120 | #define P6144 \
121 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
122 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
123 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
124 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
125 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
126 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
127 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
128 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
129 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
130 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
131 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
132 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
133 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
134 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
135 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
136 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
137 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
138 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
139 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
140 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
141 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
142 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
143 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
144 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
145 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
146 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
147 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
148 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
149 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
150 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
151 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
152 | "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
153 |
154 | /* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */
155 | #define P8192 \
156 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
157 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
158 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
159 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
160 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
161 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
162 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
163 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
164 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
165 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
166 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
167 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
168 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
169 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
170 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
171 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
172 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
173 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
174 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
175 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
176 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
177 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
178 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
179 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
180 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
181 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
182 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
183 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
184 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
185 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
186 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
187 | "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \
188 | "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \
189 | "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \
190 | "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \
191 | "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \
192 | "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \
193 | "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \
194 | "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \
195 | "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \
196 | "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \
197 | "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \
198 | "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"
199 |
200 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/hashswf.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009-2010 Howard Chu
3 | *
4 | * This file is part of librtmp.
5 | *
6 | * librtmp is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as
8 | * published by the Free Software Foundation; either version 2.1,
9 | * or (at your option) any later version.
10 | *
11 | * librtmp 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 Lesser General Public License
17 | * along with librtmp see the file COPYING. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | * http://www.gnu.org/copyleft/lgpl.html
21 | */
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "rtmp_sys.h"
30 | #include "log.h"
31 | #include "http.h"
32 |
33 | #ifdef CRYPTO
34 | #ifdef USE_POLARSSL
35 | #include
36 | #ifndef SHA256_DIGEST_LENGTH
37 | #define SHA256_DIGEST_LENGTH 32
38 | #endif
39 | #define HMAC_CTX sha2_context
40 | #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
41 | #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
42 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
43 | #define HMAC_close(ctx)
44 | #elif defined(USE_GNUTLS)
45 | #include
46 | #ifndef SHA256_DIGEST_LENGTH
47 | #define SHA256_DIGEST_LENGTH 32
48 | #endif
49 | #undef HMAC_CTX
50 | #define HMAC_CTX struct hmac_sha256_ctx
51 | #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
52 | #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
53 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
54 | #define HMAC_close(ctx)
55 | #else /* USE_OPENSSL */
56 | #include
57 | #include
58 | #include
59 | #include
60 | #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
61 | #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
62 | #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
63 | #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
64 | #endif
65 |
66 | extern void RTMP_TLS_Init();
67 | extern TLS_CTX RTMP_TLS_ctx;
68 |
69 | #include
70 |
71 | #endif /* CRYPTO */
72 |
73 | #define AGENT "Mozilla/5.0"
74 |
75 | HTTPResult
76 | HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
77 | {
78 | char *host, *path;
79 | char *p1, *p2;
80 | char hbuf[256];
81 | int port = 80;
82 | #ifdef CRYPTO
83 | int ssl = 0;
84 | #endif
85 | int hlen, flen = 0;
86 | int rc, i;
87 | int len_known;
88 | HTTPResult ret = HTTPRES_OK;
89 | struct sockaddr_in sa;
90 | RTMPSockBuf sb = {0};
91 |
92 | http->status = -1;
93 |
94 | memset(&sa, 0, sizeof(struct sockaddr_in));
95 | sa.sin_family = AF_INET;
96 |
97 | /* we only handle http here */
98 | if (strncasecmp(url, "http", 4))
99 | return HTTPRES_BAD_REQUEST;
100 |
101 | if (url[4] == 's')
102 | {
103 | #ifdef CRYPTO
104 | ssl = 1;
105 | port = 443;
106 | if (!RTMP_TLS_ctx)
107 | RTMP_TLS_Init();
108 | #else
109 | return HTTPRES_BAD_REQUEST;
110 | #endif
111 | }
112 |
113 | p1 = strchr(url + 4, ':');
114 | if (!p1 || strncmp(p1, "://", 3))
115 | return HTTPRES_BAD_REQUEST;
116 |
117 | host = p1 + 3;
118 | path = strchr(host, '/');
119 | hlen = path - host;
120 | strncpy(hbuf, host, hlen);
121 | hbuf[hlen] = '\0';
122 | host = hbuf;
123 | p1 = strrchr(host, ':');
124 | if (p1)
125 | {
126 | *p1++ = '\0';
127 | port = atoi(p1);
128 | }
129 |
130 | sa.sin_addr.s_addr = inet_addr(host);
131 | if (sa.sin_addr.s_addr == INADDR_NONE)
132 | {
133 | struct hostent *hp = gethostbyname(host);
134 | if (!hp || !hp->h_addr)
135 | return HTTPRES_LOST_CONNECTION;
136 | sa.sin_addr = *(struct in_addr *)hp->h_addr;
137 | }
138 | sa.sin_port = htons(port);
139 | sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
140 | if (sb.sb_socket == -1)
141 | return HTTPRES_LOST_CONNECTION;
142 | i =
143 | sprintf(sb.sb_buf,
144 | "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
145 | path, AGENT, host, (int)(path - url + 1), url);
146 | if (http->date[0])
147 | i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
148 | i += sprintf(sb.sb_buf + i, "\r\n");
149 |
150 | if (connect
151 | (sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
152 | {
153 | ret = HTTPRES_LOST_CONNECTION;
154 | goto leave;
155 | }
156 | #ifdef CRYPTO
157 | if (ssl)
158 | {
159 | #ifdef NO_SSL
160 | RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
161 | ret = HTTPRES_BAD_REQUEST;
162 | goto leave;
163 | #else
164 | TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
165 | TLS_setfd(sb.sb_ssl, sb.sb_socket);
166 | if (TLS_connect(sb.sb_ssl) < 0)
167 | {
168 | RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
169 | ret = HTTPRES_LOST_CONNECTION;
170 | goto leave;
171 | }
172 | #endif
173 | }
174 | #endif
175 | RTMPSockBuf_Send(&sb, sb.sb_buf, i);
176 |
177 | /* set timeout */
178 | #define HTTP_TIMEOUT 5
179 | {
180 | SET_RCVTIMEO(tv, HTTP_TIMEOUT);
181 | if (setsockopt
182 | (sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
183 | {
184 | RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
185 | __FUNCTION__, HTTP_TIMEOUT);
186 | }
187 | }
188 |
189 | sb.sb_size = 0;
190 | sb.sb_timedout = FALSE;
191 | if (RTMPSockBuf_Fill(&sb) < 1)
192 | {
193 | ret = HTTPRES_LOST_CONNECTION;
194 | goto leave;
195 | }
196 | if (strncmp(sb.sb_buf, "HTTP/1", 6))
197 | {
198 | ret = HTTPRES_BAD_REQUEST;
199 | goto leave;
200 | }
201 |
202 | p1 = strchr(sb.sb_buf, ' ');
203 | rc = atoi(p1 + 1);
204 | http->status = rc;
205 |
206 | if (rc >= 300)
207 | {
208 | if (rc == 304)
209 | {
210 | ret = HTTPRES_OK_NOT_MODIFIED;
211 | goto leave;
212 | }
213 | else if (rc == 404)
214 | ret = HTTPRES_NOT_FOUND;
215 | else if (rc >= 500)
216 | ret = HTTPRES_SERVER_ERROR;
217 | else if (rc >= 400)
218 | ret = HTTPRES_BAD_REQUEST;
219 | else
220 | ret = HTTPRES_REDIRECTED;
221 | }
222 |
223 | p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
224 | if (!p1)
225 | {
226 | ret = HTTPRES_BAD_REQUEST;
227 | goto leave;
228 | }
229 | sb.sb_start = p1 + 1;
230 | sb.sb_size -= sb.sb_start - sb.sb_buf;
231 |
232 | while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size)))
233 | {
234 | if (*sb.sb_start == '\r')
235 | {
236 | sb.sb_start += 2;
237 | sb.sb_size -= 2;
238 | break;
239 | }
240 | else
241 | if (!strncasecmp
242 | (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
243 | {
244 | flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
245 | }
246 | else
247 | if (!strncasecmp
248 | (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
249 | {
250 | *p2 = '\0';
251 | strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
252 | }
253 | p2 += 2;
254 | sb.sb_size -= p2 - sb.sb_start;
255 | sb.sb_start = p2;
256 | if (sb.sb_size < 1)
257 | {
258 | if (RTMPSockBuf_Fill(&sb) < 1)
259 | {
260 | ret = HTTPRES_LOST_CONNECTION;
261 | goto leave;
262 | }
263 | }
264 | }
265 |
266 | len_known = flen > 0;
267 | while ((!len_known || flen > 0) &&
268 | (sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0))
269 | {
270 | cb(sb.sb_start, 1, sb.sb_size, http->data);
271 | if (len_known)
272 | flen -= sb.sb_size;
273 | http->size += sb.sb_size;
274 | sb.sb_size = 0;
275 | }
276 |
277 | if (flen > 0)
278 | ret = HTTPRES_LOST_CONNECTION;
279 |
280 | leave:
281 | RTMPSockBuf_Close(&sb);
282 | return ret;
283 | }
284 |
285 | #ifdef CRYPTO
286 |
287 | #define CHUNK 16384
288 |
289 | struct info
290 | {
291 | z_stream *zs;
292 | HMAC_CTX ctx;
293 | int first;
294 | int zlib;
295 | int size;
296 | };
297 |
298 | static size_t
299 | swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
300 | {
301 | struct info *i = stream;
302 | char *p = ptr;
303 | size_t len = size * nmemb;
304 |
305 | if (i->first)
306 | {
307 | i->first = 0;
308 | /* compressed? */
309 | if (!strncmp(p, "CWS", 3))
310 | {
311 | *p = 'F';
312 | i->zlib = 1;
313 | }
314 | HMAC_crunch(i->ctx, (unsigned char *)p, 8);
315 | p += 8;
316 | len -= 8;
317 | i->size = 8;
318 | }
319 |
320 | if (i->zlib)
321 | {
322 | unsigned char out[CHUNK];
323 | i->zs->next_in = (unsigned char *)p;
324 | i->zs->avail_in = len;
325 | do
326 | {
327 | i->zs->avail_out = CHUNK;
328 | i->zs->next_out = out;
329 | inflate(i->zs, Z_NO_FLUSH);
330 | len = CHUNK - i->zs->avail_out;
331 | i->size += len;
332 | HMAC_crunch(i->ctx, out, len);
333 | }
334 | while (i->zs->avail_out == 0);
335 | }
336 | else
337 | {
338 | i->size += len;
339 | HMAC_crunch(i->ctx, (unsigned char *)p, len);
340 | }
341 | return size * nmemb;
342 | }
343 |
344 | static int tzoff;
345 | static int tzchecked;
346 |
347 | #define JAN02_1980 318340800
348 |
349 | static const char *monthtab[12] = { "Jan", "Feb", "Mar",
350 | "Apr", "May", "Jun",
351 | "Jul", "Aug", "Sep",
352 | "Oct", "Nov", "Dec"
353 | };
354 | static const char *days[] =
355 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
356 |
357 | /* Parse an HTTP datestamp into Unix time */
358 | static time_t
359 | make_unix_time(char *s)
360 | {
361 | struct tm time;
362 | int i, ysub = 1900, fmt = 0;
363 | char *month;
364 | char *n;
365 | time_t res;
366 |
367 | if (s[3] != ' ')
368 | {
369 | fmt = 1;
370 | if (s[3] != ',')
371 | ysub = 0;
372 | }
373 | for (n = s; *n; ++n)
374 | if (*n == '-' || *n == ':')
375 | *n = ' ';
376 |
377 | time.tm_mon = 0;
378 | n = strchr(s, ' ');
379 | if (fmt)
380 | {
381 | /* Day, DD-MMM-YYYY HH:MM:SS GMT */
382 | time.tm_mday = strtol(n + 1, &n, 0);
383 | month = n + 1;
384 | n = strchr(month, ' ');
385 | time.tm_year = strtol(n + 1, &n, 0);
386 | time.tm_hour = strtol(n + 1, &n, 0);
387 | time.tm_min = strtol(n + 1, &n, 0);
388 | time.tm_sec = strtol(n + 1, NULL, 0);
389 | }
390 | else
391 | {
392 | /* Unix ctime() format. Does not conform to HTTP spec. */
393 | /* Day MMM DD HH:MM:SS YYYY */
394 | month = n + 1;
395 | n = strchr(month, ' ');
396 | while (isspace(*n))
397 | n++;
398 | time.tm_mday = strtol(n, &n, 0);
399 | time.tm_hour = strtol(n + 1, &n, 0);
400 | time.tm_min = strtol(n + 1, &n, 0);
401 | time.tm_sec = strtol(n + 1, &n, 0);
402 | time.tm_year = strtol(n + 1, NULL, 0);
403 | }
404 | if (time.tm_year > 100)
405 | time.tm_year -= ysub;
406 |
407 | for (i = 0; i < 12; i++)
408 | if (!strncasecmp(month, monthtab[i], 3))
409 | {
410 | time.tm_mon = i;
411 | break;
412 | }
413 | time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
414 |
415 | /* this is normally the value of extern int timezone, but some
416 | * braindead C libraries don't provide it.
417 | */
418 | if (!tzchecked)
419 | {
420 | struct tm *tc;
421 | time_t then = JAN02_1980;
422 | tc = localtime(&then);
423 | tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
424 | tzchecked = 1;
425 | }
426 | res = mktime(&time);
427 | /* Unfortunately, mktime() assumes the input is in local time,
428 | * not GMT, so we have to correct it here.
429 | */
430 | if (res != -1)
431 | res += tzoff;
432 | return res;
433 | }
434 |
435 | /* Convert a Unix time to a network time string
436 | * Weekday, DD-MMM-YYYY HH:MM:SS GMT
437 | */
438 | static void
439 | strtime(time_t * t, char *s)
440 | {
441 | struct tm *tm;
442 |
443 | tm = gmtime((time_t *) t);
444 | sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
445 | days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
446 | tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
447 | }
448 |
449 | #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
450 |
451 | int
452 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
453 | int age)
454 | {
455 | FILE *f = NULL;
456 | char *path, date[64], cctim[64];
457 | long pos = 0;
458 | time_t ctim = -1, cnow;
459 | int i, got = 0, ret = 0;
460 | unsigned int hlen;
461 | struct info in = { 0 };
462 | struct HTTP_ctx http = { 0 };
463 | HTTPResult httpres;
464 | z_stream zs = { 0 };
465 | AVal home, hpre;
466 |
467 | date[0] = '\0';
468 | #ifdef _WIN32
469 | #ifdef XBMC4XBOX
470 | hpre.av_val = "Q:";
471 | hpre.av_len = 2;
472 | home.av_val = "\\UserData";
473 | #else
474 | hpre.av_val = getenv("HOMEDRIVE");
475 | hpre.av_len = strlen(hpre.av_val);
476 | home.av_val = getenv("HOMEPATH");
477 | #endif
478 | #define DIRSEP "\\"
479 |
480 | #else /* !_WIN32 */
481 | hpre.av_val = "";
482 | hpre.av_len = 0;
483 | home.av_val = getenv("HOME");
484 | #define DIRSEP "/"
485 | #endif
486 | if (!home.av_val)
487 | home.av_val = ".";
488 | home.av_len = strlen(home.av_val);
489 |
490 | /* SWF hash info is cached in a fixed-format file.
491 | * url:
492 | * ctim: HTTP datestamp of when we last checked it.
493 | * date: HTTP datestamp of the SWF's last modification.
494 | * size: SWF size in hex
495 | * hash: SWF hash in hex
496 | *
497 | * These fields must be present in this order. All fields
498 | * besides URL are fixed size.
499 | */
500 | path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
501 | sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
502 |
503 | f = fopen(path, "r+");
504 | while (f)
505 | {
506 | char buf[4096], *file, *p;
507 |
508 | file = strchr(url, '/');
509 | if (!file)
510 | break;
511 | file += 2;
512 | file = strchr(file, '/');
513 | if (!file)
514 | break;
515 | file++;
516 | hlen = file - url;
517 | p = strrchr(file, '/');
518 | if (p)
519 | file = p;
520 | else
521 | file--;
522 |
523 | while (fgets(buf, sizeof(buf), f))
524 | {
525 | char *r1;
526 |
527 | got = 0;
528 |
529 | if (strncmp(buf, "url: ", 5))
530 | continue;
531 | if (strncmp(buf + 5, url, hlen))
532 | continue;
533 | r1 = strrchr(buf, '/');
534 | i = strlen(r1);
535 | r1[--i] = '\0';
536 | if (strncmp(r1, file, i))
537 | continue;
538 | pos = ftell(f);
539 | while (got < 4 && fgets(buf, sizeof(buf), f))
540 | {
541 | if (!strncmp(buf, "size: ", 6))
542 | {
543 | *size = strtol(buf + 6, NULL, 16);
544 | got++;
545 | }
546 | else if (!strncmp(buf, "hash: ", 6))
547 | {
548 | unsigned char *ptr = hash, *in = (unsigned char *)buf + 6;
549 | int l = strlen((char *)in) - 1;
550 | for (i = 0; i < l; i += 2)
551 | *ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]);
552 | got++;
553 | }
554 | else if (!strncmp(buf, "date: ", 6))
555 | {
556 | buf[strlen(buf) - 1] = '\0';
557 | strncpy(date, buf + 6, sizeof(date));
558 | got++;
559 | }
560 | else if (!strncmp(buf, "ctim: ", 6))
561 | {
562 | buf[strlen(buf) - 1] = '\0';
563 | ctim = make_unix_time(buf + 6);
564 | got++;
565 | }
566 | else if (!strncmp(buf, "url: ", 5))
567 | break;
568 | }
569 | break;
570 | }
571 | break;
572 | }
573 |
574 | cnow = time(NULL);
575 | /* If we got a cache time, see if it's young enough to use directly */
576 | if (age && ctim > 0)
577 | {
578 | ctim = cnow - ctim;
579 | ctim /= 3600 * 24; /* seconds to days */
580 | if (ctim < age) /* ok, it's new enough */
581 | goto out;
582 | }
583 |
584 | in.first = 1;
585 | HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
586 | inflateInit(&zs);
587 | in.zs = &zs;
588 |
589 | http.date = date;
590 | http.data = ∈
591 |
592 | httpres = HTTP_get(&http, url, swfcrunch);
593 |
594 | inflateEnd(&zs);
595 |
596 | if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED)
597 | {
598 | ret = -1;
599 | if (httpres == HTTPRES_LOST_CONNECTION)
600 | RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
601 | __FUNCTION__, url);
602 | else if (httpres == HTTPRES_NOT_FOUND)
603 | RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
604 | else
605 | RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
606 | __FUNCTION__, url, http.status);
607 | }
608 | else
609 | {
610 | if (got && pos)
611 | fseek(f, pos, SEEK_SET);
612 | else
613 | {
614 | char *q;
615 | if (!f)
616 | f = fopen(path, "w");
617 | if (!f)
618 | {
619 | int err = errno;
620 | RTMP_Log(RTMP_LOGERROR,
621 | "%s: couldn't open %s for writing, errno %d (%s)",
622 | __FUNCTION__, path, err, strerror(err));
623 | ret = -1;
624 | goto out;
625 | }
626 | fseek(f, 0, SEEK_END);
627 | q = strchr(url, '?');
628 | if (q)
629 | i = q - url;
630 | else
631 | i = strlen(url);
632 |
633 | fprintf(f, "url: %.*s\n", i, url);
634 | }
635 | strtime(&cnow, cctim);
636 | fprintf(f, "ctim: %s\n", cctim);
637 |
638 | if (!in.first)
639 | {
640 | HMAC_finish(in.ctx, hash, hlen);
641 | *size = in.size;
642 |
643 | fprintf(f, "date: %s\n", date);
644 | fprintf(f, "size: %08x\n", in.size);
645 | fprintf(f, "hash: ");
646 | for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
647 | fprintf(f, "%02x", hash[i]);
648 | fprintf(f, "\n");
649 | }
650 | }
651 | HMAC_close(in.ctx);
652 | out:
653 | free(path);
654 | if (f)
655 | fclose(f);
656 | return ret;
657 | }
658 | #else
659 | int
660 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
661 | int age)
662 | {
663 | return -1;
664 | }
665 | #endif
666 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/http.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_HTTP_H__
2 | #define __RTMP_HTTP_H__
3 | /*
4 | * Copyright (C) 2010 Howard Chu
5 | * Copyright (C) 2010 Antti Ajanki
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | typedef enum {
27 | HTTPRES_OK, /* result OK */
28 | HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */
29 | HTTPRES_NOT_FOUND, /* not found */
30 | HTTPRES_BAD_REQUEST, /* client error */
31 | HTTPRES_SERVER_ERROR, /* server reported an error */
32 | HTTPRES_REDIRECTED, /* resource has been moved */
33 | HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */
34 | } HTTPResult;
35 |
36 | struct HTTP_ctx {
37 | char *date;
38 | int size;
39 | int status;
40 | void *data;
41 | };
42 |
43 | typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
44 |
45 | HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb);
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/log.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008-2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "log.h"
32 |
33 | #define MAX_PRINT_LEN 2048
34 |
35 | #ifdef LOG_TO_LIST
36 | extern void callback_handler(const char* s);
37 | #define printf(x) callback_handler(x)
38 | #else
39 | #include
40 | #include
41 |
42 | #define TAG "librtmp"
43 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
44 |
45 | #define printf(x) LOGV(x)
46 | #endif
47 |
48 | RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR;
49 |
50 | static int neednl;
51 |
52 | static FILE *fmsg;
53 |
54 | static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
55 |
56 | static const char *levels[] = {
57 | "CRIT", "ERROR", "WARNING", "INFO",
58 | "DEBUG", "DEBUG2"
59 | };
60 |
61 |
62 |
63 | static void rtmp_log_default(int level, const char *format, va_list vl)
64 | {
65 | char str[MAX_PRINT_LEN]="";
66 |
67 | vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
68 |
69 | /* Filter out 'no-name' */
70 | if ( RTMP_debuglevel RTMP_debuglevel )
126 | return;
127 |
128 | ptr = line;
129 |
130 | for(i=0; i> 4)];
132 | *ptr++ = hexdig[0x0f & data[i]];
133 | if ((i & 0x0f) == 0x0f) {
134 | *ptr = '\0';
135 | ptr = line;
136 | RTMP_Log(level, "%s", line);
137 | } else {
138 | *ptr++ = ' ';
139 | }
140 | }
141 | if (i & 0x0f) {
142 | *ptr = '\0';
143 | RTMP_Log(level, "%s", line);
144 | }
145 | }
146 |
147 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len)
148 | {
149 | #define BP_OFFSET 9
150 | #define BP_GRAPH 60
151 | #define BP_LEN 80
152 | char line[BP_LEN];
153 | unsigned long i;
154 |
155 | if ( !data || level > RTMP_debuglevel )
156 | return;
157 |
158 | /* in case len is zero */
159 | line[0] = '\0';
160 |
161 | for ( i = 0 ; i < len ; i++ ) {
162 | int n = i % 16;
163 | unsigned off;
164 |
165 | if( !n ) {
166 | if( i ) RTMP_Log( level, "%s", line );
167 | memset( line, ' ', sizeof(line)-2 );
168 | line[sizeof(line)-2] = '\0';
169 |
170 | off = i % 0x0ffffU;
171 |
172 | line[2] = hexdig[0x0f & (off >> 12)];
173 | line[3] = hexdig[0x0f & (off >> 8)];
174 | line[4] = hexdig[0x0f & (off >> 4)];
175 | line[5] = hexdig[0x0f & off];
176 | line[6] = ':';
177 | }
178 |
179 | off = BP_OFFSET + n*3 + ((n >= 8)?1:0);
180 | line[off] = hexdig[0x0f & ( data[i] >> 4 )];
181 | line[off+1] = hexdig[0x0f & data[i]];
182 |
183 | off = BP_GRAPH + n + ((n >= 8)?1:0);
184 |
185 | if ( isprint( data[i] )) {
186 | line[BP_GRAPH + n] = data[i];
187 | } else {
188 | line[BP_GRAPH + n] = '.';
189 | }
190 | }
191 |
192 | RTMP_Log( level, "%s", line );
193 | }
194 |
195 | /* These should only be used by apps, never by the library itself */
196 | void RTMP_LogPrintf(const char *format, ...)
197 | {
198 | char str[MAX_PRINT_LEN]="";
199 | int len;
200 | va_list args;
201 | va_start(args, format);
202 | len = vsnprintf(str, MAX_PRINT_LEN-1, format, args);
203 | va_end(args);
204 |
205 | if ( RTMP_debuglevel==RTMP_LOGCRIT )
206 | return;
207 |
208 | if ( !fmsg ) fmsg = stderr;
209 |
210 | if (neednl) {
211 | putc('\n', fmsg);
212 | neednl = 0;
213 | }
214 |
215 | if (len > MAX_PRINT_LEN-1)
216 | len = MAX_PRINT_LEN-1;
217 | fprintf(fmsg, "%s", str);
218 | printf(str);
219 | if (str[len-1] == '\n')
220 | fflush(fmsg);
221 | }
222 |
223 | void RTMP_LogStatus(const char *format, ...)
224 | {
225 | char str[MAX_PRINT_LEN]="";
226 | va_list args;
227 | va_start(args, format);
228 | vsnprintf(str, MAX_PRINT_LEN-1, format, args);
229 | va_end(args);
230 |
231 | if ( RTMP_debuglevel==RTMP_LOGCRIT )
232 | return;
233 |
234 | if ( !fmsg ) fmsg = stderr;
235 |
236 | fprintf(fmsg, "%s", str);
237 | printf(str);
238 | fflush(fmsg);
239 | neednl = 1;
240 | }
241 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008-2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #ifndef __RTMP_LOG_H__
25 | #define __RTMP_LOG_H__
26 |
27 | #include
28 | #include
29 | #include
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 | /* Enable this to get full debugging output */
35 | /* #define _DEBUG */
36 |
37 | #ifdef _DEBUG
38 | #undef NODEBUG
39 | #endif
40 |
41 | typedef enum
42 | { RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO,
43 | RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL
44 | } RTMP_LogLevel;
45 |
46 | extern RTMP_LogLevel RTMP_debuglevel;
47 |
48 | typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list);
49 | void RTMP_LogSetCallback(RTMP_LogCallback *cb);
50 | void RTMP_LogSetOutput(FILE *file);
51 |
52 | #ifdef __GNUC__
53 | void RTMP_LogPrintf(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
54 | void RTMP_LogStatus(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
55 | void RTMP_Log(int level, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
56 | #else
57 | void RTMP_LogPrintf(const char *format, ...);
58 | void RTMP_LogStatus(const char *format, ...);
59 | void RTMP_Log(int level, const char *format, ...);
60 | #endif
61 | void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
62 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
63 | void RTMP_LogSetLevel(RTMP_LogLevel lvl);
64 | RTMP_LogLevel RTMP_LogGetLevel(void);
65 |
66 | #ifdef __cplusplus
67 | }
68 | #endif
69 |
70 | #endif
71 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/parseurl.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 |
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "log.h"
32 |
33 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
34 | AVal *playpath, AVal *app)
35 | {
36 | char *p, *end, *col, *ques, *slash;
37 |
38 | RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
39 |
40 | *protocol = RTMP_PROTOCOL_RTMP;
41 | *port = 0;
42 | playpath->av_len = 0;
43 | playpath->av_val = NULL;
44 | app->av_len = 0;
45 | app->av_val = NULL;
46 |
47 | /* Old School Parsing */
48 |
49 | /* look for usual :// pattern */
50 | p = strstr(url, "://");
51 | if(!p) {
52 | RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
53 | return FALSE;
54 | }
55 | {
56 | int len = (int)(p-url);
57 |
58 | if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
59 | *protocol = RTMP_PROTOCOL_RTMP;
60 | else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
61 | *protocol = RTMP_PROTOCOL_RTMPT;
62 | else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
63 | *protocol = RTMP_PROTOCOL_RTMPS;
64 | else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
65 | *protocol = RTMP_PROTOCOL_RTMPE;
66 | else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
67 | *protocol = RTMP_PROTOCOL_RTMFP;
68 | else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
69 | *protocol = RTMP_PROTOCOL_RTMPTE;
70 | else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
71 | *protocol = RTMP_PROTOCOL_RTMPTS;
72 | else {
73 | RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
74 | goto parsehost;
75 | }
76 | }
77 |
78 | RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
79 |
80 | parsehost:
81 | /* let's get the hostname */
82 | p+=3;
83 |
84 | /* check for sudden death */
85 | if(*p==0) {
86 | RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
87 | return FALSE;
88 | }
89 |
90 | end = p + strlen(p);
91 | col = strchr(p, ':');
92 | ques = strchr(p, '?');
93 | slash = strchr(p, '/');
94 |
95 | {
96 | int hostlen;
97 | if(slash)
98 | hostlen = slash - p;
99 | else
100 | hostlen = end - p;
101 | if(col && col -p < hostlen)
102 | hostlen = col - p;
103 |
104 | if(hostlen < 256) {
105 | host->av_val = p;
106 | host->av_len = hostlen;
107 | RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
108 | } else {
109 | RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
110 | }
111 |
112 | p+=hostlen;
113 | }
114 |
115 | /* get the port number if available */
116 | if(*p == ':') {
117 | unsigned int p2;
118 | p++;
119 | p2 = atoi(p);
120 | if(p2 > 65535) {
121 | RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
122 | } else {
123 | *port = p2;
124 | }
125 | }
126 |
127 | if(!slash) {
128 | RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
129 | return TRUE;
130 | }
131 | p = slash+1;
132 |
133 | {
134 | /* parse application
135 | *
136 | * rtmp://host[:port]/app[/appinstance][/...]
137 | * application = app[/appinstance]
138 | */
139 |
140 | char *slash2, *slash3 = NULL;
141 | int applen, appnamelen;
142 |
143 | slash2 = strchr(p, '/');
144 | if(slash2)
145 | slash3 = strchr(slash2+1, '/');
146 |
147 | applen = end-p; /* ondemand, pass all parameters as app */
148 | appnamelen = applen; /* ondemand length */
149 |
150 | if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
151 | appnamelen = ques-p;
152 | }
153 | else if(strncmp(p, "ondemand/", 9)==0) {
154 | /* app = ondemand/foobar, only pass app=ondemand */
155 | applen = 8;
156 | appnamelen = 8;
157 | }
158 | else { /* app!=ondemand, so app is app[/appinstance] */
159 | if(slash3)
160 | appnamelen = slash3-p;
161 | else if(slash2)
162 | appnamelen = slash2-p;
163 |
164 | applen = appnamelen;
165 | }
166 |
167 | app->av_val = p;
168 | app->av_len = applen;
169 | RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
170 |
171 | p += appnamelen;
172 | }
173 |
174 | if (*p == '/')
175 | p++;
176 |
177 | if (end-p) {
178 | AVal av = {p, end-p};
179 | RTMP_ParsePlaypath(&av, playpath);
180 | }
181 |
182 | return TRUE;
183 | }
184 |
185 | /*
186 | * Extracts playpath from RTMP URL. playpath is the file part of the
187 | * URL, i.e. the part that comes after rtmp://host:port/app/
188 | *
189 | * Returns the stream name in a format understood by FMS. The name is
190 | * the playpath part of the URL with formatting depending on the stream
191 | * type:
192 | *
193 | * mp4 streams: prepend "mp4:", remove extension
194 | * mp3 streams: prepend "mp3:", remove extension
195 | * flv streams: remove extension
196 | */
197 | void RTMP_ParsePlaypath(AVal *in, AVal *out) {
198 | int addMP4 = 0;
199 | int addMP3 = 0;
200 | int subExt = 0;
201 | const char *playpath = in->av_val;
202 | const char *temp, *q, *ext = NULL;
203 | const char *ppstart = playpath;
204 | char *streamname, *destptr, *p;
205 |
206 | int pplen = in->av_len;
207 |
208 | out->av_val = NULL;
209 | out->av_len = 0;
210 |
211 | if ((*ppstart == '?') &&
212 | (temp=strstr(ppstart, "slist=")) != 0) {
213 | ppstart = temp+6;
214 | pplen = strlen(ppstart);
215 |
216 | temp = strchr(ppstart, '&');
217 | if (temp) {
218 | pplen = temp-ppstart;
219 | }
220 | }
221 |
222 | q = strchr(ppstart, '?');
223 | if (pplen >= 4) {
224 | if (q)
225 | ext = q-4;
226 | else
227 | ext = &ppstart[pplen-4];
228 | if ((strncmp(ext, ".f4v", 4) == 0) ||
229 | (strncmp(ext, ".mp4", 4) == 0)) {
230 | addMP4 = 1;
231 | subExt = 1;
232 | /* Only remove .flv from rtmp URL, not slist params */
233 | } else if ((ppstart == playpath) &&
234 | (strncmp(ext, ".flv", 4) == 0)) {
235 | subExt = 1;
236 | } else if (strncmp(ext, ".mp3", 4) == 0) {
237 | addMP3 = 1;
238 | subExt = 1;
239 | }
240 | }
241 |
242 | streamname = (char *)malloc((pplen+4+1)*sizeof(char));
243 | if (!streamname)
244 | return;
245 |
246 | destptr = streamname;
247 | if (addMP4) {
248 | if (strncmp(ppstart, "mp4:", 4)) {
249 | strcpy(destptr, "mp4:");
250 | destptr += 4;
251 | } else {
252 | subExt = 0;
253 | }
254 | } else if (addMP3) {
255 | if (strncmp(ppstart, "mp3:", 4)) {
256 | strcpy(destptr, "mp3:");
257 | destptr += 4;
258 | } else {
259 | subExt = 0;
260 | }
261 | }
262 |
263 | for (p=(char *)ppstart; pplen >0;) {
264 | /* skip extension */
265 | if (subExt && p == ext) {
266 | p += 4;
267 | pplen -= 4;
268 | continue;
269 | }
270 | if (*p == '%') {
271 | unsigned int c;
272 | sscanf(p+1, "%02x", &c);
273 | *destptr++ = c;
274 | pplen -= 3;
275 | p += 3;
276 | } else {
277 | *destptr++ = *p++;
278 | pplen--;
279 | }
280 | }
281 | *destptr = '\0';
282 |
283 | out->av_val = streamname;
284 | out->av_len = destptr - streamname;
285 | }
286 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/rtmp.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_H__
2 | #define __RTMP_H__
3 | /*
4 | * Copyright (C) 2005-2008 Team XBMC
5 | * http://www.xbmc.org
6 | * Copyright (C) 2008-2009 Andrej Stepanchuk
7 | * Copyright (C) 2009-2010 Howard Chu
8 | *
9 | * This file is part of librtmp.
10 | *
11 | * librtmp is free software; you can redistribute it and/or modify
12 | * it under the terms of the GNU Lesser General Public License as
13 | * published by the Free Software Foundation; either version 2.1,
14 | * or (at your option) any later version.
15 | *
16 | * librtmp is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Lesser General Public License
22 | * along with librtmp see the file COPYING. If not, write to
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 | * Boston, MA 02110-1301, USA.
25 | * http://www.gnu.org/copyleft/lgpl.html
26 | */
27 |
28 | #if !defined(NO_CRYPTO) && !defined(CRYPTO)
29 | #define CRYPTO
30 | #endif
31 |
32 | #include
33 | #include
34 | #include
35 |
36 | #include "amf.h"
37 |
38 | #ifdef __cplusplus
39 | extern "C"
40 | {
41 | #endif
42 |
43 | #define RTMP_LIB_VERSION 0x020300 /* 2.3 */
44 |
45 | #define RTMP_FEATURE_HTTP 0x01
46 | #define RTMP_FEATURE_ENC 0x02
47 | #define RTMP_FEATURE_SSL 0x04
48 | #define RTMP_FEATURE_MFP 0x08 /* not yet supported */
49 | #define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
50 | #define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
51 |
52 | #define RTMP_PROTOCOL_UNDEFINED -1
53 | #define RTMP_PROTOCOL_RTMP 0
54 | #define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
55 | #define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
56 | #define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
57 | #define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
58 | #define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
59 | #define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
60 |
61 | #define RTMP_DEFAULT_CHUNKSIZE 128
62 |
63 | /* needs to fit largest number of bytes recv() may return */
64 | #define RTMP_BUFFER_CACHE_SIZE (16*1024)
65 |
66 | #define RTMP_CHANNELS 65600
67 |
68 | extern const char RTMPProtocolStringsLower[][7];
69 | extern const AVal RTMP_DefaultFlashVer;
70 | extern int RTMP_ctrlC;
71 |
72 | uint32_t RTMP_GetTime(void);
73 |
74 | /* RTMP_PACKET_TYPE_... 0x00 */
75 | #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01
76 | /* RTMP_PACKET_TYPE_... 0x02 */
77 | #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03
78 | #define RTMP_PACKET_TYPE_CONTROL 0x04
79 | #define RTMP_PACKET_TYPE_SERVER_BW 0x05
80 | #define RTMP_PACKET_TYPE_CLIENT_BW 0x06
81 | /* RTMP_PACKET_TYPE_... 0x07 */
82 | #define RTMP_PACKET_TYPE_AUDIO 0x08
83 | #define RTMP_PACKET_TYPE_VIDEO 0x09
84 | /* RTMP_PACKET_TYPE_... 0x0A */
85 | /* RTMP_PACKET_TYPE_... 0x0B */
86 | /* RTMP_PACKET_TYPE_... 0x0C */
87 | /* RTMP_PACKET_TYPE_... 0x0D */
88 | /* RTMP_PACKET_TYPE_... 0x0E */
89 | #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F
90 | #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10
91 | #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11
92 | #define RTMP_PACKET_TYPE_INFO 0x12
93 | #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13
94 | #define RTMP_PACKET_TYPE_INVOKE 0x14
95 | /* RTMP_PACKET_TYPE_... 0x15 */
96 | #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16
97 |
98 | #define RTMP_MAX_HEADER_SIZE 18
99 |
100 | #define RTMP_PACKET_SIZE_LARGE 0
101 | #define RTMP_PACKET_SIZE_MEDIUM 1
102 | #define RTMP_PACKET_SIZE_SMALL 2
103 | #define RTMP_PACKET_SIZE_MINIMUM 3
104 |
105 | typedef struct RTMPChunk
106 | {
107 | int c_headerSize;
108 | int c_chunkSize;
109 | char *c_chunk;
110 | char c_header[RTMP_MAX_HEADER_SIZE];
111 | } RTMPChunk;
112 |
113 | typedef struct RTMPPacket
114 | {
115 | uint8_t m_headerType;
116 | uint8_t m_packetType;
117 | uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
118 | int m_nChannel;
119 | uint32_t m_nTimeStamp; /* timestamp */
120 | int32_t m_nInfoField2; /* last 4 bytes in a long header */
121 | uint32_t m_nBodySize;
122 | uint32_t m_nBytesRead;
123 | RTMPChunk *m_chunk;
124 | char *m_body;
125 | } RTMPPacket;
126 |
127 | typedef struct RTMPSockBuf
128 | {
129 | int sb_socket;
130 | int sb_size; /* number of unprocessed bytes in buffer */
131 | char *sb_start; /* pointer into sb_pBuffer of next byte to process */
132 | char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
133 | int sb_timedout;
134 | void *sb_ssl;
135 | } RTMPSockBuf;
136 |
137 | void RTMPPacket_Reset(RTMPPacket *p);
138 | void RTMPPacket_Dump(RTMPPacket *p);
139 | int RTMPPacket_Alloc(RTMPPacket *p, int nSize);
140 | void RTMPPacket_Free(RTMPPacket *p);
141 |
142 | #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)
143 |
144 | typedef struct RTMP_LNK
145 | {
146 | AVal hostname;
147 | AVal sockshost;
148 |
149 | AVal playpath0; /* parsed from URL */
150 | AVal playpath; /* passed in explicitly */
151 | AVal tcUrl;
152 | AVal swfUrl;
153 | AVal pageUrl;
154 | AVal app;
155 | AVal auth;
156 | AVal flashVer;
157 | AVal subscribepath;
158 | AVal usherToken;
159 | AVal token;
160 | AMFObject extras;
161 | int edepth;
162 |
163 | int seekTime;
164 | int stopTime;
165 |
166 | #define RTMP_LF_AUTH 0x0001 /* using auth param */
167 | #define RTMP_LF_LIVE 0x0002 /* stream is live */
168 | #define RTMP_LF_SWFV 0x0004 /* do SWF verification */
169 | #define RTMP_LF_PLST 0x0008 /* send playlist before play */
170 | #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
171 | #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
172 | int lFlags;
173 |
174 | int swfAge;
175 |
176 | int protocol;
177 | int timeout; /* connection timeout in seconds */
178 |
179 | unsigned short socksport;
180 | unsigned short port;
181 |
182 | #ifdef CRYPTO
183 | #define RTMP_SWF_HASHLEN 32
184 | void *dh; /* for encryption */
185 | void *rc4keyIn;
186 | void *rc4keyOut;
187 |
188 | uint32_t SWFSize;
189 | uint8_t SWFHash[RTMP_SWF_HASHLEN];
190 | char SWFVerificationResponse[RTMP_SWF_HASHLEN+10];
191 | #endif
192 | } RTMP_LNK;
193 |
194 | /* state for read() wrapper */
195 | typedef struct RTMP_READ
196 | {
197 | char *buf;
198 | char *bufpos;
199 | unsigned int buflen;
200 | uint32_t timestamp;
201 | uint8_t dataType;
202 | uint8_t flags;
203 | #define RTMP_READ_HEADER 0x01
204 | #define RTMP_READ_RESUME 0x02
205 | #define RTMP_READ_NO_IGNORE 0x04
206 | #define RTMP_READ_GOTKF 0x08
207 | #define RTMP_READ_GOTFLVK 0x10
208 | #define RTMP_READ_SEEKING 0x20
209 | int8_t status;
210 | #define RTMP_READ_COMPLETE -3
211 | #define RTMP_READ_ERROR -2
212 | #define RTMP_READ_EOF -1
213 | #define RTMP_READ_IGNORE 0
214 |
215 | /* if bResume == TRUE */
216 | uint8_t initialFrameType;
217 | uint32_t nResumeTS;
218 | char *metaHeader;
219 | char *initialFrame;
220 | uint32_t nMetaHeaderSize;
221 | uint32_t nInitialFrameSize;
222 | uint32_t nIgnoredFrameCounter;
223 | uint32_t nIgnoredFlvFrameCounter;
224 | } RTMP_READ;
225 |
226 | typedef struct RTMP_METHOD
227 | {
228 | AVal name;
229 | int num;
230 | } RTMP_METHOD;
231 |
232 | typedef struct RTMP
233 | {
234 | int m_inChunkSize;
235 | int m_outChunkSize;
236 | int m_nBWCheckCounter;
237 | int m_nBytesIn;
238 | int m_nBytesInSent;
239 | int m_nBufferMS;
240 | int m_stream_id; /* returned in _result from createStream */
241 | int m_mediaChannel;
242 | uint32_t m_mediaStamp;
243 | uint32_t m_pauseStamp;
244 | int m_pausing;
245 | int m_nServerBW;
246 | int m_nClientBW;
247 | uint8_t m_nClientBW2;
248 | uint8_t m_bPlaying;
249 | uint8_t m_bSendEncoding;
250 | uint8_t m_bSendCounter;
251 |
252 | int m_numInvokes;
253 | int m_numCalls;
254 | RTMP_METHOD *m_methodCalls; /* remote method calls queue */
255 |
256 | RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
257 | RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
258 | int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */
259 |
260 | double m_fAudioCodecs; /* audioCodecs for the connect packet */
261 | double m_fVideoCodecs; /* videoCodecs for the connect packet */
262 | double m_fEncoding; /* AMF0 or AMF3 */
263 |
264 | double m_fDuration; /* duration of stream in seconds */
265 |
266 | int m_msgCounter; /* RTMPT stuff */
267 | int m_polling;
268 | int m_resplen;
269 | int m_unackd;
270 | AVal m_clientID;
271 |
272 | RTMP_READ m_read;
273 | RTMPPacket m_write;
274 | RTMPSockBuf m_sb;
275 | RTMP_LNK Link;
276 | } RTMP;
277 |
278 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host,
279 | unsigned int *port, AVal *playpath, AVal *app);
280 |
281 | void RTMP_ParsePlaypath(AVal *in, AVal *out);
282 | void RTMP_SetBufferMS(RTMP *r, int size);
283 | void RTMP_UpdateBufferMS(RTMP *r);
284 |
285 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);
286 | int RTMP_SetupURL(RTMP *r, char *url);
287 | void RTMP_SetupStream(RTMP *r, int protocol,
288 | AVal *hostname,
289 | unsigned int port,
290 | AVal *sockshost,
291 | AVal *playpath,
292 | AVal *tcUrl,
293 | AVal *swfUrl,
294 | AVal *pageUrl,
295 | AVal *app,
296 | AVal *auth,
297 | AVal *swfSHA256Hash,
298 | uint32_t swfSize,
299 | AVal *flashVer,
300 | AVal *subscribepath,
301 | AVal *usherToken,
302 | int dStart,
303 | int dStop, int bLiveStream, long int timeout);
304 |
305 | int RTMP_Connect(RTMP *r, RTMPPacket *cp);
306 | struct sockaddr;
307 | int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
308 | int RTMP_Connect1(RTMP *r, RTMPPacket *cp);
309 | int RTMP_Serve(RTMP *r);
310 |
311 | int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
312 | int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
313 | int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);
314 | int RTMP_IsConnected(RTMP *r);
315 | int RTMP_Socket(RTMP *r);
316 | int RTMP_IsTimedout(RTMP *r);
317 | double RTMP_GetDuration(RTMP *r);
318 | int RTMP_ToggleStream(RTMP *r);
319 |
320 | int RTMP_ConnectStream(RTMP *r, int seekTime);
321 | int RTMP_ReconnectStream(RTMP *r, int seekTime);
322 | void RTMP_DeleteStream(RTMP *r);
323 | int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet);
324 | int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet);
325 |
326 | void RTMP_Init(RTMP *r);
327 | void RTMP_Close(RTMP *r);
328 | RTMP *RTMP_Alloc(void);
329 | void RTMP_Free(RTMP *r);
330 | void RTMP_EnableWrite(RTMP *r);
331 |
332 | int RTMP_LibVersion(void);
333 | void RTMP_UserInterrupt(void); /* user typed Ctrl-C */
334 |
335 | int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject,
336 | unsigned int nTime);
337 |
338 | /* caller probably doesn't know current timestamp, should
339 | * just use RTMP_Pause instead
340 | */
341 | int RTMP_SendPause(RTMP *r, int DoPause, int dTime);
342 | int RTMP_Pause(RTMP *r, int DoPause);
343 |
344 | int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
345 | AMFObjectProperty * p);
346 |
347 | int RTMPSockBuf_Fill(RTMPSockBuf *sb);
348 | int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
349 | int RTMPSockBuf_Close(RTMPSockBuf *sb);
350 |
351 | int RTMP_SendCreateStream(RTMP *r);
352 | int RTMP_SendSeek(RTMP *r, int dTime);
353 | int RTMP_SendServerBW(RTMP *r);
354 | int RTMP_SendClientBW(RTMP *r);
355 | void RTMP_DropRequest(RTMP *r, int i, int freeit);
356 | int RTMP_Read(RTMP *r, char *buf, int size);
357 | int RTMP_Write(RTMP *r, const char *buf, int size);
358 |
359 | /* hashswf.c */
360 | int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
361 | int age);
362 |
363 | #ifdef __cplusplus
364 | };
365 | #endif
366 |
367 | #endif
368 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/librtmp/rtmp_sys.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_SYS_H__
2 | #define __RTMP_SYS_H__
3 | /*
4 | * Copyright (C) 2010 Howard Chu
5 | *
6 | * This file is part of librtmp.
7 | *
8 | * librtmp is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU Lesser General Public License as
10 | * published by the Free Software Foundation; either version 2.1,
11 | * or (at your option) any later version.
12 | *
13 | * librtmp is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU Lesser General Public License
19 | * along with librtmp see the file COPYING. If not, write to
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 | * Boston, MA 02110-1301, USA.
22 | * http://www.gnu.org/copyleft/lgpl.html
23 | */
24 |
25 | #ifdef _WIN32
26 |
27 | #include
28 | #include
29 |
30 | #ifdef _MSC_VER /* MSVC */
31 | #define snprintf _snprintf
32 | #define strcasecmp stricmp
33 | #define strncasecmp strnicmp
34 | #define vsnprintf _vsnprintf
35 | #endif
36 |
37 | #define GetSockError() WSAGetLastError()
38 | #define SetSockError(e) WSASetLastError(e)
39 | #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
40 | #define EWOULDBLOCK WSAETIMEDOUT /* we don't use nonblocking, but we do use timeouts */
41 | #define sleep(n) Sleep(n*1000)
42 | #define msleep(n) Sleep(n)
43 | #define SET_RCVTIMEO(tv,s) int tv = s*1000
44 | #else /* !_WIN32 */
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #define GetSockError() errno
54 | #define SetSockError(e) errno = e
55 | #undef closesocket
56 | #define closesocket(s) close(s)
57 | #define msleep(n) usleep(n*1000)
58 | #define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0}
59 | #endif
60 |
61 | #include "rtmp.h"
62 |
63 | #ifdef USE_POLARSSL
64 | #include
65 | #include
66 | #include
67 | typedef struct tls_ctx {
68 | havege_state hs;
69 | ssl_session ssn;
70 | } tls_ctx;
71 | #define TLS_CTX tls_ctx *
72 | #define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\
73 | ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
74 | ssl_set_rng(s, havege_rand, &ctx->hs);\
75 | ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
76 | ssl_set_session(s, 1, 600, &ctx->ssn)
77 | #define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd)
78 | #define TLS_connect(s) ssl_handshake(s)
79 | #define TLS_read(s,b,l) ssl_read(s,(unsigned char *)b,l)
80 | #define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l)
81 | #define TLS_shutdown(s) ssl_close_notify(s)
82 | #define TLS_close(s) ssl_free(s); free(s)
83 |
84 | #elif defined(USE_GNUTLS)
85 | #include
86 | typedef struct tls_ctx {
87 | gnutls_certificate_credentials_t cred;
88 | gnutls_priority_t prios;
89 | } tls_ctx;
90 | #define TLS_CTX tls_ctx *
91 | #define TLS_client(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred)
92 | #define TLS_setfd(s,fd) gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd)
93 | #define TLS_connect(s) gnutls_handshake(s)
94 | #define TLS_read(s,b,l) gnutls_record_recv(s,b,l)
95 | #define TLS_write(s,b,l) gnutls_record_send(s,b,l)
96 | #define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR)
97 | #define TLS_close(s) gnutls_deinit(s)
98 |
99 | #else /* USE_OPENSSL */
100 | #define TLS_CTX SSL_CTX *
101 | #define TLS_client(ctx,s) s = SSL_new(ctx)
102 | #define TLS_setfd(s,fd) SSL_set_fd(s,fd)
103 | #define TLS_connect(s) SSL_connect(s)
104 | #define TLS_read(s,b,l) SSL_read(s,b,l)
105 | #define TLS_write(s,b,l) SSL_write(s,b,l)
106 | #define TLS_shutdown(s) SSL_shutdown(s)
107 | #define TLS_close(s) SSL_free(s)
108 |
109 | #endif
110 | #endif
111 |
--------------------------------------------------------------------------------
/app/src/main/jni/dump/rtmpdump.c:
--------------------------------------------------------------------------------
1 | /* RTMPDump
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | * Copyright (C) 2009 Howard Chu
4 | *
5 | * This Program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; either version 2, or (at your option)
8 | * any later version.
9 | *
10 | * This Program 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
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with RTMPDump; see the file COPYING. If not, write to
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 | * Boston, MA 02110-1301, USA.
19 | * http://www.gnu.org/copyleft/gpl.html
20 | *
21 | */
22 |
23 | #define _FILE_OFFSET_BITS 64
24 |
25 | #include
26 | #include
27 |
28 | #include // to catch Ctrl-C
29 | #include
30 |
31 | #include "librtmp/rtmp_sys.h"
32 | #include "librtmp/log.h"
33 |
34 | #ifdef WIN32
35 | #define fseeko fseeko64
36 | #define ftello ftello64
37 | #include
38 | #include
39 | #define SET_BINMODE(f) setmode(fileno(f), O_BINARY)
40 | #else
41 | #define SET_BINMODE(f)
42 | #endif
43 |
44 | #define RD_SUCCESS 0
45 | #define RD_FAILED 1
46 | #define RD_INCOMPLETE 2
47 | #define RD_NO_CONNECT 3
48 |
49 | #define DEF_TIMEOUT 30 /* seconds */
50 | #define DEF_BUFTIME (10 * 60 * 60 * 1000) /* 10 hours default */
51 | #define DEF_SKIPFRM 0
52 |
53 | /* original entry point of the program */
54 | int rtmpdump_main(int argc, char **argv);
55 |
56 | // starts sockets
57 | int InitSockets() {
58 | #ifdef WIN32
59 | WORD version;
60 | WSADATA wsaData;
61 |
62 | version = MAKEWORD(1, 1);
63 | return (WSAStartup(version, &wsaData) == 0);
64 | #else
65 | return TRUE;
66 | #endif
67 | }
68 |
69 | inline void CleanupSockets() {
70 | #ifdef WIN32
71 | WSACleanup();
72 | #endif
73 | }
74 |
75 | #ifdef _DEBUG
76 | uint32_t debugTS = 0;
77 | int pnum = 0;
78 |
79 | FILE *netstackdump = 0;
80 | FILE *netstackdump_read = 0;
81 | #endif
82 |
83 | uint32_t nIgnoredFlvFrameCounter = 0;
84 | uint32_t nIgnoredFrameCounter = 0;
85 | #define MAX_IGNORED_FRAMES 50
86 |
87 | FILE *file = 0;
88 |
89 | void sigIntHandler(int sig) {
90 |
91 | RTMP_ctrlC = TRUE;
92 | RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
93 | // ignore all these signals now and let the connection close
94 | signal(SIGINT, SIG_IGN);
95 | signal(SIGTERM, SIG_IGN);
96 | #ifndef WIN32
97 | signal(SIGHUP, SIG_IGN);
98 | signal(SIGPIPE, SIG_IGN);
99 | signal(SIGQUIT, SIG_IGN);
100 | #endif
101 |
102 | }
103 |
104 | #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
105 | int hex2bin(char *str, char **hex) {
106 | char *ptr;
107 | int i, l = strlen(str);
108 |
109 | if (l & 1)
110 | return 0;
111 |
112 | *hex = malloc(l / 2);
113 | ptr = *hex;
114 | if (!ptr)
115 | return 0;
116 |
117 | for (i = 0; i < l; i += 2)
118 | *ptr++ = (HEX2BIN(str[i]) << 4) | HEX2BIN(str[i+1]);
119 | return l / 2;
120 | }
121 |
122 | static const AVal av_onMetaData = AVC("onMetaData");
123 | static const AVal av_duration = AVC("duration");
124 | static const AVal av_conn = AVC("conn");
125 | static const AVal av_token = AVC("token");
126 | static const AVal av_playlist = AVC("playlist");
127 | static const AVal av_true = AVC("true");
128 |
129 | int OpenResumeFile(const char *flvFile, // file name [in]
130 | FILE ** file, // opened file [out]
131 | off_t * size, // size of the file [out]
132 | char **metaHeader, // meta data read from the file [out]
133 | uint32_t * nMetaHeaderSize, // length of metaHeader [out]
134 | double *duration) // duration of the stream in ms [out]
135 | {
136 | size_t bufferSize = 0;
137 | char hbuf[16], *buffer = NULL;
138 |
139 | *nMetaHeaderSize = 0;
140 | *size = 0;
141 |
142 | *file = fopen(flvFile, "r+b");
143 | if (!*file)
144 | return RD_SUCCESS;// RD_SUCCESS, because we go to fresh file mode instead of quiting
145 |
146 | fseek(*file, 0, SEEK_END);
147 | *size = ftello(*file);
148 | fseek(*file, 0, SEEK_SET);
149 |
150 | if (*size > 0) {
151 | // verify FLV format and read header
152 | uint32_t prevTagSize = 0;
153 |
154 | // check we've got a valid FLV file to continue!
155 | if (fread(hbuf, 1, 13, *file) != 13) {
156 | RTMP_Log(RTMP_LOGERROR, "Couldn't read FLV file header!");
157 | return RD_FAILED;
158 | }
159 | if (hbuf[0] != 'F' || hbuf[1] != 'L' || hbuf[2] != 'V' || hbuf[3] != 0x01) {
160 | RTMP_Log(RTMP_LOGERROR, "Invalid FLV file!");
161 | return RD_FAILED;
162 | }
163 |
164 | if ((hbuf[4] & 0x05) == 0) {
165 | RTMP_Log(RTMP_LOGERROR,
166 | "FLV file contains neither video nor audio, aborting!");
167 | return RD_FAILED;
168 | }
169 |
170 | uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5);
171 | fseek(*file, dataOffset, SEEK_SET);
172 |
173 | if (fread(hbuf, 1, 4, *file) != 4) {
174 | RTMP_Log(RTMP_LOGERROR, "Invalid FLV file: missing first prevTagSize!");
175 | return RD_FAILED;
176 | }
177 | prevTagSize = AMF_DecodeInt32(hbuf);
178 | if (prevTagSize != 0) {
179 | RTMP_Log(RTMP_LOGWARNING,
180 | "First prevTagSize is not zero: prevTagSize = 0x%08X",
181 | prevTagSize);
182 | }
183 |
184 | // go through the file to find the meta data!
185 | off_t pos = dataOffset + 4;
186 | int bFoundMetaHeader = FALSE;
187 |
188 | while (pos < *size - 4 && !bFoundMetaHeader) {
189 | fseeko(*file, pos, SEEK_SET);
190 | if (fread(hbuf, 1, 4, *file) != 4)
191 | break;
192 |
193 | uint32_t dataSize = AMF_DecodeInt24(hbuf + 1);
194 |
195 | if (hbuf[0] == 0x12) {
196 | if (dataSize > bufferSize) {
197 | /* round up to next page boundary */
198 | bufferSize = dataSize + 4095;
199 | bufferSize ^= (bufferSize & 4095);
200 | free(buffer);
201 | buffer = malloc(bufferSize);
202 | if (!buffer)
203 | return RD_FAILED;
204 | }
205 |
206 | fseeko(*file, pos + 11, SEEK_SET);
207 | if (fread(buffer, 1, dataSize, *file) != dataSize)
208 | break;
209 |
210 | AMFObject metaObj;
211 | int nRes = AMF_Decode(&metaObj, buffer, dataSize, FALSE);
212 | if (nRes < 0) {
213 | RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet",
214 | __FUNCTION__);
215 | break;
216 | }
217 |
218 | AVal metastring;
219 | AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);
220 |
221 | if (AVMATCH(&metastring, &av_onMetaData)) {
222 | AMF_Dump(&metaObj);
223 |
224 | *nMetaHeaderSize = dataSize;
225 | if (*metaHeader)
226 | free(*metaHeader);
227 | *metaHeader = (char *) malloc(*nMetaHeaderSize);
228 | memcpy(*metaHeader, buffer, *nMetaHeaderSize);
229 |
230 | // get duration
231 | AMFObjectProperty prop;
232 | if (RTMP_FindFirstMatchingProperty(&metaObj, &av_duration,
233 | &prop)) {
234 | *duration = AMFProp_GetNumber(&prop);
235 | RTMP_Log(RTMP_LOGDEBUG, "File has duration: %f", *duration);
236 | }
237 |
238 | bFoundMetaHeader = TRUE;
239 | break;
240 | }
241 | //metaObj.Reset();
242 | //delete obj;
243 | }
244 | pos += (dataSize + 11 + 4);
245 | }
246 |
247 | free(buffer);
248 | if (!bFoundMetaHeader)
249 | RTMP_Log(RTMP_LOGWARNING, "Couldn't locate meta data!");
250 | }
251 |
252 | return RD_SUCCESS;
253 | }
254 |
255 | int GetLastKeyframe(FILE * file, // output file [in]
256 | int nSkipKeyFrames, // max number of frames to skip when searching for key frame [in]
257 | uint32_t * dSeek, // offset of the last key frame [out]
258 | char **initialFrame, // content of the last keyframe [out]
259 | int *initialFrameType, // initial frame type (audio/video) [out]
260 | uint32_t * nInitialFrameSize) // length of initialFrame [out]
261 | {
262 | const size_t bufferSize = 16;
263 | char buffer[bufferSize];
264 | uint8_t dataType;
265 | int bAudioOnly;
266 | off_t size;
267 |
268 | fseek(file, 0, SEEK_END);
269 | size = ftello(file);
270 |
271 | fseek(file, 4, SEEK_SET);
272 | if (fread(&dataType, sizeof(uint8_t), 1, file) != 1)
273 | return RD_FAILED;
274 |
275 | bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);
276 |
277 | RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly,
278 | (unsigned long long) size);
279 |
280 | // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
281 |
282 | //if(!bAudioOnly) // we have to handle video/video+audio different since we have non-seekable frames
283 | //{
284 | // find the last seekable frame
285 | off_t tsize = 0;
286 | uint32_t prevTagSize = 0;
287 |
288 | // go through the file and find the last video keyframe
289 | do {
290 | int xread;
291 | skipkeyframe: if (size - tsize < 13) {
292 | RTMP_Log(RTMP_LOGERROR,
293 | "Unexpected start of file, error in tag sizes, couldn't arrive at prevTagSize=0");
294 | return RD_FAILED;
295 | }
296 | fseeko(file, size - tsize - 4, SEEK_SET);
297 | xread = fread(buffer, 1, 4, file);
298 | if (xread != 4) {
299 | RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
300 | return RD_FAILED;
301 | }
302 |
303 | prevTagSize = AMF_DecodeInt32(buffer);
304 | //RTMP_Log(RTMP_LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
305 |
306 | if (prevTagSize == 0) {
307 | RTMP_Log(RTMP_LOGERROR, "Couldn't find keyframe to resume from!");
308 | return RD_FAILED;
309 | }
310 |
311 | if (prevTagSize < 0 || prevTagSize > size - 4 - 13) {
312 | RTMP_Log(RTMP_LOGERROR,
313 | "Last tag size must be greater/equal zero (prevTagSize=%d) and smaller then filesize, corrupt file!",
314 | prevTagSize);
315 | return RD_FAILED;
316 | }
317 | tsize += prevTagSize + 4;
318 |
319 | // read header
320 | fseeko(file, size - tsize, SEEK_SET);
321 | if (fread(buffer, 1, 12, file) != 12) {
322 | RTMP_Log(RTMP_LOGERROR, "Couldn't read header!");
323 | return RD_FAILED;
324 | }
325 | //*
326 | #ifdef _DEBUG
327 | uint32_t ts = AMF_DecodeInt24(buffer + 4);
328 | ts |= (buffer[7] << 24);
329 | RTMP_Log(RTMP_LOGDEBUG, "%02X: TS: %d ms", buffer[0], ts);
330 | #endif //*/
331 | // this just continues the loop whenever the number of skipped frames is > 0,
332 | // so we look for the next keyframe to continue with
333 | //
334 | // this helps if resuming from the last keyframe fails and one doesn't want to start
335 | // the download from the beginning
336 | //
337 | if (nSkipKeyFrames > 0
338 | && !(!bAudioOnly
339 | && (buffer[0] != 0x09 || (buffer[11] & 0xf0) != 0x10))) {
340 | #ifdef _DEBUG
341 | RTMP_Log(RTMP_LOGDEBUG,
342 | "xxxxxxxxxxxxxxxxxxxxxxxx Well, lets go one more back!");
343 | #endif
344 | nSkipKeyFrames--;
345 | goto skipkeyframe;
346 | }
347 |
348 | } while ((bAudioOnly && buffer[0] != 0x08)
349 | || (!bAudioOnly && (buffer[0] != 0x09 || (buffer[11] & 0xf0) != 0x10)));// as long as we don't have a keyframe / last audio frame
350 |
351 | // save keyframe to compare/find position in stream
352 | *initialFrameType = buffer[0];
353 | *nInitialFrameSize = prevTagSize - 11;
354 | *initialFrame = (char *) malloc(*nInitialFrameSize);
355 |
356 | fseeko(file, size - tsize + 11, SEEK_SET);
357 | if (fread(*initialFrame, 1, *nInitialFrameSize, file) != *nInitialFrameSize) {
358 | RTMP_Log(RTMP_LOGERROR, "Couldn't read last keyframe, aborting!");
359 | return RD_FAILED;
360 | }
361 |
362 | *dSeek = AMF_DecodeInt24(buffer + 4);// set seek position to keyframe tmestamp
363 | *dSeek |= (buffer[7] << 24);
364 | //}
365 | //else // handle audio only, we can seek anywhere we'd like
366 | //{
367 | //}
368 |
369 | if (*dSeek < 0) {
370 | RTMP_Log(RTMP_LOGERROR,
371 | "Last keyframe timestamp is negative, aborting, your file is corrupt!");
372 | return RD_FAILED;
373 | }
374 | RTMP_Log(RTMP_LOGDEBUG, "Last keyframe found at: %d ms, size: %d, type: %02X",
375 | *dSeek, *nInitialFrameSize, *initialFrameType);
376 |
377 | /*
378 | // now read the timestamp of the frame before the seekable keyframe:
379 | fseeko(file, size-tsize-4, SEEK_SET);
380 | if(fread(buffer, 1, 4, file) != 4) {
381 | RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
382 | goto start;
383 | }
384 | uint32_t prevTagSize = RTMP_LIB::AMF_DecodeInt32(buffer);
385 | fseeko(file, size-tsize-4-prevTagSize+4, SEEK_SET);
386 | if(fread(buffer, 1, 4, file) != 4) {
387 | RTMP_Log(RTMP_LOGERROR, "Couldn't read previous timestamp!");
388 | goto start;
389 | }
390 | uint32_t timestamp = RTMP_LIB::AMF_DecodeInt24(buffer);
391 | timestamp |= (buffer[3]<<24);
392 |
393 | RTMP_Log(RTMP_LOGDEBUG, "Previous timestamp: %d ms", timestamp);
394 | */
395 |
396 | if (*dSeek != 0) {
397 | // seek to position after keyframe in our file (we will ignore the keyframes resent by the server
398 | // since they are sent a couple of times and handling this would be a mess)
399 | fseeko(file, size - tsize + prevTagSize + 4, SEEK_SET);
400 |
401 | // make sure the WriteStream doesn't write headers and ignores all the 0ms TS packets
402 | // (including several meta data headers and the keyframe we seeked to)
403 | //bNoHeader = TRUE; if bResume==true this is true anyway
404 | }
405 |
406 | //}
407 |
408 | return RD_SUCCESS;
409 | }
410 |
411 | int Download(
412 | RTMP * rtmp, // connected RTMP object
413 | FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration,
414 | int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame,
415 | int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames,
416 | int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes,
417 | int bOverrideBufferTime, uint32_t bufferTime, double *percent)// percentage downloaded [out]
418 | {
419 | int32_t now, lastUpdate;
420 | int bufferSize = 64 * 1024;
421 | char *buffer;
422 | int nRead = 0;
423 | off_t size = ftello(file);
424 | unsigned long lastPercent = 0;
425 |
426 | rtmp->m_read.timestamp = dSeek;
427 |
428 | *percent = 0.0;
429 |
430 | if (rtmp->m_read.timestamp) {
431 | RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n",
432 | rtmp->m_read.timestamp);
433 | }
434 |
435 | if (bLiveStream) {
436 | RTMP_LogPrintf("Starting Live Stream\n");
437 | } else {
438 | // print initial status
439 | // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
440 | if (duration > 0) {
441 | if ((double) rtmp->m_read.timestamp >= (double) duration * 999.0) {
442 | RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n",
443 | (double) rtmp->m_read.timestamp / 1000.0,
444 | (double) duration / 1000.0);
445 | return RD_SUCCESS;
446 | } else {
447 | *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0)
448 | * 100.0;
449 | *percent = ((double) (int) (*percent * 10.0)) / 10.0;
450 | RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n",
451 | bResume ? "Resuming" : "Starting", (double) size / 1024.0,
452 | (double) rtmp->m_read.timestamp / 1000.0, *percent);
453 | }
454 | } else {
455 | RTMP_LogPrintf("%s download at: %.3f kB\n",
456 | bResume ? "Resuming" : "Starting", (double) size / 1024.0);
457 | }
458 | if (bRealtimeStream)
459 | RTMP_LogPrintf(
460 | " in approximately realtime (disabled BUFX speedup hack)\n");
461 | }
462 |
463 | if (dStopOffset > 0)
464 | RTMP_LogPrintf("For duration: %.3f sec\n",
465 | (double) (dStopOffset - dSeek) / 1000.0);
466 |
467 | if (bResume && nInitialFrameSize > 0)
468 | rtmp->m_read.flags |= RTMP_READ_RESUME;
469 | rtmp->m_read.initialFrameType = initialFrameType;
470 | rtmp->m_read.nResumeTS = dSeek;
471 | rtmp->m_read.metaHeader = metaHeader;
472 | rtmp->m_read.initialFrame = initialFrame;
473 | rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize;
474 | rtmp->m_read.nInitialFrameSize = nInitialFrameSize;
475 |
476 | buffer = (char *) malloc(bufferSize);
477 |
478 | now = RTMP_GetTime();
479 | lastUpdate = now - 1000;
480 | do {
481 | nRead = RTMP_Read(rtmp, buffer, bufferSize);
482 | //RTMP_LogPrintf("nRead: %d\n", nRead);
483 | if (nRead > 0) {
484 | if (fwrite(buffer, sizeof(unsigned char), nRead, file)
485 | != (size_t) nRead) {
486 | RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!",
487 | __FUNCTION__);
488 | free(buffer);
489 | return RD_FAILED;
490 | }
491 | size += nRead;
492 |
493 | //RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
494 | if (duration <= 0)// if duration unknown try to get it from the stream (onMetaData)
495 | duration = RTMP_GetDuration(rtmp);
496 |
497 | if (duration > 0) {
498 | // make sure we claim to have enough buffer time!
499 | if (!bOverrideBufferTime && bufferTime < (duration * 1000.0)) {
500 | bufferTime = (uint32_t)(duration * 1000.0) + 5000;// extra 5sec to make sure we've got enough
501 |
502 | RTMP_Log(RTMP_LOGDEBUG,
503 | "Detected that buffer time is less than duration, resetting to: %dms",
504 | bufferTime);
505 | RTMP_SetBufferMS(rtmp, bufferTime);
506 | RTMP_UpdateBufferMS(rtmp);
507 | }
508 | *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0)
509 | * 100.0;
510 | *percent = ((double) (int) (*percent * 10.0)) / 10.0;
511 | if (bHashes) {
512 | if (lastPercent + 1 <= *percent) {
513 | RTMP_LogStatus("#");
514 | lastPercent = (unsigned long) *percent;
515 | }
516 | } else {
517 | now = RTMP_GetTime();
518 | if (abs(now - lastUpdate) > 200) {
519 | RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
520 | (double) size / 1024.0,
521 | (double) (rtmp->m_read.timestamp) / 1000.0,
522 | *percent);
523 | lastUpdate = now;
524 | }
525 | }
526 | } else {
527 | now = RTMP_GetTime();
528 | if (abs(now - lastUpdate) > 200) {
529 | if (bHashes)
530 | RTMP_LogStatus("#");
531 | else
532 | RTMP_LogStatus("\r%.3f kB / %.2f sec",
533 | (double) size / 1024.0,
534 | (double) (rtmp->m_read.timestamp) / 1000.0);
535 | lastUpdate = now;
536 | }
537 | }
538 | }
539 | #ifdef _DEBUG
540 | else
541 | {
542 | RTMP_Log(RTMP_LOGDEBUG, "zero read!");
543 | }
544 | #endif
545 |
546 | } while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp)
547 | && !RTMP_IsTimedout(rtmp));
548 | free(buffer);
549 | if (nRead < 0)
550 | nRead = rtmp->m_read.status;
551 |
552 | /* Final status update */
553 | if (!bHashes) {
554 | if (duration > 0) {
555 | *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0)
556 | * 100.0;
557 | *percent = ((double) (int) (*percent * 10.0)) / 10.0;
558 | RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double) size / 1024.0,
559 | (double) (rtmp->m_read.timestamp) / 1000.0, *percent);
560 | } else {
561 | RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0,
562 | (double) (rtmp->m_read.timestamp) / 1000.0);
563 | }
564 | }
565 |
566 | RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead);
567 |
568 | if (bResume && nRead == -2) {
569 | RTMP_LogPrintf("Couldn't resume FLV file, try --skip %d\n\n",
570 | nSkipKeyFrames + 1);
571 | return RD_FAILED;
572 | }
573 |
574 | if (nRead == -3)
575 | return RD_SUCCESS;
576 |
577 | if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0
578 | || RTMP_IsTimedout(rtmp)) {
579 | return RD_INCOMPLETE;
580 | }
581 |
582 | return RD_SUCCESS;
583 | }
584 |
585 | #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
586 |
587 | void usage(char *prog) {
588 | RTMP_LogPrintf(
589 | "\n%s: This program dumps the media content streamed over RTMP.\n\n",
590 | prog);
591 | RTMP_LogPrintf("--help|-h Prints this help screen.\n");
592 | RTMP_LogPrintf("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n");
593 | RTMP_LogPrintf(
594 | "--host|-n hostname Overrides the hostname in the rtmp url\n");
595 | RTMP_LogPrintf("--port|-c port Overrides the port in the rtmp url\n");
596 | RTMP_LogPrintf("--socks|-S host:port Use the specified SOCKS proxy\n");
597 | RTMP_LogPrintf(
598 | "--protocol|-l num Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n");
599 | RTMP_LogPrintf(
600 | "--playpath|-y path Overrides the playpath parsed from rtmp url\n");
601 | RTMP_LogPrintf("--playlist|-Y Set playlist before playing\n");
602 | RTMP_LogPrintf("--swfUrl|-s url URL to player swf file\n");
603 | RTMP_LogPrintf(
604 | "--tcUrl|-t url URL to played stream (default: \"rtmp://host[:port]/app\")\n");
605 | RTMP_LogPrintf("--pageUrl|-p url Web URL of played programme\n");
606 | RTMP_LogPrintf("--app|-a app Name of target app on server\n");
607 | #ifdef CRYPTO
608 | RTMP_LogPrintf(
609 | "--swfhash|-w hexstring SHA256 hash of the decompressed SWF file (32 bytes)\n");
610 | RTMP_LogPrintf(
611 | "--swfsize|-x num Size of the decompressed SWF file, required for SWFVerification\n");
612 | RTMP_LogPrintf(
613 | "--swfVfy|-W url URL to player swf file, compute hash/size automatically\n");
614 | RTMP_LogPrintf(
615 | "--swfAge|-X days Number of days to use cached SWF hash before refreshing\n");
616 | #endif
617 | RTMP_LogPrintf(
618 | "--auth|-u string Authentication string to be appended to the connect string\n");
619 | RTMP_LogPrintf(
620 | "--conn|-C type:data Arbitrary AMF data to be appended to the connect string\n");
621 | RTMP_LogPrintf(
622 | " B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n");
623 | RTMP_LogPrintf(
624 | " Z:(null), NB:name:boolean, NS:name:string, NN:name:number\n");
625 | RTMP_LogPrintf(
626 | "--flashVer|-f string Flash version string (default: \"%s\")\n",
627 | RTMP_DefaultFlashVer.av_val);
628 | RTMP_LogPrintf(
629 | "--live|-v Save a live stream, no --resume (seeking) of live streams possible\n");
630 | RTMP_LogPrintf(
631 | "--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
632 | RTMP_LogPrintf(
633 | "--realtime|-R Don't attempt to speed up download via the Pause/Unpause BUFX hack\n");
634 | RTMP_LogPrintf(
635 | "--flv|-o string FLV output file name, if the file name is - print stream to stdout\n");
636 | RTMP_LogPrintf("--resume|-e Resume a partial RTMP download\n");
637 | RTMP_LogPrintf(
638 | "--timeout|-m num Timeout connection num seconds (default: %u)\n",
639 | DEF_TIMEOUT);
640 | RTMP_LogPrintf(
641 | "--start|-A num Start at num seconds into stream (not valid when using --live)\n");
642 | RTMP_LogPrintf("--stop|-B num Stop at num seconds into stream\n");
643 | RTMP_LogPrintf("--token|-T key Key for SecureToken response\n");
644 | RTMP_LogPrintf(
645 | "--jtv|-j JSON Authentication token for Justin.tv legacy servers\n");
646 | RTMP_LogPrintf(
647 | "--hashes|-# Display progress with hashes, not with the byte counter\n");
648 | RTMP_LogPrintf(
649 | "--buffer|-b Buffer time in milliseconds (default: %u)\n",
650 | DEF_BUFTIME);
651 | RTMP_LogPrintf(
652 | "--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
653 | DEF_SKIPFRM);
654 | RTMP_LogPrintf("--quiet|-q Suppresses all command output.\n");
655 | RTMP_LogPrintf("--verbose|-V Verbose command output.\n");
656 | RTMP_LogPrintf("--debug|-z Debug level command output.\n");
657 | RTMP_LogPrintf(
658 | "If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect ");
659 | RTMP_LogPrintf("packet.\n\n");
660 | }
661 |
662 | int rtmpdump_main(int argc, char **argv) {
663 | extern char *optarg;
664 | extern int optind;
665 | //reset optind counter
666 | optind=1;
667 |
668 | int nStatus = RD_SUCCESS;
669 | double percent = 0;
670 | double duration = 0.0;
671 |
672 | int nSkipKeyFrames = DEF_SKIPFRM;// skip this number of keyframes when resuming
673 |
674 | int bOverrideBufferTime = FALSE;// if the user specifies a buffer time override this is true
675 | int bStdoutMode = TRUE; // if true print the stream directly to stdout, messages go to stderr
676 | int bResume = FALSE; // true in resume mode
677 | uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise
678 | uint32_t bufferTime = DEF_BUFTIME;
679 |
680 | // meta header and initial frame for the resume mode (they are read from the file and compared with
681 | // the stream we are trying to continue
682 | char *metaHeader = 0;
683 | uint32_t nMetaHeaderSize = 0;
684 |
685 | // video keyframe for matching
686 | char *initialFrame = 0;
687 | uint32_t nInitialFrameSize = 0;
688 | int initialFrameType = 0; // tye: audio or video
689 |
690 | AVal hostname = { 0, 0 };
691 | AVal playpath = { 0, 0 };
692 | AVal subscribepath = { 0, 0 };
693 | AVal usherToken = { 0, 0 }; //Justin.tv auth token
694 | int port = -1;
695 | int protocol = RTMP_PROTOCOL_UNDEFINED;
696 | int retries = 0;
697 | int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
698 | int bRealtimeStream = FALSE; // If true, disable the BUFX hack (be patient)
699 | int bHashes = FALSE; // display byte counters not hashes by default
700 |
701 | long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds
702 | uint32_t dStartOffset = 0; // seek position in non-live mode
703 | uint32_t dStopOffset = 0;
704 | RTMP rtmp = { 0 };
705 |
706 | AVal swfUrl = { 0, 0 };
707 | AVal tcUrl = { 0, 0 };
708 | AVal pageUrl = { 0, 0 };
709 | AVal app = { 0, 0 };
710 | AVal auth = { 0, 0 };
711 | AVal swfHash = { 0, 0 };
712 | uint32_t swfSize = 0;
713 | AVal flashVer = { 0, 0 };
714 | AVal sockshost = { 0, 0 };
715 |
716 | #ifdef CRYPTO
717 | int swfAge = 30; /* 30 days for SWF cache by default */
718 | int swfVfy = 0;
719 | unsigned char hash[RTMP_SWF_HASHLEN];
720 | #endif
721 |
722 | char *flvFile = 0;
723 |
724 | signal(SIGINT, sigIntHandler);
725 | signal(SIGTERM, sigIntHandler);
726 | #ifndef WIN32
727 | signal(SIGHUP, sigIntHandler);
728 | signal(SIGPIPE, sigIntHandler);
729 | signal(SIGQUIT, sigIntHandler);
730 | #endif
731 |
732 | RTMP_debuglevel = RTMP_LOGINFO;
733 |
734 | // Check for --quiet option before printing any output
735 | int index = 0;
736 | while (index < argc) {
737 | if (strcmp(argv[index], "--quiet") == 0 || strcmp(argv[index], "-q") == 0)
738 | RTMP_debuglevel = RTMP_LOGCRIT;
739 | index++;
740 | }
741 |
742 | RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
743 | RTMP_LogPrintf(
744 | "(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
745 |
746 | if (!InitSockets()) {
747 | RTMP_Log(RTMP_LOGERROR,
748 | "Couldn't load sockets support on your platform, exiting!");
749 | return RD_FAILED;
750 | }
751 |
752 | /* sleep(30); */
753 |
754 | RTMP_Init(&rtmp);
755 |
756 | int opt;
757 | struct option longopts[] = { { "help", 0, NULL, 'h' }, { "host", 1, NULL, 'n' },
758 | { "port", 1, NULL, 'c' }, { "socks", 1, NULL, 'S' }, { "protocol", 1,
759 | NULL, 'l' }, { "playpath", 1, NULL, 'y' }, { "playlist", 0,
760 | NULL, 'Y' }, { "rtmp", 1, NULL, 'r' },
761 | { "swfUrl", 1, NULL, 's' }, { "tcUrl", 1, NULL, 't' }, { "pageUrl", 1,
762 | NULL, 'p' }, { "app", 1, NULL, 'a' }, { "auth", 1, NULL, 'u' },
763 | { "conn", 1, NULL, 'C' },
764 | #ifdef CRYPTO
765 | { "swfhash", 1, NULL, 'w' }, { "swfsize", 1, NULL, 'x' }, { "swfVfy", 1,
766 | NULL, 'W' }, { "swfAge", 1, NULL, 'X' },
767 | #endif
768 | { "flashVer", 1, NULL, 'f' }, { "live", 0, NULL, 'v' }, { "realtime", 0,
769 | NULL, 'R' }, { "flv", 1, NULL, 'o' },
770 | { "resume", 0, NULL, 'e' }, { "timeout", 1, NULL, 'm' }, { "buffer", 1,
771 | NULL, 'b' }, { "skip", 1, NULL, 'k' }, { "subscribe", 1, NULL,
772 | 'd' }, { "start", 1, NULL, 'A' }, { "stop", 1, NULL, 'B' }, {
773 | "token", 1, NULL, 'T' }, { "hashes", 0, NULL, '#' }, { "debug",
774 | 0, NULL, 'z' }, { "quiet", 0, NULL, 'q' }, { "verbose", 0, NULL,
775 | 'V' }, { "jtv", 1, NULL, 'j' }, { 0, 0, 0, 0 } };
776 |
777 | while ((opt = getopt_long(argc, argv,
778 | "hVveqzRr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
779 | longopts, NULL)) != -1) {
780 | switch (opt) {
781 | case 'h':
782 | usage(argv[0]);
783 | return RD_SUCCESS;
784 | #ifdef CRYPTO
785 | case 'w': {
786 | int res = hex2bin(optarg, &swfHash.av_val);
787 | if (res != RTMP_SWF_HASHLEN) {
788 | swfHash.av_val = NULL;
789 | RTMP_Log(RTMP_LOGWARNING,
790 | "Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!",
791 | RTMP_SWF_HASHLEN);
792 | }
793 | swfHash.av_len = RTMP_SWF_HASHLEN;
794 | break;
795 | }
796 | case 'x': {
797 | int size = atoi(optarg);
798 | if (size <= 0) {
799 | RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n");
800 | } else {
801 | swfSize = size;
802 | }
803 | break;
804 | }
805 | case 'W':
806 | STR2AVAL(swfUrl, optarg);
807 | swfVfy = 1;
808 | break;
809 | case 'X': {
810 | int num = atoi(optarg);
811 | if (num < 0) {
812 | RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n");
813 | } else {
814 | swfAge = num;
815 | }
816 | }
817 | break;
818 | #endif
819 | case 'k':
820 | nSkipKeyFrames = atoi(optarg);
821 | if (nSkipKeyFrames < 0) {
822 | RTMP_Log(RTMP_LOGERROR,
823 | "Number of keyframes skipped must be greater or equal zero, using zero!");
824 | nSkipKeyFrames = 0;
825 | } else {
826 | RTMP_Log(RTMP_LOGDEBUG,
827 | "Number of skipped key frames for resume: %d",
828 | nSkipKeyFrames);
829 | }
830 | break;
831 | case 'b': {
832 | int32_t bt = atol(optarg);
833 | if (bt < 0) {
834 | RTMP_Log(RTMP_LOGERROR,
835 | "Buffer time must be greater than zero, ignoring the specified value %d!",
836 | bt);
837 | } else {
838 | bufferTime = bt;
839 | bOverrideBufferTime = TRUE;
840 | }
841 | break;
842 | }
843 | case 'v':
844 | bLiveStream = TRUE; // no seeking or resuming possible!
845 | break;
846 | case 'R':
847 | bRealtimeStream = TRUE; // seeking and resuming is still possible
848 | break;
849 | case 'd':
850 | STR2AVAL(subscribepath, optarg);
851 | break;
852 | case 'n':
853 | STR2AVAL(hostname, optarg);
854 | break;
855 | case 'c':
856 | port = atoi(optarg);
857 | break;
858 | case 'l':
859 | protocol = atoi(optarg);
860 | if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) {
861 | RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol);
862 | return RD_FAILED;
863 | }
864 | break;
865 | case 'y':
866 | STR2AVAL(playpath, optarg);
867 | break;
868 | case 'Y':
869 | RTMP_SetOpt(&rtmp, &av_playlist, (AVal *) &av_true);
870 | break;
871 | case 'r': {
872 | AVal parsedHost, parsedApp, parsedPlaypath;
873 | unsigned int parsedPort = 0;
874 | int parsedProtocol = RTMP_PROTOCOL_UNDEFINED;
875 |
876 | if (!RTMP_ParseURL(optarg, &parsedProtocol, &parsedHost, &parsedPort,
877 | &parsedPlaypath, &parsedApp)) {
878 | RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!",
879 | optarg);
880 | } else {
881 | if (!hostname.av_len)
882 | hostname = parsedHost;
883 | if (port == -1)
884 | port = parsedPort;
885 | if (playpath.av_len == 0 && parsedPlaypath.av_len) {
886 | playpath = parsedPlaypath;
887 | }
888 | if (protocol == RTMP_PROTOCOL_UNDEFINED)
889 | protocol = parsedProtocol;
890 | if (app.av_len == 0 && parsedApp.av_len) {
891 | app = parsedApp;
892 | }
893 | }
894 | break;
895 | }
896 | case 's':
897 | STR2AVAL(swfUrl, optarg);
898 | break;
899 | case 't':
900 | STR2AVAL(tcUrl, optarg);
901 | break;
902 | case 'p':
903 | STR2AVAL(pageUrl, optarg);
904 | break;
905 | case 'a':
906 | STR2AVAL(app, optarg);
907 | break;
908 | case 'f':
909 | STR2AVAL(flashVer, optarg);
910 | break;
911 | case 'o':
912 | flvFile = optarg;
913 | if (strcmp(flvFile, "-"))
914 | bStdoutMode = FALSE;
915 |
916 | break;
917 | case 'e':
918 | bResume = TRUE;
919 | break;
920 | case 'u':
921 | STR2AVAL(auth, optarg);
922 | break;
923 | case 'C': {
924 | AVal av;
925 | STR2AVAL(av, optarg);
926 | if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) {
927 | RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg);
928 | return RD_FAILED;
929 | }
930 | }
931 | break;
932 | case 'm':
933 | timeout = atoi(optarg);
934 | break;
935 | case 'A':
936 | dStartOffset = (int) (atof(optarg) * 1000.0);
937 | break;
938 | case 'B':
939 | dStopOffset = (int) (atof(optarg) * 1000.0);
940 | break;
941 | case 'T': {
942 | AVal token;
943 | STR2AVAL(token, optarg);
944 | RTMP_SetOpt(&rtmp, &av_token, &token);
945 | }
946 | break;
947 | case '#':
948 | bHashes = TRUE;
949 | break;
950 | case 'q':
951 | RTMP_debuglevel = RTMP_LOGCRIT;
952 | break;
953 | case 'V':
954 | RTMP_debuglevel = RTMP_LOGDEBUG;
955 | break;
956 | case 'z':
957 | RTMP_debuglevel = RTMP_LOGALL;
958 | break;
959 | case 'S':
960 | STR2AVAL(sockshost, optarg);
961 | break;
962 | case 'j':
963 | STR2AVAL(usherToken, optarg);
964 | break;
965 | default:
966 | RTMP_LogPrintf("unknown option: %c\n", opt);
967 | usage(argv[0]);
968 | return RD_FAILED;
969 | break;
970 | }
971 | }
972 |
973 | if (!hostname.av_len) {
974 | RTMP_Log(RTMP_LOGERROR,
975 | "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
976 | return RD_FAILED;
977 | }
978 | if (playpath.av_len == 0) {
979 | RTMP_Log(RTMP_LOGERROR,
980 | "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
981 | return RD_FAILED;
982 | }
983 |
984 | if (protocol == RTMP_PROTOCOL_UNDEFINED) {
985 | RTMP_Log(RTMP_LOGWARNING,
986 | "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
987 | protocol = RTMP_PROTOCOL_RTMP;
988 | }
989 | if (port == -1) {
990 | RTMP_Log(RTMP_LOGWARNING,
991 | "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
992 | port = 0;
993 | }
994 | if (port == 0) {
995 | if (protocol & RTMP_FEATURE_SSL)
996 | port = 443;
997 | else if (protocol & RTMP_FEATURE_HTTP)
998 | port = 80;
999 | else
1000 | port = 1935;
1001 | }
1002 |
1003 | if (flvFile == 0) {
1004 | RTMP_Log(RTMP_LOGWARNING,
1005 | "You haven't specified an output file (-o filename), using stdout");
1006 | bStdoutMode = TRUE;
1007 | }
1008 |
1009 | if (bStdoutMode && bResume) {
1010 | RTMP_Log(RTMP_LOGWARNING,
1011 | "Can't resume in stdout mode, ignoring --resume option");
1012 | bResume = FALSE;
1013 | }
1014 |
1015 | if (bLiveStream && bResume) {
1016 | RTMP_Log(RTMP_LOGWARNING,
1017 | "Can't resume live stream, ignoring --resume option");
1018 | bResume = FALSE;
1019 | }
1020 |
1021 | #ifdef CRYPTO
1022 | if (swfVfy) {
1023 | if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0) {
1024 | swfHash.av_val = (char *) hash;
1025 | swfHash.av_len = RTMP_SWF_HASHLEN;
1026 | }
1027 | }
1028 |
1029 | if (swfHash.av_len == 0 && swfSize > 0) {
1030 | RTMP_Log(RTMP_LOGWARNING,
1031 | "Ignoring SWF size, supply also the hash with --swfhash");
1032 | swfSize = 0;
1033 | }
1034 |
1035 | if (swfHash.av_len != 0 && swfSize == 0) {
1036 | RTMP_Log(RTMP_LOGWARNING,
1037 | "Ignoring SWF hash, supply also the swf size with --swfsize");
1038 | swfHash.av_len = 0;
1039 | swfHash.av_val = NULL;
1040 | }
1041 | #endif
1042 |
1043 | if (tcUrl.av_len == 0) {
1044 | tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + hostname.av_len
1045 | + app.av_len + sizeof("://:65535/");
1046 | tcUrl.av_val = (char *) malloc(tcUrl.av_len);
1047 | if (!tcUrl.av_val)
1048 | return RD_FAILED;
1049 | tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
1050 | RTMPProtocolStringsLower[protocol], hostname.av_len,
1051 | hostname.av_val, port, app.av_len, app.av_val);
1052 | }
1053 |
1054 | int first = 1;
1055 |
1056 | // User defined seek offset
1057 | if (dStartOffset > 0) {
1058 | // Live stream
1059 | if (bLiveStream) {
1060 | RTMP_Log(RTMP_LOGWARNING,
1061 | "Can't seek in a live stream, ignoring --start option");
1062 | dStartOffset = 0;
1063 | }
1064 | }
1065 |
1066 | RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
1067 | &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer,
1068 | &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
1069 |
1070 | /* Try to keep the stream moving if it pauses on us */
1071 | if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
1072 | rtmp.Link.lFlags |= RTMP_LF_BUFX;
1073 |
1074 | off_t size = 0;
1075 |
1076 | // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
1077 | if (bResume) {
1078 | nStatus = OpenResumeFile(flvFile, &file, &size, &metaHeader,
1079 | &nMetaHeaderSize, &duration);
1080 | if (nStatus == RD_FAILED)
1081 | goto clean;
1082 |
1083 | if (!file) {
1084 | // file does not exist, so go back into normal mode
1085 | bResume = FALSE; // we are back in fresh file mode (otherwise finalizing file won't be done)
1086 | } else {
1087 | nStatus = GetLastKeyframe(file, nSkipKeyFrames, &dSeek, &initialFrame,
1088 | &initialFrameType, &nInitialFrameSize);
1089 | if (nStatus == RD_FAILED) {
1090 | RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe.");
1091 | goto clean;
1092 | }
1093 |
1094 | if (dSeek == 0) {
1095 | RTMP_Log(RTMP_LOGDEBUG,
1096 | "Last keyframe is first frame in stream, switching from resume to normal mode!");
1097 | bResume = FALSE;
1098 | }
1099 | }
1100 | }
1101 |
1102 | if (!file) {
1103 | if (bStdoutMode) {
1104 | file = stdout;
1105 | SET_BINMODE(file);
1106 | } else {
1107 | file = fopen(flvFile, "w+b");
1108 | if (file == 0) {
1109 | RTMP_LogPrintf("Failed to open file! %s\n", flvFile);
1110 | return RD_FAILED;
1111 | }
1112 | }
1113 | }
1114 |
1115 | #ifdef _DEBUG
1116 | netstackdump = fopen("netstackdump", "wb");
1117 | netstackdump_read = fopen("netstackdump_read", "wb");
1118 | #endif
1119 |
1120 | while (!RTMP_ctrlC) {
1121 | RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
1122 | RTMP_SetBufferMS(&rtmp, bufferTime);
1123 |
1124 | if (first) {
1125 | first = 0;
1126 | RTMP_LogPrintf("Connecting ...\n");
1127 |
1128 | if (!RTMP_Connect(&rtmp, NULL)) {
1129 | nStatus = RD_NO_CONNECT;
1130 | break;
1131 | }
1132 |
1133 | RTMP_Log(RTMP_LOGINFO, "Connected...");
1134 |
1135 | // User defined seek offset
1136 | if (dStartOffset > 0) {
1137 | // Don't need the start offset if resuming an existing file
1138 | if (bResume) {
1139 | RTMP_Log(RTMP_LOGWARNING,
1140 | "Can't seek a resumed stream, ignoring --start option");
1141 | dStartOffset = 0;
1142 | } else {
1143 | dSeek = dStartOffset;
1144 | }
1145 | }
1146 |
1147 | // Calculate the length of the stream to still play
1148 | if (dStopOffset > 0) {
1149 | // Quit if start seek is past required stop offset
1150 | if (dStopOffset <= dSeek) {
1151 | RTMP_LogPrintf("Already Completed\n");
1152 | nStatus = RD_SUCCESS;
1153 | break;
1154 | }
1155 | }
1156 |
1157 | if (!RTMP_ConnectStream(&rtmp, dSeek)) {
1158 | nStatus = RD_FAILED;
1159 | break;
1160 | }
1161 | } else {
1162 | nInitialFrameSize = 0;
1163 |
1164 | if (retries) {
1165 | RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
1166 | if (!RTMP_IsTimedout(&rtmp))
1167 | nStatus = RD_FAILED;
1168 | else
1169 | nStatus = RD_INCOMPLETE;
1170 | break;
1171 | }
1172 | RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n");
1173 | /* Did we already try pausing, and it still didn't work? */
1174 | if (rtmp.m_pausing == 3) {
1175 | /* Only one try at reconnecting... */
1176 | retries = 1;
1177 | dSeek = rtmp.m_pauseStamp;
1178 | if (dStopOffset > 0) {
1179 | if (dStopOffset <= dSeek) {
1180 | RTMP_LogPrintf("Already Completed\n");
1181 | nStatus = RD_SUCCESS;
1182 | break;
1183 | }
1184 | }
1185 | if (!RTMP_ReconnectStream(&rtmp, dSeek)) {
1186 | RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
1187 | if (!RTMP_IsTimedout(&rtmp))
1188 | nStatus = RD_FAILED;
1189 | else
1190 | nStatus = RD_INCOMPLETE;
1191 | break;
1192 | }
1193 | } else if (!RTMP_ToggleStream(&rtmp)) {
1194 | RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
1195 | if (!RTMP_IsTimedout(&rtmp))
1196 | nStatus = RD_FAILED;
1197 | else
1198 | nStatus = RD_INCOMPLETE;
1199 | break;
1200 | }
1201 | bResume = TRUE;
1202 | }
1203 |
1204 | nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume,
1205 | metaHeader, nMetaHeaderSize, initialFrame, initialFrameType,
1206 | nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream,
1207 | bRealtimeStream, bHashes, bOverrideBufferTime, bufferTime,
1208 | &percent);
1209 | free(initialFrame);
1210 | initialFrame = NULL;
1211 |
1212 | /* If we succeeded, we're done.
1213 | */
1214 | if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream)
1215 | break;
1216 | }
1217 |
1218 | if (nStatus == RD_SUCCESS) {
1219 | RTMP_LogPrintf("Download complete\n");
1220 | } else if (nStatus == RD_INCOMPLETE) {
1221 | RTMP_LogPrintf(
1222 | "Download may be incomplete (downloaded about %.2f%%), try resuming\n",
1223 | percent);
1224 | }
1225 |
1226 | clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n");
1227 | RTMP_Close(&rtmp);
1228 |
1229 | if (file != 0) {
1230 | fclose(file);
1231 | file = 0;
1232 | }
1233 | CleanupSockets();
1234 |
1235 | #ifdef _DEBUG
1236 | if (netstackdump != 0)
1237 | fclose(netstackdump);
1238 | if (netstackdump_read != 0)
1239 | fclose(netstackdump_read);
1240 | #endif
1241 | return nStatus;
1242 | }
1243 |
--------------------------------------------------------------------------------
/app/src/main/jni/jniinterface.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #define TAG "RtmpdumpNative"
9 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
10 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
11 |
12 | #ifdef __cplusplus
13 | extern "C" {
14 | #endif
15 |
16 | void callback_handler(const char *s);
17 | extern int rtmpdump_main(int argc, char **argv);
18 |
19 | static JavaVM *sVm;
20 | static jobject clazz;
21 |
22 | void initClassHelper(JNIEnv *env, const char *path, jobject *objptr) {
23 | jclass cls = env->FindClass(path);
24 | if (!cls) {
25 | LOGE("initClassHelper: failed to get %s class reference", path);
26 | return;
27 | }
28 | jmethodID constr = env->GetMethodID(cls, "", "()V");
29 | if (!constr) {
30 | LOGE("initClassHelper: failed to get %s constructor", path);
31 | return;
32 | }
33 | jobject obj = env->NewObject(cls, constr);
34 | if (!obj) {
35 | LOGE("initClassHelper: failed to create a %s object", path);
36 | return;
37 | }
38 | (*objptr) = env->NewGlobalRef(obj);
39 | }
40 |
41 | void callback_handler(const char *s) {
42 | int status;
43 | JNIEnv *env;
44 | bool isAttached = false;
45 | status = sVm->GetEnv((void **) &env, JNI_VERSION_1_4);
46 | if (status < 0) {
47 | LOGE("callback_handler: failed to get JNI environment, "
48 | "assuming native thread");
49 | status = sVm->AttachCurrentThread(&env, NULL);
50 | if (status < 0) {
51 | LOGE("callback_handler: failed to attach "
52 | "current thread");
53 | return;
54 | }
55 | isAttached = true;
56 | }
57 |
58 | /* Construct a Java string */
59 | jstring js = env->NewStringUTF(s);
60 | if (!js) {
61 | LOGE("String is leeg");
62 | }
63 | jclass interfaceClass = env->GetObjectClass(clazz);
64 | if (interfaceClass == NULL) {
65 | LOGE("callback_handler: failed to get class reference");
66 | if (isAttached)
67 | sVm->DetachCurrentThread();
68 | return;
69 | }
70 | //Ljava/lang/String;
71 | /* Find the callBack method ID */
72 | jmethodID method = env->GetStaticMethodID(interfaceClass, "testCallback",
73 | "(Ljava/lang/String;)V");
74 | if (!method) {
75 | LOGE("callback_handler: failed to get method ID");
76 | if (isAttached)
77 | sVm->DetachCurrentThread();
78 | return;
79 | }
80 |
81 | env->CallStaticVoidMethod(interfaceClass, method, js);
82 |
83 | if (isAttached)
84 | sVm->DetachCurrentThread();
85 | }
86 |
87 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
88 | LOGV("Loading native library compiled at " __TIME__ " " __DATE__);
89 |
90 | const char* path = "com/schriek/rtmpdump/callBack";
91 |
92 | sVm = jvm;
93 | JNIEnv *env;
94 |
95 | if (jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
96 | LOGE("Failed to get the environment using GetEnv()");
97 | return -1;
98 | }
99 |
100 | initClassHelper(env, path, &clazz);
101 |
102 | return JNI_VERSION_1_4;
103 | }
104 |
105 | JNIEXPORT void JNICALL Java_com_schriek_rtmpdump_Rtmpdump_run(JNIEnv *env, jobject obj, jobjectArray args)
106 | {
107 | LOGV("run() called");
108 | int i = 0;
109 | int argc = 0;
110 | char **argv = NULL;
111 |
112 | if (args != NULL) {
113 | argc = env->GetArrayLength(args);
114 | argv = (char **) malloc(sizeof(char *) * argc);
115 |
116 | for(i=0;iGetObjectArrayElement(args, i);
119 | argv[i] = (char *)env->GetStringUTFChars(str, NULL);
120 | }
121 | }
122 |
123 | LOGV("run passing off to main()");
124 | rtmpdump_main(argc, argv);
125 | }
126 |
127 | void Java_com_schriek_rtmpdump_Rtmpdump_testNative(JNIEnv* env,
128 | jobject javaThis) {
129 |
130 | LOGV("testNative() called!");
131 |
132 | callback_handler("Loading native library compiled at " __TIME__ " " __DATE__);
133 | }
134 |
135 | void Java_com_schriek_rtmpdump_Rtmpdump_stop(JNIEnv* env, jobject javaThis) {
136 | LOGV("stop() called!");
137 |
138 | /* probably not the best way to do this */
139 | exit(0);
140 | }
141 |
142 | #ifdef __cplusplus
143 | }
144 | #endif
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-hdpi/ic_action_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-mdpi/ic_action_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-xhdpi/ic_action_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/commandfragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/helpfragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | rtmpdump
4 | Help
5 | Command
6 | Hello world!
7 | Settings
8 | MainActivity
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle-experimental:0.6.0-alpha2'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eschriek/rtmpdump-android/2b231636359e61e9ca7baecfd6337cf66e5b7a83/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
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-2.9-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-15
15 |
--------------------------------------------------------------------------------
/rtmpdump-android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------