Seperate handler from thread to UI

Signed-off-by: Leo Ma <begeekmyfriend@gmail.com>
camera2
Leo Ma 9 years ago
parent a1050c902b
commit 0c24613585

@ -16,13 +16,15 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.ossrs.yasea.rtmp.RtmpPublisher;
import net.ossrs.yasea.rtmp.RtmpHandler;
import com.seu.magicfilter.utils.MagicFilterType;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
public class MainActivity extends AppCompatActivity implements RtmpHandler.RtmpListener,
SrsRecordHandler.SrsRecordListener, SrsNetworkHandler.SrsNetworkListener {
private static final String TAG = "Yasea";
Button btnPublish = null;
@ -31,7 +33,7 @@ public class MainActivity extends AppCompatActivity {
Button btnSwitchEncoder = null;
private SharedPreferences sp;
private String rtmpUrl = "rtmp://0.0.0.0/" + getRandomAlphaString(3) + '/' + getRandomAlphaDigitString(5);
private String rtmpUrl = "rtmp://ossrs.net/" + getRandomAlphaString(3) + '/' + getRandomAlphaDigitString(5);
private String recPath = Environment.getExternalStorageDirectory().getPath() + "/test.mp4";
private SrsPublisher mPublisher;
@ -58,7 +60,11 @@ public class MainActivity extends AppCompatActivity {
btnSwitchCamera = (Button) findViewById(R.id.swCam);
btnRecord = (Button) findViewById(R.id.record);
btnSwitchEncoder = (Button) findViewById(R.id.swEnc);
mPublisher = new SrsPublisher((SrsCameraView) findViewById(R.id.glsurfaceview_camera));
mPublisher.setRtmpHandler(new RtmpHandler(this));
mPublisher.setRecordHandler(new SrsRecordHandler(this));
mPublisher.setNetworkHandler(new SrsNetworkHandler(this));
mPublisher.setPreviewResolution(640, 480);
btnPublish.setOnClickListener(new View.OnClickListener() {
@ -74,7 +80,7 @@ public class MainActivity extends AppCompatActivity {
mPublisher.setVideoHDMode();
mPublisher.startPublish(rtmpUrl);
if (btnSwitchEncoder.getText().toString().contentEquals("soft encoding")) {
if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) {
Toast.makeText(getApplicationContext(), "Use hard encoder", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Use soft encoder", Toast.LENGTH_SHORT).show();
@ -119,152 +125,13 @@ public class MainActivity extends AppCompatActivity {
btnSwitchEncoder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (btnSwitchEncoder.getText().toString().contentEquals("soft encoding")) {
if (btnSwitchEncoder.getText().toString().contentEquals("soft encoder")) {
mPublisher.swithToSoftEncoder();
btnSwitchEncoder.setText("hard encoding");
} else if (btnSwitchEncoder.getText().toString().contentEquals("hard encoding")) {
btnSwitchEncoder.setText("hard encoder");
} else if (btnSwitchEncoder.getText().toString().contentEquals("hard encoder")) {
mPublisher.swithToHardEncoder();
btnSwitchEncoder.setText("soft encoding");
}
}
});
mPublisher.setPublishEventHandler(new RtmpPublisher.EventHandler() {
@Override
public void onRtmpConnecting(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRtmpConnected(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRtmpVideoStreaming(final String msg) {
}
@Override
public void onRtmpAudioStreaming(final String msg) {
}
@Override
public void onRtmpStopped(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRtmpDisconnected(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRtmpOutputFps(final double fps) {
Log.i(TAG, String.format("Output Fps: %f", fps));
}
@Override
public void onRtmpVideoBitrate(final double bitrate) {
int rate = (int) bitrate;
if (rate / 1000 > 0) {
Log.i(TAG, String.format("Video bitrate: %f kbps", bitrate / 1000));
} else {
Log.i(TAG, String.format("Video bitrate: %d bps", rate));
}
}
@Override
public void onRtmpAudioBitrate(final double bitrate) {
int rate = (int) bitrate;
if (rate / 1000 > 0) {
Log.i(TAG, String.format("Audio bitrate: %f kbps", bitrate / 1000));
} else {
Log.i(TAG, String.format("Audio bitrate: %d bps", rate));
}
}
});
mPublisher.setRecordEventHandler(new SrsMp4Muxer.EventHandler() {
@Override
public void onRecordPause(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRecordResume(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onRecordStarted(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Recording file: " + msg, Toast.LENGTH_SHORT).show();
btnSwitchEncoder.setText("soft encoder");
}
});
}
@Override
public void onRecordFinished(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "MP4 file saved: " + msg, Toast.LENGTH_SHORT).show();
}
});
}
});
mPublisher.setNetworkEventHandler(new SrsEncoder.EventHandler() {
@Override
public void onNetworkResume(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onNetworkWeak(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
});
@ -418,4 +285,93 @@ public class MainActivity extends AppCompatActivity {
}
return sb.toString();
}
// Implementation of SrsRtmpListener.
@Override
public void onRtmpConnecting(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onRtmpConnected(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onRtmpVideoStreaming() {
}
@Override
public void onRtmpAudioStreaming() {
}
@Override
public void onRtmpStopped() {
Toast.makeText(getApplicationContext(), "Stopped", Toast.LENGTH_SHORT).show();
}
@Override
public void onRtmpDisconnected() {
Toast.makeText(getApplicationContext(), "Disconnected", Toast.LENGTH_SHORT).show();
}
@Override
public void onRtmpVideoFpsChanged(double fps) {
Log.i(TAG, String.format("Output Fps: %f", fps));
}
@Override
public void onRtmpVideoBitrateChanged(double bitrate) {
int rate = (int) bitrate;
if (rate / 1000 > 0) {
Log.i(TAG, String.format("Video bitrate: %f kbps", bitrate / 1000));
} else {
Log.i(TAG, String.format("Video bitrate: %d bps", rate));
}
}
@Override
public void onRtmpAudioBitrateChanged(double bitrate) {
int rate = (int) bitrate;
if (rate / 1000 > 0) {
Log.i(TAG, String.format("Audio bitrate: %f kbps", bitrate / 1000));
} else {
Log.i(TAG, String.format("Audio bitrate: %d bps", rate));
}
}
// Implementation of SrsRecordHandler.
@Override
public void onRecordPause() {
Toast.makeText(getApplicationContext(), "Record paused", Toast.LENGTH_SHORT).show();
}
@Override
public void onRecordResume() {
Toast.makeText(getApplicationContext(), "Record resumed", Toast.LENGTH_SHORT).show();
}
@Override
public void onRecordStarted(String msg) {
Toast.makeText(getApplicationContext(), "Recording file: " + msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onRecordFinished(String msg) {
Toast.makeText(getApplicationContext(), "MP4 file saved: " + msg, Toast.LENGTH_SHORT).show();
}
// Implementation of SrsNetworkHandler.
@Override
public void onNetworkWeak() {
Toast.makeText(getApplicationContext(), "Network weak", Toast.LENGTH_SHORT).show();
}
@Override
public void onNetworkResume() {
Toast.makeText(getApplicationContext(), "Network resume", Toast.LENGTH_SHORT).show();
}
}

@ -49,7 +49,7 @@ public class SrsEncoder {
private MediaCodec.BufferInfo vebi = new MediaCodec.BufferInfo();
private MediaCodec.BufferInfo aebi = new MediaCodec.BufferInfo();
private EventHandler mHandler;
private SrsNetworkHandler mHandler;
private boolean networkWeakTriggered = false;
private boolean mCameraFaceFront = true;
private boolean useSoftEncoder = false;
@ -63,13 +63,6 @@ public class SrsEncoder {
private int audioFlvTrack;
private int audioMp4Track;
public interface EventHandler {
void onNetworkResume(String msg);
void onNetworkWeak(String msg);
}
// Y, U (Cb) and V (Cr)
// yuv420 yuv yuv yuv yuv
// yuv420p (planar) yyyy*2 uu vv
@ -93,7 +86,7 @@ public class SrsEncoder {
this.mp4Muxer = mp4Muxer;
}
public void setNetworkEventHandler(EventHandler handler) {
public void setNetworkEventHandler(SrsNetworkHandler handler) {
mHandler = handler;
}
@ -370,99 +363,6 @@ public class SrsEncoder {
}
}
public void onGetYuvFrame(byte[] data) {
// Check video frame cache number to judge the networking situation.
// Just cache GOP / FPS seconds data according to latency.
AtomicInteger videoFrameCacheNumber = flvMuxer.getVideoFrameCacheNumber();
if (videoFrameCacheNumber != null && videoFrameCacheNumber.get() < VGOP) {
long pts = System.nanoTime() / 1000 - mPresentTimeUs;
if (useSoftEncoder) {
if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
swPortraitYuvFrame(data, pts);
} else {
swLandscapeYuvFrame(data, pts);
}
} else {
byte[] processedData = mOrientation == Configuration.ORIENTATION_PORTRAIT ?
hwPortraitYuvFrame(data) : hwLandscapeYuvFrame(data);
if (processedData != null) {
onProcessedYuvFrame(processedData, pts);
} else {
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(),
new IllegalArgumentException("libyuv failure"));
}
}
if (networkWeakTriggered) {
networkWeakTriggered = false;
mHandler.onNetworkResume("Network resume");
}
} else {
mHandler.onNetworkWeak("Network weak");
networkWeakTriggered = true;
}
}
private byte[] hwPortraitYuvFrame(byte[] data) {
if (mCameraFaceFront) {
switch (mVideoColorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
return NV21ToI420(data, vPrevWidth, vPrevHeight, true, 270);
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
return NV21ToNV12(data, vPrevWidth, vPrevHeight, true, 270);
default:
throw new IllegalStateException("Unsupported color format!");
}
} else {
switch (mVideoColorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
return NV21ToI420(data, vPrevWidth, vPrevHeight, false, 90);
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
return NV21ToNV12(data, vPrevWidth, vPrevHeight, false, 90);
default:
throw new IllegalStateException("Unsupported color format!");
}
}
}
private byte[] hwLandscapeYuvFrame(byte[] data) {
if (mCameraFaceFront) {
switch (mVideoColorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
return NV21ToI420(data, vPrevWidth, vPrevHeight, true, 0);
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
return NV21ToNV12(data, vPrevWidth, vPrevHeight, true, 0);
default:
throw new IllegalStateException("Unsupported color format!");
}
} else {
switch (mVideoColorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
return NV21ToI420(data, vPrevWidth, vPrevHeight, false, 0);
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
return NV21ToNV12(data, vPrevWidth, vPrevHeight, false, 0);
default:
throw new IllegalStateException("Unsupported color format!");
}
}
}
private void swPortraitYuvFrame(byte[] data, long pts) {
if (mCameraFaceFront) {
NV21SoftEncode(data, vPrevWidth, vPrevHeight, true, 270, pts);
} else {
NV21SoftEncode(data, vPrevWidth, vPrevHeight, false, 90, pts);
}
}
private void swLandscapeYuvFrame(byte[] data, long pts) {
if (mCameraFaceFront) {
NV21SoftEncode(data, vPrevWidth, vPrevHeight, true, 0, pts);
} else {
NV21SoftEncode(data, vPrevWidth, vPrevHeight, false, 0, pts);
}
}
public void onGetRgbaFrame(byte[] data, int width, int height) {
// Check video frame cache number to judge the networking situation.
// Just cache GOP / FPS seconds data according to latency.
@ -483,10 +383,10 @@ public class SrsEncoder {
if (networkWeakTriggered) {
networkWeakTriggered = false;
mHandler.onNetworkResume("Network resume");
mHandler.notifyNetworkResume();
}
} else {
mHandler.onNetworkWeak("Network weak");
mHandler.notifyNetworkWeak();
networkWeakTriggered = true;
}
}

@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import net.ossrs.yasea.rtmp.DefaultRtmpPublisher;
import net.ossrs.yasea.rtmp.RtmpHandler;
/**
* Created by winlin on 5/2/15.
@ -65,7 +66,7 @@ public class SrsFlvMuxer {
* constructor.
* @param handler the rtmp event handler.
*/
public SrsFlvMuxer(DefaultRtmpPublisher.EventHandler handler) {
public SrsFlvMuxer(RtmpHandler handler) {
publisher = new DefaultRtmpPublisher(handler);
}

@ -67,7 +67,7 @@ public class SrsMp4Muxer {
private static final int AUDIO_TRACK = 101;
private File mRecFile;
private EventHandler mHandler;
private SrsRecordHandler mHandler;
private MediaFormat videoFormat = null;
private MediaFormat audioFormat = null;
@ -105,31 +105,17 @@ public class SrsMp4Muxer {
samplingFrequencyIndexMap.put(8000, 0xb);
}
public SrsMp4Muxer(EventHandler handler) {
public SrsMp4Muxer(SrsRecordHandler handler) {
mHandler = handler;
}
/**
* MP4 recording event handler.
*/
public interface EventHandler {
void onRecordPause(String msg);
void onRecordResume(String msg);
void onRecordStarted(String msg);
void onRecordFinished(String msg);
}
/**
* start recording.
*/
public void record(File outputFile) throws IOException {
mRecFile = outputFile;
createMovie(mRecFile);
mHandler.onRecordStarted(mRecFile.getPath());
mHandler.notifyRecordStarted(mRecFile.getPath());
if (!spsList.isEmpty() && !ppsList.isEmpty()) {
mp4Movie.addTrack(videoFormat, false);
@ -171,7 +157,7 @@ public class SrsMp4Muxer {
public void pause() {
if (bRecording) {
bPaused = true;
mHandler.onRecordPause("Recording pause");
mHandler.notifyRecordPause();
}
}
@ -182,7 +168,7 @@ public class SrsMp4Muxer {
if (bRecording) {
bPaused = false;
needToFindKeyFrame = true;
mHandler.onRecordResume("Recording resume");
mHandler.notifyRecordResume();
}
}
@ -210,7 +196,7 @@ public class SrsMp4Muxer {
} catch (IOException e) {
e.printStackTrace();
}
mHandler.onRecordFinished(mRecFile.getPath());
mHandler.notifyRecordFinished(mRecFile.getPath());
}
Log.i(TAG, String.format("SrsMp4Muxer closed"));
}
@ -291,7 +277,7 @@ public class SrsMp4Muxer {
public final static int CodedSliceExt = 20;
}
public void writeVideoSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) throws IllegalArgumentException {
private void writeVideoSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) throws IllegalArgumentException {
int nal_unit_type = bb.get(4) & 0x1f;
if (nal_unit_type == SrsAvcNaluType.IDR || nal_unit_type == SrsAvcNaluType.NonIDR) {
writeFrameByte(VIDEO_TRACK, bb, bi, nal_unit_type == SrsAvcNaluType.IDR);
@ -324,7 +310,7 @@ public class SrsMp4Muxer {
}
}
public void writeAudioSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
private void writeAudioSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
if (!aacSpecConfig) {
aacSpecConfig = true;
} else {
@ -471,7 +457,7 @@ public class SrsMp4Muxer {
}
}
public class Sample {
class Sample {
private long offset = 0;
private long size = 0;
@ -489,7 +475,7 @@ public class SrsMp4Muxer {
}
}
public class Track {
class Track {
private int trackId = 0;
private ArrayList<Sample> samples = new ArrayList<>();
private long duration = 0;
@ -683,7 +669,7 @@ public class SrsMp4Muxer {
}
}
public class Mp4Movie {
class Mp4Movie {
private Matrix matrix = Matrix.ROTATE_0;
private HashMap<Integer, Track> tracks = new HashMap<>();
@ -713,7 +699,7 @@ public class SrsMp4Muxer {
}
}
public class InterleaveChunkMdat implements Box {
class InterleaveChunkMdat implements Box {
private boolean first = true;
private ContainerBox parent;
private ByteBuffer header = ByteBuffer.allocateDirect(16);

@ -0,0 +1,55 @@
package net.ossrs.yasea;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* Created by leo.ma on 2016/11/4.
*/
public class SrsNetworkHandler extends Handler {
private static final int MSG_NETWORK_WEAK = 0;
private static final int MSG_NETWORK_RESUME = 1;
private WeakReference<SrsNetworkListener> mWeakListener;
public SrsNetworkHandler(SrsNetworkListener listener) {
mWeakListener = new WeakReference<>(listener);
}
public void notifyNetworkWeak() {
sendEmptyMessage(MSG_NETWORK_WEAK);
}
public void notifyNetworkResume() {
sendEmptyMessage(MSG_NETWORK_RESUME);
}
@Override // runs on UI thread
public void handleMessage(Message msg) {
SrsNetworkListener listener = mWeakListener.get();
if (listener == null) {
return;
}
switch (msg.what) {
case MSG_NETWORK_WEAK:
listener.onNetworkWeak();
break;
case MSG_NETWORK_RESUME:
listener.onNetworkResume();
break;
default:
throw new RuntimeException("unknown msg " + msg.what);
}
}
interface SrsNetworkListener {
void onNetworkWeak();
void onNetworkResume();
}
}

@ -4,7 +4,7 @@ import android.content.Context;
import android.media.AudioManager;
import android.media.AudioRecord;
import net.ossrs.yasea.rtmp.RtmpPublisher;
import net.ossrs.yasea.rtmp.RtmpHandler;
import com.seu.magicfilter.utils.MagicFilterType;
@ -260,17 +260,17 @@ public class SrsPublisher {
audioManager.setMode(oldMode);
}
public void setPublishEventHandler(RtmpPublisher.EventHandler handler) {
public void setRtmpHandler(RtmpHandler handler) {
mFlvMuxer = new SrsFlvMuxer(handler);
mEncoder.setFlvMuxer(mFlvMuxer);
}
public void setRecordEventHandler(SrsMp4Muxer.EventHandler handler) {
public void setRecordHandler(SrsRecordHandler handler) {
mMp4Muxer = new SrsMp4Muxer(handler);
mEncoder.setMp4Muxer(mMp4Muxer);
}
public void setNetworkEventHandler(SrsEncoder.EventHandler handler) {
public void setNetworkHandler(SrsNetworkHandler handler) {
mEncoder.setNetworkEventHandler(handler);
}
}

@ -0,0 +1,75 @@
package net.ossrs.yasea;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* Created by leo.ma on 2016/11/4.
*/
public class SrsRecordHandler extends Handler {
private static final int MSG_RECORD_PAUSE = 0;
private static final int MSG_RECORD_RESUME = 1;
private static final int MSG_RECORD_STARTED = 2;
private static final int MSG_RECORD_FINISHED = 3;
private WeakReference<SrsRecordListener> mWeakListener;
SrsRecordHandler(SrsRecordListener listener) {
mWeakListener = new WeakReference<>(listener);
}
public void notifyRecordPause() {
sendEmptyMessage(MSG_RECORD_PAUSE);
}
public void notifyRecordResume() {
sendEmptyMessage(MSG_RECORD_RESUME);
}
public void notifyRecordStarted(String msg) {
obtainMessage(MSG_RECORD_STARTED, msg).sendToTarget();
}
public void notifyRecordFinished(String msg) {
obtainMessage(MSG_RECORD_FINISHED, msg).sendToTarget();
}
@Override // runs on UI thread
public void handleMessage(Message msg) {
SrsRecordListener listener = mWeakListener.get();
if (listener == null) {
return;
}
switch (msg.what) {
case MSG_RECORD_PAUSE:
listener.onRecordPause();
break;
case MSG_RECORD_RESUME:
listener.onRecordResume();
break;
case MSG_RECORD_STARTED:
listener.onRecordStarted((String) msg.obj);
break;
case MSG_RECORD_FINISHED:
listener.onRecordFinished((String) msg.obj);
break;
default:
throw new RuntimeException("unknown msg " + msg.what);
}
}
interface SrsRecordListener {
void onRecordPause();
void onRecordResume();
void onRecordStarted(String msg);
void onRecordFinished(String msg);
}
}

@ -14,7 +14,7 @@ public class DefaultRtmpPublisher implements RtmpPublisher {
private RtmpConnection rtmpConnection;
public DefaultRtmpPublisher(RtmpPublisher.EventHandler handler) {
public DefaultRtmpPublisher(RtmpHandler handler) {
rtmpConnection = new RtmpConnection(handler);
}
@ -62,11 +62,6 @@ public class DefaultRtmpPublisher implements RtmpPublisher {
return rtmpConnection.getVideoFrameCacheNumber();
}
@Override
public final EventHandler getEventHandler() {
return rtmpConnection.getEventHandler();
}
@Override
public final String getServerIpAddr() {
return rtmpConnection.getServerIpAddr();
@ -86,4 +81,5 @@ public class DefaultRtmpPublisher implements RtmpPublisher {
public void setVideoResolution(int width, int height) {
rtmpConnection.setVideoResolution(width, height);
}
}

@ -0,0 +1,126 @@
package net.ossrs.yasea.rtmp;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* Created by leo.ma on 2016/11/3.
*/
public class RtmpHandler extends Handler {
private static final int MSG_RTMP_CONNECTING = 0;
private static final int MSG_RTMP_CONNECTED = 1;
private static final int MSG_RTMP_VIDEO_STREAMING = 2;
private static final int MSG_RTMP_AUDIO_STREAMING = 3;
private static final int MSG_RTMP_STOPPED = 4;
private static final int MSG_RTMP_DISCONNECTED = 5;
private static final int MSG_RTMP_VIDEO_FPS_CHANGED = 6;
private static final int MSG_RTMP_VIDEO_BITRATE_CHANGED = 7;
private static final int MSG_RTMP_AUDIO_BITRATE_CHANGED = 8;
private WeakReference<RtmpListener> mWeakListener;
public RtmpHandler(RtmpListener listener) {
mWeakListener = new WeakReference<>(listener);
}
public void notifyRtmpConnecting(String msg) {
obtainMessage(MSG_RTMP_CONNECTING, msg).sendToTarget();
}
public void notifyRtmpConnected(String msg) {
obtainMessage(MSG_RTMP_CONNECTED, msg).sendToTarget();
}
public void notifyRtmpVideoStreaming() {
sendEmptyMessage(MSG_RTMP_VIDEO_STREAMING);
}
public void notifyRtmpAudioStreaming() {
sendEmptyMessage(MSG_RTMP_AUDIO_STREAMING);
}
public void notifyRtmpStopped() {
sendEmptyMessage(MSG_RTMP_STOPPED);
}
public void notifyRtmpDisconnected() {
sendEmptyMessage(MSG_RTMP_DISCONNECTED);
}
public void notifyRtmpVideoFpsChanged(double fps) {
obtainMessage(MSG_RTMP_VIDEO_FPS_CHANGED, fps).sendToTarget();
}
public void notifyRtmpVideoBitrateChanged(double bitrate) {
obtainMessage(MSG_RTMP_VIDEO_BITRATE_CHANGED, bitrate).sendToTarget();
}
public void notifyRtmpAudioBitrateChanged(double bitrate) {
obtainMessage(MSG_RTMP_AUDIO_BITRATE_CHANGED, bitrate).sendToTarget();
}
@Override // runs on UI thread
public void handleMessage(Message msg) {
RtmpListener listener = mWeakListener.get();
if (listener == null) {
return;
}
switch (msg.what) {
case MSG_RTMP_CONNECTING:
listener.onRtmpConnecting((String) msg.obj);
break;
case MSG_RTMP_CONNECTED:
listener.onRtmpConnected((String) msg.obj);
break;
case MSG_RTMP_VIDEO_STREAMING:
listener.onRtmpVideoStreaming();
break;
case MSG_RTMP_AUDIO_STREAMING:
listener.onRtmpAudioStreaming();
break;
case MSG_RTMP_STOPPED:
listener.onRtmpStopped();
break;
case MSG_RTMP_DISCONNECTED:
listener.onRtmpDisconnected();
break;
case MSG_RTMP_VIDEO_FPS_CHANGED:
listener.onRtmpVideoFpsChanged((double) msg.obj);
break;
case MSG_RTMP_VIDEO_BITRATE_CHANGED:
listener.onRtmpVideoBitrateChanged((double) msg.obj);
break;
case MSG_RTMP_AUDIO_BITRATE_CHANGED:
listener.onRtmpAudioBitrateChanged((double) msg.obj);
break;
default:
throw new RuntimeException("unknown msg " + msg.what);
}
}
public interface RtmpListener {
void onRtmpConnecting(String msg);
void onRtmpConnected(String msg);
void onRtmpVideoStreaming();
void onRtmpAudioStreaming();
void onRtmpStopped();
void onRtmpDisconnected();
void onRtmpVideoFpsChanged(double fps);
void onRtmpVideoBitrateChanged(double bitrate);
void onRtmpAudioBitrateChanged(double bitrate);
}
}

@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public interface RtmpPublisher {
/**
* Issues an RTMP "connect" command and write the media content stream packets (audio and video).
* Issues an RTMP "connect" command and wait for the response.
*
* @param url specify the RTMP url
* @return If succeeded return true else return false
@ -57,11 +57,6 @@ public interface RtmpPublisher {
*/
void publishAudioData(byte[] data, int dts) throws IllegalStateException;
/**
* obtain event handler in publisher
*/
EventHandler getEventHandler();
/**
* obtain video frame number cached in publisher
*/
@ -90,27 +85,4 @@ public interface RtmpPublisher {
*/
void setVideoResolution(int width, int height);
/**
* RTMP event handler.
*/
interface EventHandler {
void onRtmpConnecting(String msg);
void onRtmpConnected(String msg);
void onRtmpVideoStreaming(String msg);
void onRtmpAudioStreaming(String msg);
void onRtmpStopped(String msg);
void onRtmpDisconnected(String msg);
void onRtmpOutputFps(double fps);
void onRtmpVideoBitrate(double bitrate);
void onRtmpAudioBitrate(double bitrate);
}
}

@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import android.util.Log;
import net.ossrs.yasea.rtmp.RtmpHandler;
import net.ossrs.yasea.rtmp.RtmpPublisher;
import net.ossrs.yasea.rtmp.amf.AmfMap;
import net.ossrs.yasea.rtmp.amf.AmfNull;
@ -45,7 +46,9 @@ public class RtmpConnection implements RtmpPublisher {
private static final String TAG = "RtmpConnection";
private static final Pattern rtmpUrlPattern = Pattern.compile("^rtmp://([^/:]+)(:(\\d+))*/([^/]+)(/(.*))*$");
private RtmpPublisher.EventHandler mHandler;
private RtmpHandler mHandler;
private int port;
private String host;
private String appName;
private String streamName;
private String publishType;
@ -79,7 +82,7 @@ public class RtmpConnection implements RtmpPublisher {
private long videoLastTimeMillis;
private long audioLastTimeMillis;
public RtmpConnection(RtmpPublisher.EventHandler handler) {
public RtmpConnection(RtmpHandler handler) {
mHandler = handler;
}
@ -96,8 +99,6 @@ public class RtmpConnection implements RtmpPublisher {
@Override
public boolean connect(String url) throws IOException {
int port;
String host;
Matcher matcher = rtmpUrlPattern.matcher(url);
if (matcher.matches()) {
tcUrl = url.substring(0, url.lastIndexOf('/'));
@ -113,8 +114,7 @@ public class RtmpConnection implements RtmpPublisher {
}
// socket connection
Log.d(TAG, "connect() called. Host: " + host + ", port: " + port + ", appName: " + appName +
", publishPath: " + streamName);
Log.d(TAG, "connect() called. Host: " + host + ", port: " + port + ", appName: " + appName + ", publishPath: " + streamName);
rtmpSessionInfo = new RtmpSessionInfo();
rtmpDecoder = new RtmpDecoder(rtmpSessionInfo);
socket = new Socket();
@ -170,7 +170,7 @@ public class RtmpConnection implements RtmpPublisher {
args.setProperty("objectEncoding", 0);
invoke.addData(args);
sendRtmpPacket(invoke);
mHandler.onRtmpConnecting("connecting");
mHandler.notifyRtmpConnecting("Connecting");
synchronized (connectingLock) {
try {
@ -231,7 +231,7 @@ public class RtmpConnection implements RtmpPublisher {
}
}
if (publishPermitted) {
mHandler.onRtmpConnected("connected" + srsServerInfo);
mHandler.notifyRtmpConnected("Connected" + srsServerInfo);
} else {
shutdown();
}
@ -301,7 +301,7 @@ public class RtmpConnection implements RtmpPublisher {
closeStream.getHeader().setMessageStreamId(currentStreamId);
closeStream.addData(new AmfNull());
sendRtmpPacket(closeStream);
mHandler.onRtmpStopped("stopped");
mHandler.notifyRtmpStopped();
}
@Override
@ -335,7 +335,7 @@ public class RtmpConnection implements RtmpPublisher {
Log.e(TAG, "shutdown(): failed to close socket", ex);
}
mHandler.onRtmpDisconnected("disconnected");
mHandler.notifyRtmpDisconnected();
}
reset();
@ -378,7 +378,7 @@ public class RtmpConnection implements RtmpPublisher {
audio.getHeader().setMessageStreamId(currentStreamId);
sendRtmpPacket(audio);
calcAudioBitrate(audio.getHeader().getPacketLength());
mHandler.onRtmpAudioStreaming("audio streaming");
mHandler.notifyRtmpAudioStreaming();
}
@Override
@ -399,7 +399,7 @@ public class RtmpConnection implements RtmpPublisher {
sendRtmpPacket(video);
videoFrameCacheNumber.decrementAndGet();
calcVideoFpsAndBitrate(video.getHeader().getPacketLength());
mHandler.onRtmpVideoStreaming("video streaming");
mHandler.notifyRtmpVideoStreaming();
}
private void calcVideoFpsAndBitrate(int length) {
@ -410,8 +410,8 @@ public class RtmpConnection implements RtmpPublisher {
} else {
if (++videoFrameCount >= 48) {
long diffTimeMillis = System.nanoTime() / 1000000 - videoLastTimeMillis;
mHandler.onRtmpOutputFps((double) videoFrameCount * 1000 / diffTimeMillis);
mHandler.onRtmpVideoBitrate((double) videoDataLength * 8 * 1000 / diffTimeMillis);
mHandler.notifyRtmpVideoFpsChanged((double) videoFrameCount * 1000 / diffTimeMillis);
mHandler.notifyRtmpVideoBitrateChanged((double) videoDataLength * 8 * 1000 / diffTimeMillis);
videoFrameCount = 0;
videoDataLength = 0;
}
@ -426,7 +426,7 @@ public class RtmpConnection implements RtmpPublisher {
} else {
if (++audioFrameCount >= 48) {
long diffTimeMillis = System.nanoTime() / 1000000 - audioLastTimeMillis;
mHandler.onRtmpAudioBitrate((double) audioDataLength * 8 * 1000 / diffTimeMillis);
mHandler.notifyRtmpAudioBitrateChanged((double) audioDataLength * 8 * 1000 / diffTimeMillis);
audioFrameCount = 0;
audioDataLength = 0;
}
@ -599,11 +599,6 @@ public class RtmpConnection implements RtmpPublisher {
return videoFrameCacheNumber;
}
@Override
public EventHandler getEventHandler() {
return mHandler;
}
@Override
public final String getServerIpAddr() {
return serverIpAddr == null ? null : serverIpAddr.getValue();

@ -44,7 +44,7 @@
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="soft encoding"
android:text="soft encoder"
android:id="@+id/swEnc"
android:layout_alignBottom="@+id/publish"
android:layout_toRightOf="@id/record"

Loading…
Cancel
Save