├── .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 | 8 | 9 | 10 | 11 | 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 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |