使用librtmp推送AVC数据——Android端

2023-10-22 02:50

本文主要是介绍使用librtmp推送AVC数据——Android端,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/a992036795/article/details/54572335


一、前言 
本文要讲述的是将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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

分别的作用: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帧

/*** H.264 的编码信息帧是发送给 RTMP 服务器称为 AVC sequence header,* RTMP 服务器只有收到 AVC sequence header 中的 sps, pps 才能解析后续发送的 H264 帧。*/
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; //1:keyframe 7:AVCbody[i++] = 0x00; // AVC sequence headerbody[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00; //fill in 0/*AVCDecoderConfigurationRecord*/body[i++] = 0x01;body[i++] = sps[1]; //AVCProfileIndecationbody[i++] = sps[2]; //profile_compatibiltybody[i++] = sps[3]; //AVCLevelIndicationbody[i++] = 0xff;//lengthSizeMinusOne/*SPS*/body[i++] = 0xe1;body[i++] = (sps_len >> 8) & 0xff;body[i++] = sps_len & 0xff;/*sps data*/memcpy(&body[i], sps, sps_len);i += sps_len;/*PPS*/body[i++] = 0x01;/*sps data length*/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、发送视频数据

//sps 与 pps 的帧界定符都是 00 00 00 01,而普通帧可能是 00 00 00 01 也有可能 00 00 01
int send_rtmp_video(unsigned char *buf, int len, long time) {int type;long timeOffset;timeOffset = time - start_time;/*start_time为开始直播的时间戳*//*去掉帧界定符*/if (buf[2] == 0x00) {/*00 00 00 01*/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;/* send video packet*/body = (unsigned char *) packet->m_body;memset(body, 0, len + 9);/*key frame*/body[0] = 0x27;if (type == NAL_SLICE_IDR) {body[0] = 0x17; //关键帧}body[1] = 0x01;/*nal unit*/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;/*copy data*/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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

完整代码: 
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;  // compensate the mirror} else {  // back-facingresult = (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) {// data 是Nv21if (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) {// Yuv420packedPlannar 和 yuv420sp很像// 区别在于 加入 width = 4的话 y1,y2,y3 ,y4公用 u1v1// 而 yuv420dp 则是 y1y2y5y6 共用 u1v1//http://blog.csdn.net/jumper511/article/details/21719313//这样处理的话颜色核能会有些失真。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) {
//        MediaCodecByteBuffer[] inputBuffers = vencoder.getInputBuffers();ByteBuffer[] outputBuffers = vencoder.getOutputBuffers();int inputBufferId = vencoder.dequeueInputBuffer(-1);if (inputBufferId >= 0) {// fill inputBuffers[inputBufferId] with valid dataByteBuffer 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) {// outputBuffers[outputBufferId] is ready to be processed or rendered.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: // spsLog.d(TAG, "type=NAL_SPS");//[0, 0, 0, 1, 103, 66, -64, 13, -38, 5, -126, 90, 1, -31, 16, -115, 64, 0, 0, 0, 1, 104, -50, 6, -30]//打印发现这里将 SPS帧和 PPS帧合在了一起发送// SPS为 [4,len-8]// PPS为后4个字节//so .byte[] pps = new byte[4];byte[] sps = new byte[vBufferInfo.size - 12];bb.getInt();// 抛弃 0,0,0,1bb.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: // ppsLog.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;// YSystem.arraycopy(data, 0, dstData, 0, size);for (int i = 0; i < size / 4; i++) {dstData[size + i] = data[size + i * 2 + 1]; //UdstData[size + size / 4 + i] = data[size + i * 2]; //V}}public static void Nv21ToYuv420SP(byte[] data, byte[] dstData, int w, int h) {int size = w * h;// YSystem.arraycopy(data, 0, dstData, 0, size);for (int i = 0; i < size / 4; i++) {dstData[size + i * 2] = data[size + i * 2 + 1]; //UdstData[size + i * 2 + 1] = data[size + i * 2]; //V}}
}
  • 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(const char *buf, int buflen, int type, unsigned int timestamp);
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空间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);
}/*** H.264 的编码信息帧是发送给 RTMP 服务器称为 AVC sequence header,* RTMP 服务器只有收到 AVC sequence header 中的 sps, pps 才能解析后续发送的 H264 帧。*/
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; //1:keyframe 7:AVCbody[i++] = 0x00; // AVC sequence headerbody[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00; //fill in 0/*AVCDecoderConfigurationRecord*/body[i++] = 0x01;body[i++] = sps[1]; //AVCProfileIndecationbody[i++] = sps[2]; //profile_compatibiltybody[i++] = sps[3]; //AVCLevelIndicationbody[i++] = 0xff;//lengthSizeMinusOne/*SPS*/body[i++] = 0xe1;body[i++] = (sps_len >> 8) & 0xff;body[i++] = sps_len & 0xff;/*sps data*/memcpy(&body[i], sps, sps_len);i += sps_len;/*PPS*/body[i++] = 0x01;/*sps data length*/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;
}//sps 与 pps 的帧界定符都是 00 00 00 01,而普通帧可能是 00 00 00 01 也有可能 00 00 01
int send_rtmp_video(unsigned char *buf, int len, long time) {int type;long timeOffset;timeOffset = time - start_time;/*start_time为开始直播的时间戳*//*去掉帧界定符*/if (buf[2] == 0x00) {/*00 00 00 01*/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;/* send video packet*/body = (unsigned char *) packet->m_body;memset(body, 0, len + 9);/*key frame*/body[0] = 0x27;if (type == NAL_SLICE_IDR) {body[0] = 0x17; //关键帧}body[1] = 0x01;/*nal unit*/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;/*copy data*/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








这篇关于使用librtmp推送AVC数据——Android端的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/258634

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传