diff --git a/.idea/.name b/.idea/.name index 99bfd3e..a2ffb41 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -VideoStreaming \ No newline at end of file +VideoStream \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 51d40c0..8dc6a51 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -8,6 +8,8 @@ + - - @@ -735,369 +2292,427 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - + + - - + + - + + + + + + - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + @@ -1107,13 +2722,15 @@ - - - - - + + + + + + + - + + + + + + + + + - - + + + + + - + + + - + + - - - - - - @@ -1359,56 +3018,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1445,9 +3054,7 @@ - - - + @@ -1468,15 +3075,15 @@ + + + - - - @@ -1529,9 +3136,7 @@ - - - + @@ -1544,15 +3149,15 @@ + + + - - - @@ -1607,19 +3212,12 @@ - - - - - - - - - + + @@ -1639,15 +3237,15 @@ + + + - - - @@ -1721,8 +3319,127 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VideoStream.iml b/VideoStream.iml new file mode 100644 index 0000000..135c0f9 --- /dev/null +++ b/VideoStream.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/rtmp_video_publisher/.gitignore b/rtmp_video_publisher/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/rtmp_video_publisher/.gitignore @@ -0,0 +1 @@ +/build diff --git a/rtmp_video_publisher/build.gradle b/rtmp_video_publisher/build.gradle new file mode 100644 index 0000000..7349cc3 --- /dev/null +++ b/rtmp_video_publisher/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 'android-L' + buildToolsVersion "20.0.0" + + defaultConfig { + applicationId "com.c77.videopublisher.rtmp_video_publisher" + minSdkVersion 15 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile 'org.red5:android-rmtp-client:0.3' +} diff --git a/rtmp_video_publisher/proguard-rules.pro b/rtmp_video_publisher/proguard-rules.pro new file mode 100644 index 0000000..0e13397 --- /dev/null +++ b/rtmp_video_publisher/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /opt/android-studio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/rtmp_video_publisher/rtmp_video_publisher.iml b/rtmp_video_publisher/rtmp_video_publisher.iml new file mode 100644 index 0000000..0e3ed7d --- /dev/null +++ b/rtmp_video_publisher/rtmp_video_publisher.iml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtmp_video_publisher/src/androidTest/java/com/c77/videopublisher/rtmp_video_publisher/ApplicationTest.java b/rtmp_video_publisher/src/androidTest/java/com/c77/videopublisher/rtmp_video_publisher/ApplicationTest.java new file mode 100644 index 0000000..410e4bb --- /dev/null +++ b/rtmp_video_publisher/src/androidTest/java/com/c77/videopublisher/rtmp_video_publisher/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.c77.videopublisher.rtmp_video_publisher; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/rtmp_video_publisher/src/main/AndroidManifest.xml b/rtmp_video_publisher/src/main/AndroidManifest.xml new file mode 100644 index 0000000..dccf5f7 --- /dev/null +++ b/rtmp_video_publisher/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/rtmp_video_publisher/src/main/ic_launcher-web.png b/rtmp_video_publisher/src/main/ic_launcher-web.png new file mode 100644 index 0000000..4b14629 Binary files /dev/null and b/rtmp_video_publisher/src/main/ic_launcher-web.png differ diff --git a/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/VideoPublisherMainActivity.java b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/VideoPublisherMainActivity.java new file mode 100644 index 0000000..f1ceb57 --- /dev/null +++ b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/VideoPublisherMainActivity.java @@ -0,0 +1,89 @@ +package com.c77.videopublisher.rtmp_video_publisher; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.c77.videopublisher.rtmp_video_publisher.manager.ClientManager; + +public class VideoPublisherMainActivity extends Activity implements View.OnClickListener { + + public static final int STOPPED = 0; + public static final int RECORDING = 1; + + ClientManager clientManager = new ClientManager(); + Button startButton = null; + Button stopButton = null; + Button exitButon = null; + //TextView textView = null; + int status = STOPPED; + + public void onClick(View v) { + if (v == startButton) { + this.setTitle("Started!"); + if(status == STOPPED){ + clientManager.setRecording(true); + status = RECORDING; + } + } + if (v == stopButton) { + this.setTitle("stoped"); + if(status == RECORDING){ + clientManager.setRecording(false); + status = STOPPED; + } + } + if (v == exitButon) { + clientManager.setRecording(false); + clientManager.setRunning(false); + System.exit(0); + } + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + startButton = new Button(this); + stopButton = new Button(this); + exitButon = new Button(this); + //textView = new TextView(this); + + startButton.setText("Start"); + stopButton.setText("Stop"); + exitButon.setText("Exit"); + /*textView.setText("Android Recorder ChangeLog£º" + + "\n(1)Get PCM data." + + "\n(2)Encode with speex." + + "\n(3)Package in flv format" + + "\n(4)Publish audio to server." + + "\n(5)Record both local and server side.");*/ + + startButton.setOnClickListener(this); + stopButton.setOnClickListener(this); + exitButon.setOnClickListener(this); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + //layout.addView(textView); + layout.addView(startButton); + layout.addView(stopButton); + layout.addView(exitButon); + this.setContentView(layout); + + clientManager.setMode(ClientManager.NETANDFILE); + clientManager.setRunning(true); + Thread cmThread = new Thread(clientManager); + cmThread.start(); + + } + + @Override + protected void onStart() { + super.onStart(); + } +} \ No newline at end of file diff --git a/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/Consumer.java b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/Consumer.java new file mode 100644 index 0000000..c36d4a2 --- /dev/null +++ b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/Consumer.java @@ -0,0 +1,10 @@ +package com.c77.videopublisher.rtmp_video_publisher.io; + +public interface Consumer { + + public void putData(long ts, byte[] buf, int size); + + public void setRecording(boolean isRecording); + + public boolean isRecording(); +} diff --git a/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/file/FileClient.java b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/file/FileClient.java new file mode 100644 index 0000000..a9f80c8 --- /dev/null +++ b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/file/FileClient.java @@ -0,0 +1,110 @@ +package com.c77.videopublisher.rtmp_video_publisher.io.file; + +import java.io.File; +import org.apache.mina.core.buffer.IoBuffer; +import org.red5.io.IStreamableFile; +import org.red5.io.ITagWriter; +import org.red5.io.IoConstants; +import org.red5.io.flv.FLVService; +import org.red5.io.flv.Tag; +import org.red5.server.net.rtmp.message.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A client write tags to local file. + */ +public class FileClient implements Constants { + private static Logger log = LoggerFactory.getLogger(FileClient.class); + + private String saveAsFileName; + private ITagWriter tagWriter; + private int prevSize = 0; + private Tag tag; + private int currentTime = 0; + private long timeBase = 0; + private int sampleRate = 0; + private int channle; + + public FileClient() { + + } + + public void start(String saveAsFileName) { + this.saveAsFileName = saveAsFileName; + init(); + } + + private void init() { + File file = new File(saveAsFileName); + FLVService flvService = new FLVService(); + flvService.setGenerateMetadata(true); + try { + IStreamableFile flv = flvService.getStreamableFile(file); + tagWriter = flv.getWriter(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void stop() { + if (tagWriter != null) { + tagWriter.close(); + tagWriter = null; + } + log.debug("writer closed!"); + } + + public void writeTag(byte[] buf, int size, long ts) { + if (timeBase == 0) { + timeBase = ts; + } + currentTime = (int) (ts - timeBase); + + tag = new Tag(IoConstants.TYPE_AUDIO, currentTime, size + 1, null, + prevSize); + prevSize = size + 1; + + byte tagType = (byte) ((IoConstants.FLAG_FORMAT_SPEEX << 4)) + | (IoConstants.FLAG_SIZE_16_BIT << 1); + + switch (sampleRate) { + case 44100: + tagType |= IoConstants.FLAG_RATE_44_KHZ << 2; + break; + case 22050: + tagType |= IoConstants.FLAG_RATE_22_KHZ << 2; + break; + case 11025: + tagType |= IoConstants.FLAG_RATE_11_KHZ << 2; + break; + default: + tagType |= IoConstants.FLAG_RATE_5_5_KHZ << 2; + } + + tagType |= (channle == 2 ? IoConstants.FLAG_TYPE_STEREO + : IoConstants.FLAG_TYPE_MONO); + + IoBuffer body = IoBuffer.allocate(tag.getBodySize()); + body.setAutoExpand(true); + body.put(tagType); + body.put(buf); + body.flip(); + body.limit(size + 1); + tag.setBody(body); + + try { + tagWriter.writeTag(tag); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + } + + public void setChannle(int channle) { + this.channle = channle; + } +} diff --git a/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/net/NetClient.java b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/net/NetClient.java new file mode 100644 index 0000000..3c8cef1 --- /dev/null +++ b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/io/net/NetClient.java @@ -0,0 +1,235 @@ +package com.c77.videopublisher.rtmp_video_publisher.io.net; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.mina.core.buffer.IoBuffer; +import org.red5.io.IoConstants; +import org.red5.io.flv.Tag; +import org.red5.io.utils.ObjectMap; +import org.red5.server.messaging.IMessage; +import org.red5.server.net.rtmp.INetStreamEventHandler; +import org.red5.server.net.rtmp.RTMPClient; +import org.red5.server.net.rtmp.event.AudioData; +import org.red5.server.net.rtmp.event.FlexStreamSend; +import org.red5.server.net.rtmp.event.IRTMPEvent; +import org.red5.server.net.rtmp.event.Invoke; +import org.red5.server.net.rtmp.event.Notify; +import org.red5.server.net.rtmp.event.Unknown; +import org.red5.server.net.rtmp.event.VideoData; +import org.red5.server.net.rtmp.message.Constants; +import org.red5.server.net.rtmp.status.StatusCodes; +import org.red5.server.service.IPendingServiceCall; +import org.red5.server.service.IPendingServiceCallback; +import org.red5.server.stream.message.RTMPMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A publish client to publish stream to server. + */ +public class NetClient implements INetStreamEventHandler, + IPendingServiceCallback { + + private static Logger log = LoggerFactory.getLogger(NetClient.class); + + private List frameBuffer = new ArrayList(); + + public static final int STOPPED = 0; + + public static final int CONNECTING = 1; + + public static final int STREAM_CREATING = 2; + + public static final int PUBLISHING = 3; + + public static final int PUBLISHED = 4; + + private String host; + + private int port; + + private String app; + + private int state; + + private String publishName; + + private int streamId; + + private String publishMode; + + private RTMPClient rtmpClient; + + private int prevSize = 0; + + private Tag tag; + + private int currentTime = 0; + + private long timeBase = 0; + + private int sampleRate = 0; + + private int channle; + + public int getState() { + return state; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public void setApp(String app) { + this.app = app; + } + + public void setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + } + + public void setChannle(int channle) { + this.channle = channle; + } + + public synchronized void start(String publishName, String publishMode, + Object[] params) { + state = CONNECTING; + this.publishName = publishName; + this.publishMode = publishMode; + rtmpClient = new RTMPClient(); + + Map defParams = rtmpClient.makeDefaultConnectionParams( + host, port, app); + rtmpClient.connect(host, port, defParams, this, params); + } + + public synchronized void stop() { + if (state >= STREAM_CREATING) { + rtmpClient.disconnect(); + } + state = STOPPED; + } + + public void writeTag(byte[] buf, int size, long ts) { + if (timeBase == 0) { + timeBase = ts; + } + currentTime = (int) (ts - timeBase); + tag = new Tag(IoConstants.TYPE_AUDIO, currentTime, size + 1, null, + prevSize); + prevSize = size + 1; + + byte tagType = (byte) ((IoConstants.FLAG_FORMAT_SPEEX << 4)) + | (IoConstants.FLAG_SIZE_16_BIT << 1); + switch (sampleRate) { + case 44100: + tagType |= IoConstants.FLAG_RATE_44_KHZ << 2; + break; + case 22050: + tagType |= IoConstants.FLAG_RATE_22_KHZ << 2; + break; + case 11025: + tagType |= IoConstants.FLAG_RATE_11_KHZ << 2; + break; + default: + tagType |= IoConstants.FLAG_RATE_5_5_KHZ << 2; + } + + tagType |= (channle == 2 ? IoConstants.FLAG_TYPE_STEREO + : IoConstants.FLAG_TYPE_MONO); + + IoBuffer body = IoBuffer.allocate(tag.getBodySize()); + body.setAutoExpand(true); + body.put(tagType); + body.put(buf); + body.flip(); + body.limit(tag.getBodySize()); + tag.setBody(body); + + IMessage msg = makeMessageFromTag(tag); + try { + pushMessage(msg); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public IMessage makeMessageFromTag(Tag tag) { + IRTMPEvent msg = null; + switch (tag.getDataType()) { + case Constants.TYPE_AUDIO_DATA: + msg = new AudioData(tag.getBody()); + break; + case Constants.TYPE_VIDEO_DATA: + msg = new VideoData(tag.getBody()); + break; + case Constants.TYPE_INVOKE: + msg = new Invoke(tag.getBody()); + break; + case Constants.TYPE_NOTIFY: + msg = new Notify(tag.getBody()); + break; + case Constants.TYPE_FLEX_STREAM_SEND: + msg = new FlexStreamSend(tag.getBody()); + break; + default: + log.warn("Unexpected type? {}", tag.getDataType()); + msg = new Unknown(tag.getDataType(), tag.getBody()); + } + msg.setTimestamp(tag.getTimestamp()); + RTMPMessage rtmpMsg = new RTMPMessage(); + rtmpMsg.setBody(msg); + rtmpMsg.getBody(); + return rtmpMsg; + } + + synchronized public void pushMessage(IMessage message) throws IOException { + if (state >= PUBLISHED && message instanceof RTMPMessage) { + RTMPMessage rtmpMsg = (RTMPMessage) message; + rtmpClient.publishStreamData(streamId, rtmpMsg); + } else { + frameBuffer.add(message); + } + } + + public synchronized void onStreamEvent(Notify notify) { + log.debug("onStreamEvent: {}", notify); + ObjectMap map = (ObjectMap) notify.getCall().getArguments()[0]; + String code = (String) map.get("code"); + log.debug("<:{}", code); + if (StatusCodes.NS_PUBLISH_START.equals(code)) { + state = PUBLISHED; + while (frameBuffer.size() > 0) { + rtmpClient.publishStreamData(streamId, frameBuffer.remove(0)); + } + } + } + + public synchronized void resultReceived(IPendingServiceCall call) { + log.debug("resultReceived:> {}", call.getServiceMethodName()); + if ("connect".equals(call.getServiceMethodName())) { + state = STREAM_CREATING; + rtmpClient.createStream(this); + } else if ("createStream".equals(call.getServiceMethodName())) { + state = PUBLISHING; + Object result = call.getResult(); + if (result instanceof Integer) { + Integer streamIdInt = (Integer) result; + streamId = streamIdInt.intValue(); + rtmpClient.publish(streamIdInt.intValue(), publishName, + publishMode, this); + } else { + rtmpClient.disconnect(); + state = STOPPED; + } + } + } +} diff --git a/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/manager/ClientManager.java b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/manager/ClientManager.java new file mode 100644 index 0000000..f166c02 --- /dev/null +++ b/rtmp_video_publisher/src/main/java/com/c77/videopublisher/rtmp_video_publisher/manager/ClientManager.java @@ -0,0 +1,189 @@ +package com.c77.videopublisher.rtmp_video_publisher.manager; + +import com.c77.videopublisher.rtmp_video_publisher.io.Consumer; +import com.c77.videopublisher.rtmp_video_publisher.io.file.FileClient; +import com.c77.videopublisher.rtmp_video_publisher.io.net.NetClient; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientManager implements Runnable, Consumer { + private Logger log = LoggerFactory.getLogger(ClientManager.class); + + private final Object mutex = new Object(); + public static final int NETONLY = 1; + public static final int FILEONLY = 2; + public static final int NETANDFILE = 3; + private int mode = NETONLY; + private int seq = 0; + private volatile boolean isRecording; + private volatile boolean isRunning; + private processedData pData; + private List list; + private String publishNameBase = "test"; + private String publishName; + private String fileNameBase = "/mnt/sdcard/test"; + private String fileName; + private NetClient netClient = new NetClient(); + private FileClient fileClient = new FileClient(); + + public ClientManager() { + super(); + list = Collections.synchronizedList(new LinkedList()); + } + + private void netClientInit() { + netClient.setHost("192.168.1.200"); + netClient.setPort(1935); + netClient.setApp("live"); + netClient.setChannle(1); + netClient.setSampleRate(8000); + } + + private void fileClientInit() { + fileClient.setChannle(1); + fileClient.setSampleRate(8000); + } + + public void setMode(int mode) { + this.mode = mode; + } + + public void run() { + log.debug("publish thread runing"); + + netClientInit(); + fileClientInit(); + + while (this.isRunning()) { + synchronized (mutex) { + while (!this.isRecording) { + try { + mutex.wait(); + } catch (InterruptedException e) { + throw new IllegalStateException("Wait() interrupted!", + e); + } + } + } + + setupFileName(); + startClient(); + startPcmRecorder(); + + while (this.isRecording()) { + if (list.size() > 0) { + writeTag(); + log.debug("list size = {}", list.size()); + } else { + try { + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + // recorder.stop(); + while(list.size() > 0){ + writeTag(); + log.debug("list size = {}", list.size()); + } + stop(); + } + } + + private void setupFileName(){ + fileName = fileNameBase + seq +".flv"; + publishName = publishNameBase + seq; + seq++; + log.info("setup file name done"); + } + + private void startClient() { + switch (this.mode) { + case NETONLY: + netClient.start(publishName, "record", null); + break; + case FILEONLY: + fileClient.start(fileName); + break; + case NETANDFILE: + netClient.start(publishName, "record", null); + fileClient.start(fileName); + break; + default: + netClient.start(publishName, "record", null); + } + } + + private void startPcmRecorder(){ + /*recorder = new PcmRecorder(this); + recorder.setRecording(true); + Thread th = new Thread(recorder); + th.start();*/ + } + + private void writeTag() { + pData = list.remove(0); + if(this.mode == NETONLY || this.mode == NETANDFILE){ + netClient.writeTag(pData.processed, pData.size, pData.ts); + } + if(this.mode == FILEONLY || this.mode == NETANDFILE){ + fileClient.writeTag(pData.processed, pData.size, pData.ts); + } + } + + public void putData(long ts, byte[] buf, int size) { + processedData data = new processedData(); + data.ts = ts; + data.size = size; + System.arraycopy(buf, 0, data.processed, 0, size); + list.add(data); + } + + private void stop() { + netClient.stop(); + fileClient.stop(); + + } + + public boolean isRunning() { + synchronized (mutex) { + return isRunning; + } + } + + public void setRunning(boolean isRunning) { + synchronized (mutex) { + this.isRunning = isRunning; + if (this.isRunning) { + mutex.notify(); + } + } + } + + public void setRecording(boolean isRecording) { + synchronized (mutex) { + this.isRecording = isRecording; + if (this.isRecording) { + mutex.notify(); + } + } + } + + public boolean isRecording() { + synchronized (mutex) { + return isRecording; + } + } + + class processedData { + private long ts; + private int size; + private byte[] processed = new byte[256]; + } +} diff --git a/rtmp_video_publisher/src/main/res/drawable-hdpi/ic_launcher.png b/rtmp_video_publisher/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..c1b44cc Binary files /dev/null and b/rtmp_video_publisher/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/rtmp_video_publisher/src/main/res/drawable-mdpi/ic_launcher.png b/rtmp_video_publisher/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..a1bf709 Binary files /dev/null and b/rtmp_video_publisher/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/rtmp_video_publisher/src/main/res/drawable-xhdpi/ic_launcher.png b/rtmp_video_publisher/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..3b5ae5b Binary files /dev/null and b/rtmp_video_publisher/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/rtmp_video_publisher/src/main/res/drawable-xxhdpi/ic_launcher.png b/rtmp_video_publisher/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b7df51b Binary files /dev/null and b/rtmp_video_publisher/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/rtmp_video_publisher/src/main/res/layout/activity_video_publisher_main.xml b/rtmp_video_publisher/src/main/res/layout/activity_video_publisher_main.xml new file mode 100644 index 0000000..e49182c --- /dev/null +++ b/rtmp_video_publisher/src/main/res/layout/activity_video_publisher_main.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/rtmp_video_publisher/src/main/res/menu/video_publisher_main.xml b/rtmp_video_publisher/src/main/res/menu/video_publisher_main.xml new file mode 100644 index 0000000..f6e57b0 --- /dev/null +++ b/rtmp_video_publisher/src/main/res/menu/video_publisher_main.xml @@ -0,0 +1,8 @@ + + + diff --git a/rtmp_video_publisher/src/main/res/values-w820dp/dimens.xml b/rtmp_video_publisher/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/rtmp_video_publisher/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/rtmp_video_publisher/src/main/res/values/dimens.xml b/rtmp_video_publisher/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/rtmp_video_publisher/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/rtmp_video_publisher/src/main/res/values/strings.xml b/rtmp_video_publisher/src/main/res/values/strings.xml new file mode 100644 index 0000000..609b668 --- /dev/null +++ b/rtmp_video_publisher/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + VideoPublisher + Hello world! + Settings + + diff --git a/rtmp_video_publisher/src/main/res/values/styles.xml b/rtmp_video_publisher/src/main/res/values/styles.xml new file mode 100644 index 0000000..ff6c9d2 --- /dev/null +++ b/rtmp_video_publisher/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/rtmp_video_receiver/.gitignore b/rtmp_video_receiver/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/rtmp_video_receiver/.gitignore @@ -0,0 +1 @@ +/build diff --git a/rtmp_video_receiver/build.gradle b/rtmp_video_receiver/build.gradle new file mode 100644 index 0000000..548309f --- /dev/null +++ b/rtmp_video_receiver/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 'android-L' + buildToolsVersion "20.0.0" + + defaultConfig { + applicationId "com.c77.videoreceiver.rtmp_video_receiver" + minSdkVersion 15 + targetSdkVersion 19 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile 'org.red5:android-rmtp-client:0.3' +} diff --git a/rtmp_video_receiver/proguard-rules.pro b/rtmp_video_receiver/proguard-rules.pro new file mode 100644 index 0000000..0e13397 --- /dev/null +++ b/rtmp_video_receiver/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /opt/android-studio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/rtmp_video_receiver/rtmp_video_receiver.iml b/rtmp_video_receiver/rtmp_video_receiver.iml new file mode 100644 index 0000000..a0e78ec --- /dev/null +++ b/rtmp_video_receiver/rtmp_video_receiver.iml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rtmp_video_receiver/src/androidTest/java/com/c77/videoreceiver/rtmp_video_receiver/ApplicationTest.java b/rtmp_video_receiver/src/androidTest/java/com/c77/videoreceiver/rtmp_video_receiver/ApplicationTest.java new file mode 100644 index 0000000..79a89dc --- /dev/null +++ b/rtmp_video_receiver/src/androidTest/java/com/c77/videoreceiver/rtmp_video_receiver/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.c77.videoreceiver.rtmp_video_receiver; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/rtmp_video_receiver/src/main/AndroidManifest.xml b/rtmp_video_receiver/src/main/AndroidManifest.xml new file mode 100644 index 0000000..357fe84 --- /dev/null +++ b/rtmp_video_receiver/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/rtmp_video_receiver/src/main/ic_launcher-web.png b/rtmp_video_receiver/src/main/ic_launcher-web.png new file mode 100644 index 0000000..4b14629 Binary files /dev/null and b/rtmp_video_receiver/src/main/ic_launcher-web.png differ diff --git a/rtmp_video_receiver/src/main/java/com/c77/videoreceiver/rtmp_video_receiver/VideoReceiverMainActivity.java b/rtmp_video_receiver/src/main/java/com/c77/videoreceiver/rtmp_video_receiver/VideoReceiverMainActivity.java new file mode 100644 index 0000000..f235389 --- /dev/null +++ b/rtmp_video_receiver/src/main/java/com/c77/videoreceiver/rtmp_video_receiver/VideoReceiverMainActivity.java @@ -0,0 +1,84 @@ +package com.c77.videoreceiver.rtmp_video_receiver; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.EditText; + + +public class VideoReceiverMainActivity extends Activity implements View.OnClickListener { + + WebView webView; + + String bodyHtml; + String rtmpUrl; + String fileName; + Button refreshButton; + String htmlPost = ""; + String htmlPre = "" + + "" + + "" + + "" + + ""; + + String htmlCode = " "; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_video_receiver_main); + + webView = (WebView) findViewById(R.id.webview); + + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setAllowFileAccess(true); + webView.getSettings().setPluginState(WebSettings.PluginState.ON); + webView.getSettings().setSupportZoom(true); + webView.getSettings().setAppCacheEnabled(true); + + refreshButton = + (Button) findViewById(R.id.main_bt_refresh); + refreshButton.setOnClickListener(this); + + refreshFileName(); + } + + @Override + public void onClick(View v) { + refreshFileName(); + } + + private void refreshFileName() { + EditText etRtmpUrl = + (EditText) findViewById(R.id.setup_et_host); + EditText etFileName = + (EditText) findViewById(R.id.setup_et_file); + rtmpUrl = etRtmpUrl.getText().toString(); + fileName = etFileName.getText().toString(); + if (fileName.endsWith(".flv")) { + fileName = "flv:" + fileName; + } + + bodyHtml = htmlCode; + bodyHtml = bodyHtml.replaceAll("@FILESRC@", + "\"file=" + fileName + + "&streamer=" + rtmpUrl + "\""); + webView.loadDataWithBaseURL("http://127.0.0.1", + htmlPre + bodyHtml + + htmlPost, "text/html", "UTF-8", null); + } + +} \ No newline at end of file diff --git a/rtmp_video_receiver/src/main/res/drawable-hdpi/ic_launcher.png b/rtmp_video_receiver/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..c1b44cc Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bg.png b/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bg.png new file mode 100644 index 0000000..e87577d Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bg.png differ diff --git a/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bt.png b/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bt.png new file mode 100644 index 0000000..632d190 Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-hdpi/main_bt.png differ diff --git a/rtmp_video_receiver/src/main/res/drawable-mdpi/ic_launcher.png b/rtmp_video_receiver/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..a1bf709 Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/rtmp_video_receiver/src/main/res/drawable-xhdpi/ic_launcher.png b/rtmp_video_receiver/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..3b5ae5b Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/rtmp_video_receiver/src/main/res/drawable-xxhdpi/ic_launcher.png b/rtmp_video_receiver/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b7df51b Binary files /dev/null and b/rtmp_video_receiver/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/rtmp_video_receiver/src/main/res/layout/activity_video_receiver_main.xml b/rtmp_video_receiver/src/main/res/layout/activity_video_receiver_main.xml new file mode 100644 index 0000000..ed184cf --- /dev/null +++ b/rtmp_video_receiver/src/main/res/layout/activity_video_receiver_main.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + +