一、前言
本文要讲述的是将AVC(h264)数据推送到流媒体服务器。我的实现方法是:1先使用Android自带的API采集摄像头数据,然后进行h264编码。2、然后使用ndk开发将编码后的数据通过librtmp发送出去。关于如何得到librtmp的动态库和如何使用系统API编码视频,可以参考我以前的文章。
- 移植librtmp http://blog.csdn.net/a992036795/article/details/54377892
- 使用系统api编码视频 http://blog.csdn.net/a992036795/article/details/54286654
二、思路
1、使用摄像头采集视频、编码、得到h264数据。(这些不是重点,之前文章有讲到)
2、定义jni方法,我定义了4个方法:
public static final native int init(String url, int timeOut);public static final native int sendSpsAndPps(byte[] sps, int sysLen, byte[] pps, int ppsLen, long time);public static final native int sendVideoFrame(byte[] frame, int len, long time);public static final native int stop();
分别的作用:1、初始化,并连接url、握手。2、发送SPS帧、和PPS帧。3、发送视频数据。4、释放资源。
3、实现这些jni方法。
4、对得到的h264帧进行判断,(主要的帧分为 SPS帧、 PPS帧、 IDR帧、 非关键帧)调用。
三、h264数据的格式分析
1、对应h264数据而言,每帧的界定符都是 00 00 00 01 或者 00 00 01。但如果是SPS或PPS帧他们的界定符一定是00 00 00 01
2、 帧类型有:
NAL_SLICE = 1
NAL_SLICE_DPA = 2
NAL_SLICE_DPB = 3
NAL_SLICE_DPC = 4
NAL_SLICE_IDR = 5
NAL_SEI = 6
NAL_SPS = 7
NAL_PPS = 8
NAL_AUD = 9
NAL_FILLER = 12
我们发送 RTMP 数据时只需要知道四种帧类型,其它类型我都把它规类成非关键帧。分别是
NAL_SPS(7), sps 帧
NAL_PPS(8), pps 帧
NAL_SLICE_IDR(5), 关键帧
NAL_SLICE(1) 非关键帧
帧类型的方式判断为界面符后首字节的低四位。
第一帧的帧类型为: 0x67 & 0x1F = 7,这是一个 SPS 帧
第二帧的帧类型为: 0x68 & 0x1F = 8,这是一个 PPS 帧
第三帧的帧类型为: 0x06 & 0x1F = 6,这是一个 SEI 帧
四、代码
1、初始化并连接url
JNIEXPORT jint JNICALL
Java_com_blueberry_hellortmp_Rtmp_init(JNIEnv *env, jclass type, jstring url_, jint timeOut) {const char *url = (*env)->GetStringUTFChars(env, url_, 0);int ret;RTMP_LogSetLevel(RTMP_LOGDEBUG);rtmp = RTMP_Alloc(); //申请rtmp空间RTMP_Init(rtmp);rtmp->Link.timeout = timeOut;//单位秒RTMP_SetupURL(rtmp, url);RTMP_EnableWrite(rtmp);//握手if ((ret = RTMP_Connect(rtmp, NULL)) <= 0) {LOGD("rtmp connet error");return (*env)->NewStringUTF(env, "error");}if ((ret = RTMP_ConnectStream(rtmp, 0)) <= 0) {LOGD("rtmp connect stream error");}(*env)->ReleaseStringUTFChars(env, url_, url);return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
2、发送sps帧 和pps帧
int send_video_sps_pps(unsigned char *sps, int sps_len, unsigned char *pps, int pps_len) {int i;packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + 1024);memset(packet, 0, RTMP_HEAD_SIZE);packet->m_body = (char *) packet + RTMP_HEAD_SIZE;body = (unsigned char *) packet->m_body;i = 0;body[i++] = 0x17; body[i++] = 0x00; body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00; body[i++] = 0x01;body[i++] = sps[1]; body[i++] = sps[2]; body[i++] = sps[3]; body[i++] = 0xff;body[i++] = 0xe1;body[i++] = (sps_len >> 8) & 0xff;body[i++] = sps_len & 0xff;memcpy(&body[i], sps, sps_len);i += sps_len;body[i++] = 0x01;body[i++] = (pps_len >> 8) & 0xff;body[i++] = pps_len & 0xff;memcpy(&body[i], pps, pps_len);i += pps_len;packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nBodySize = i;packet->m_nChannel = 0x04;packet->m_nTimeStamp = 0;packet->m_hasAbsTimestamp = 0;packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;packet->m_nInfoField2 = rtmp->m_stream_id;if (RTMP_IsConnected(rtmp)) {RTMP_SendPacket(rtmp, packet, TRUE);}free(packet);return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
3、发送视频数据
int send_rtmp_video(unsigned char *buf, int len, long time) {int type;long timeOffset;timeOffset = time - start_time;if (buf[2] == 0x00) {buf += 4;len -= 4;} else if (buf[2] == 0x01) {buf += 3;len - 3;}type = buf[0] & 0x1f;packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + len + 9);memset(packet, 0, RTMP_HEAD_SIZE);packet->m_body = (char *) packet + RTMP_HEAD_SIZE;packet->m_nBodySize = len + 9;body = (unsigned char *) packet->m_body;memset(body, 0, len + 9);body[0] = 0x27;if (type == NAL_SLICE_IDR) {body[0] = 0x17; }body[1] = 0x01;body[2] = 0x00;body[3] = 0x00;body[4] = 0x00;body[5] = (len >> 24) & 0xff;body[6] = (len >> 16) & 0xff;body[7] = (len >> 8) & 0xff;body[8] = (len) & 0xff;memcpy(&body[9], buf, len);packet->m_hasAbsTimestamp = 0;packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nInfoField2 = rtmp->m_stream_id;packet->m_nChannel = 0x04;packet->m_headerType = RTMP_PACKET_SIZE_LARGE;packet->m_nTimeStamp = timeOffset;if (RTMP_IsConnected(rtmp)) {RTMP_SendPacket(rtmp, packet, TRUE);}free(packet);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
4、释放
int stop() {RTMP_Close(rtmp);RTMP_Free(rtmp);
}
完整代码:
MainActivity.Java
package com.blueberry.hellortmp;import android.app.Activity;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.List;import static android.hardware.Camera.Parameters.FOCUS_MODE_AUTO;
import static android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX;
import static android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX;
import static android.media.MediaCodec.CONFIGURE_FLAG_ENCODE;
import static android.media.MediaFormat.KEY_BIT_RATE;
import static android.media.MediaFormat.KEY_COLOR_FORMAT;
import static android.media.MediaFormat.KEY_FRAME_RATE;
import static android.media.MediaFormat.KEY_I_FRAME_INTERVAL;public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback2 {static {System.loadLibrary("hellortmp");}static final int NAL_SLICE = 1;static final int NAL_SLICE_DPA = 2;static final int NAL_SLICE_DPB = 3;static final int NAL_SLICE_DPC = 4;static final int NAL_SLICE_IDR = 5;static final int NAL_SEI = 6;static final int NAL_SPS = 7;static final int NAL_PPS = 8;static final int NAL_AUD = 9;static final int NAL_FILLER = 12;private static final String VCODEC_MIME = "video/avc";private Button btnStart;private SurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private Camera mCamera;private boolean isStarted;private int colorFormat;private long presentationTimeUs;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btnStart = (Button) findViewById(R.id.btn_start);mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);btnStart.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {togglePublish();}});mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(this);}private void togglePublish() {if (isStarted) {stop();} else {start();}btnStart.setText(isStarted ? "停止" : "开始");}private void start() {isStarted = true;initVideoEncoder();presentationTimeUs = new Date().getTime() * 1000;Rtmp.init("rtmp://192.168.155.1:1935/live/test", 5);}private MediaCodec vencoder;private void initVideoEncoder() {MediaCodecInfo mediaCodecInfo = selectCodec(VCODEC_MIME);colorFormat = getColorFormat(mediaCodecInfo);try {vencoder = MediaCodec.createByCodecName(mediaCodecInfo.getName());Log.d(TAG, "编码器:" + mediaCodecInfo.getName() + "创建完成!");} catch (IOException e) {e.printStackTrace();throw new RuntimeException("vencodec初始化失败!", e);}MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,previewSize.width, previewSize.height);mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);mediaFormat.setInteger(KEY_BIT_RATE, 300 * 1000); mediaFormat.setInteger(KEY_COLOR_FORMAT, colorFormat);mediaFormat.setInteger(KEY_FRAME_RATE, 20);mediaFormat.setInteger(KEY_I_FRAME_INTERVAL, 5);vencoder.configure(mediaFormat, null, null, CONFIGURE_FLAG_ENCODE);vencoder.start();}private static MediaCodecInfo selectCodec(String mimeType) {int numCodecs = MediaCodecList.getCodecCount();for (int i = 0; i < numCodecs; i++) {MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);if (!codecInfo.isEncoder()) {continue;}String[] types = codecInfo.getSupportedTypes();for (int j = 0; j < types.length; j++) {if (types[j].equalsIgnoreCase(mimeType)) {return codecInfo;}}}return null;}private int getColorFormat(MediaCodecInfo mediaCodecInfo) {int matchedFormat = 0;MediaCodecInfo.CodecCapabilities codecCapabilities =mediaCodecInfo.getCapabilitiesForType(VCODEC_MIME);for (int i = 0; i < codecCapabilities.colorFormats.length; i++) {int format = codecCapabilities.colorFormats[i];if (format >= codecCapabilities.COLOR_FormatYUV420Planar &&format <= codecCapabilities.COLOR_FormatYUV420PackedSemiPlanar) {if (format >= matchedFormat) {matchedFormat = format;break;}}}return matchedFormat;}private void stop() {isStarted = false;vencoder.stop();vencoder.release();Rtmp.stop();}@Overrideprotected void onResume() {super.onResume();initCamera();}@Overrideprotected void onPause() {super.onPause();releaseCamera();}private void releaseCamera() {if (mCamera != null) {mCamera.release();}mCamera = null;}private void initCamera() {try {if (mCamera == null) {mCamera = Camera.open();}} catch (Exception e) {throw new RuntimeException("open camera fail", e);}setParameters();setCameraDisplayOrientation(this, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);try {mCamera.setPreviewDisplay(mSurfaceHolder);} catch (IOException e) {e.printStackTrace();}mCamera.addCallbackBuffer(new byte[calculateFrameSize(ImageFormat.NV21)]);mCamera.setPreviewCallbackWithBuffer(getPreviewCallback());mCamera.startPreview();}public static void setCameraDisplayOrientation(Activity activity,int cameraId, android.hardware.Camera camera) {android.hardware.Camera.CameraInfo info =new android.hardware.Camera.CameraInfo();android.hardware.Camera.getCameraInfo(cameraId, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360; } else { result = (info.orientation - degrees + 360) % 360;}camera.setDisplayOrientation(result);}private void setParameters() {Camera.Parameters parameters = mCamera.getParameters();List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();for (Camera.Size size : supportedPreviewSizes) {if (size.width <= 360 && size.width >= 180) {previewSize = size;Log.d(TAG, "select size width=" + size.width + ",height=" + size.height);break;}}List<int[]> supportedPreviewFpsRange = parameters.getSupportedPreviewFpsRange();int[] destRange = {30 * 1000, 30 * 1000};for (int[] range : supportedPreviewFpsRange) {if (range[PREVIEW_FPS_MIN_INDEX] >= 30 * 1000 && range[PREVIEW_FPS_MAX_INDEX] <= 100 * 1000) {destRange = range;break;}}parameters.setPreviewSize(previewSize.width, previewSize.height);parameters.setPreviewFpsRange(destRange[PREVIEW_FPS_MIN_INDEX],destRange[PREVIEW_FPS_MAX_INDEX]);parameters.setPreviewFormat(ImageFormat.NV21);parameters.setFocusMode(FOCUS_MODE_AUTO);mCamera.setParameters(parameters);}private static final String TAG = "MainActivity";private Camera.Size previewSize;@Overridepublic void surfaceRedrawNeeded(SurfaceHolder holder) {}@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {initCamera();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}public Camera.PreviewCallback getPreviewCallback() {return new Camera.PreviewCallback() {byte[] dstByte = new byte[calculateFrameSize(ImageFormat.NV21)];@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {if (data == null) {mCamera.addCallbackBuffer(new byte[calculateFrameSize(ImageFormat.NV21)]);} else {if (isStarted) {if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar) {Yuv420Util.Nv21ToYuv420SP(data, dstByte, previewSize.width, previewSize.height);} else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar) {Yuv420Util.Nv21ToI420(data, dstByte, previewSize.width, previewSize.height);} else if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar) {Yuv420Util.Nv21ToYuv420SP(data, dstByte, previewSize.width, previewSize.height);} else {System.arraycopy(data, 0, dstByte, 0, data.length);}onGetVideoFrame(dstByte);}mCamera.addCallbackBuffer(data);}}};}private MediaCodec.BufferInfo vBufferInfo = new MediaCodec.BufferInfo();private void onGetVideoFrame(byte[] i420) {
ByteBuffer[] inputBuffers = vencoder.getInputBuffers();ByteBuffer[] outputBuffers = vencoder.getOutputBuffers();int inputBufferId = vencoder.dequeueInputBuffer(-1);if (inputBufferId >= 0) {ByteBuffer bb = inputBuffers[inputBufferId];bb.clear();bb.put(i420, 0, i420.length);long pts = new Date().getTime() * 1000 - presentationTimeUs;vencoder.queueInputBuffer(inputBufferId, 0, i420.length, pts, 0);}for (; ; ) {int outputBufferId = vencoder.dequeueOutputBuffer(vBufferInfo, 0);if (outputBufferId >= 0) {ByteBuffer bb = outputBuffers[outputBufferId];onEncodedh264Frame(bb, vBufferInfo);vencoder.releaseOutputBuffer(outputBufferId, false);}if (outputBufferId < 0) {break;}}}private void onEncodedh264Frame(ByteBuffer bb, MediaCodec.BufferInfo vBufferInfo) {int offset = 4;if (bb.get(2) == 0x01) {offset = 3;}int type = bb.get(offset) & 0x1f;switch (type) {case NAL_SLICE:Log.d(TAG, "type=NAL_SLICE");break;case NAL_SLICE_DPA:Log.d(TAG, "type=NAL_SLICE_DPA");break;case NAL_SLICE_DPB:Log.d(TAG, "type=NAL_SLICE_DPB");break;case NAL_SLICE_DPC:Log.d(TAG, "type=NAL_SLICE_DPC");break;case NAL_SLICE_IDR: Log.d(TAG, "type=NAL_SLICE_IDR");break;case NAL_SEI:Log.d(TAG, "type=NAL_SEI");break;case NAL_SPS: Log.d(TAG, "type=NAL_SPS");byte[] pps = new byte[4];byte[] sps = new byte[vBufferInfo.size - 12];bb.getInt();bb.get(sps, 0, sps.length);bb.getInt();bb.get(pps, 0, pps.length);Log.d(TAG, "解析得到 sps:" + Arrays.toString(sps) + ",PPS=" + Arrays.toString(pps));Rtmp.sendSpsAndPps(sps, sps.length, pps, pps.length, vBufferInfo.presentationTimeUs / 1000);return;case NAL_PPS: Log.d(TAG, "type=NAL_PPS");break;case NAL_AUD:Log.d(TAG, "type=NAL_AUD");break;case NAL_FILLER:Log.d(TAG, "type=NAL_FILLER");break;}byte[] bytes = new byte[vBufferInfo.size];bb.get(bytes);Rtmp.sendVideoFrame(bytes, bytes.length, vBufferInfo.presentationTimeUs / 1000);}private int calculateFrameSize(int format) {return previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(format) / 8;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
Yuv420Util.java
package com.blueberry.hellortmp;/*** Created by blueberry on 1/13/2017.*/public class Yuv420Util {public static void Nv21ToI420(byte[] data, byte[] dstData, int w, int h) {int size = w * h;System.arraycopy(data, 0, dstData, 0, size);for (int i = 0; i < size / 4; i++) {dstData[size + i] = data[size + i * 2 + 1]; dstData[size + size / 4 + i] = data[size + i * 2]; }}public static void Nv21ToYuv420SP(byte[] data, byte[] dstData, int w, int h) {int size = w * h;System.arraycopy(data, 0, dstData, 0, size);for (int i = 0; i < size / 4; i++) {dstData[size + i * 2] = data[size + i * 2 + 1]; dstData[size + i * 2 + 1] = data[size + i * 2]; }}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
public.c
#include <jni.h>#include "rtmp.h"
#include "rtmp_sys.h"
#include "log.h"
#include "android/log.h"
#include "time.h"#define TAG "RTMP"#define RTMP_HEAD_SIZE (sizeof(RTMPPacket)+RTMP_MAX_HEADER_SIZE)#define NAL_SLICE 1
#define NAL_SLICE_DPA 2
#define NAL_SLICE_DPB 3
#define NAL_SLICE_DPC 4
#define NAL_SLICE_IDR 5
#define NAL_SEI 6
#define NAL_SPS 7
#define NAL_PPS 8
#define NAL_AUD 9
#define NAL_FILLER 12#define LOGD(fmt, ...) \__android_log_print(ANDROID_LOG_DEBUG,TAG,fmt,##__VA_ARGS__);
RTMP *rtmp;RTMPPacket *packet = NULL;
unsigned char *body;
long start_time;
int send_video_sps_pps(unsigned char *sps, int sps_len, unsigned char *pps, int pps_len);int send_rtmp_video(unsigned char *buf, int len, long time);int stop();JNIEXPORT jint JNICALL
Java_com_blueberry_hellortmp_Rtmp_init(JNIEnv *env, jclass type, jstring url_, jint timeOut) {const char *url = (*env)->GetStringUTFChars(env, url_, 0);int ret;RTMP_LogSetLevel(RTMP_LOGDEBUG);rtmp = RTMP_Alloc(); RTMP_Init(rtmp);rtmp->Link.timeout = timeOut;RTMP_SetupURL(rtmp, url);RTMP_EnableWrite(rtmp);if ((ret = RTMP_Connect(rtmp, NULL)) <= 0) {LOGD("rtmp connet error");return (*env)->NewStringUTF(env, "error");}if ((ret = RTMP_ConnectStream(rtmp, 0)) <= 0) {LOGD("rtmp connect stream error");}(*env)->ReleaseStringUTFChars(env, url_, url);return ret;
}JNIEXPORT jint JNICALL
Java_com_blueberry_hellortmp_Rtmp_sendSpsAndPps(JNIEnv *env, jclass type, jbyteArray sps_,jint spsLen, jbyteArray pps_, jint ppsLen,jlong time) {jbyte *sps = (*env)->GetByteArrayElements(env, sps_, NULL);jbyte *pps = (*env)->GetByteArrayElements(env, pps_, NULL);int ret = send_video_sps_pps((unsigned char *) sps, spsLen, (unsigned char *) pps, ppsLen);start_time = time;(*env)->ReleaseByteArrayElements(env, sps_, sps, 0);(*env)->ReleaseByteArrayElements(env, pps_, pps, 0);return ret;
}JNIEXPORT jint JNICALL
Java_com_blueberry_hellortmp_Rtmp_sendVideoFrame(JNIEnv *env, jclass type, jbyteArray frame_,jint len, jlong time) {jbyte *frame = (*env)->GetByteArrayElements(env, frame_, NULL);int ret = send_rtmp_video((unsigned char *) frame, len, time);(*env)->ReleaseByteArrayElements(env, frame_, frame, 0);return ret;
}int stop() {RTMP_Close(rtmp);RTMP_Free(rtmp);
}
int send_video_sps_pps(unsigned char *sps, int sps_len, unsigned char *pps, int pps_len) {int i;packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + 1024);memset(packet, 0, RTMP_HEAD_SIZE);packet->m_body = (char *) packet + RTMP_HEAD_SIZE;body = (unsigned char *) packet->m_body;i = 0;body[i++] = 0x17; body[i++] = 0x00; body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00; body[i++] = 0x01;body[i++] = sps[1]; body[i++] = sps[2]; body[i++] = sps[3]; body[i++] = 0xff;body[i++] = 0xe1;body[i++] = (sps_len >> 8) & 0xff;body[i++] = sps_len & 0xff;memcpy(&body[i], sps, sps_len);i += sps_len;body[i++] = 0x01;body[i++] = (pps_len >> 8) & 0xff;body[i++] = pps_len & 0xff;memcpy(&body[i], pps, pps_len);i += pps_len;packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nBodySize = i;packet->m_nChannel = 0x04;packet->m_nTimeStamp = 0;packet->m_hasAbsTimestamp = 0;packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;packet->m_nInfoField2 = rtmp->m_stream_id;if (RTMP_IsConnected(rtmp)) {RTMP_SendPacket(rtmp, packet, TRUE);}free(packet);return 0;
}
int send_rtmp_video(unsigned char *buf, int len, long time) {int type;long timeOffset;timeOffset = time - start_time;if (buf[2] == 0x00) {buf += 4;len -= 4;} else if (buf[2] == 0x01) {buf += 3;len - 3;}type = buf[0] & 0x1f;packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + len + 9);memset(packet, 0, RTMP_HEAD_SIZE);packet->m_body = (char *) packet + RTMP_HEAD_SIZE;packet->m_nBodySize = len + 9;body = (unsigned char *) packet->m_body;memset(body, 0, len + 9);body[0] = 0x27;if (type == NAL_SLICE_IDR) {body[0] = 0x17; }body[1] = 0x01;body[2] = 0x00;body[3] = 0x00;body[4] = 0x00;body[5] = (len >> 24) & 0xff;body[6] = (len >> 16) & 0xff;body[7] = (len >> 8) & 0xff;body[8] = (len) & 0xff;memcpy(&body[9], buf, len);packet->m_hasAbsTimestamp = 0;packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;packet->m_nInfoField2 = rtmp->m_stream_id;packet->m_nChannel = 0x04;packet->m_headerType = RTMP_PACKET_SIZE_LARGE;packet->m_nTimeStamp = timeOffset;if (RTMP_IsConnected(rtmp)) {RTMP_SendPacket(rtmp, packet, TRUE);}free(packet);
}JNIEXPORT jint JNICALL
Java_com_blueberry_hellortmp_Rtmp_stop(JNIEnv *env, jclass type) {stop();return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
可以安装Adobe Media Server进行观看
代码地址:https://github.com/blueberryCoder/Media/tree/master/HelloRtmp
本文参考:
https://my.oschina.net/jerikc/blog/501948