美文网首页
Android Media 09 --- RTMP推流(libr

Android Media 09 --- RTMP推流(libr

作者: 沪漂意哥哥 | 来源:发表于2022-04-24 07:17 被阅读0次

一.CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(librtmp)
add_library(
       native-lib
       SHARED
       native-lib.cpp )

find_library(
       log-lib
       log )

target_link_libraries(
       native-lib
       ${log-lib}
       rtmp)

二.MainActivity

public class MainActivity extends AppCompatActivity   {
   private MediaProjectionManager mediaProjectionManager;
   private MediaProjection mediaProjection;
   ScreenLive screenLive;
   String url = "rtmp://live-push.bilivideo.com/live-bvc/?streamname=live_436361523_69672384&key=654ce7137e852ab60fde72836c815a63&schedule=rtmp";

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       checkPermission();
   }

   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);
       if (requestCode == 100 && resultCode == Activity.RESULT_OK) {
           mediaProjection = mediaProjectionManager.getMediaProjection
                   (resultCode, data);
           screenLive = new ScreenLive();
           screenLive.startLive(url, mediaProjection);
       }
   }

   public void startLive(View view) {
       this.mediaProjectionManager = (MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE);
       Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent();
       startActivityForResult(captureIntent, 100);
   }

   public boolean checkPermission() {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
               Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
           requestPermissions(new String[]{
                   Manifest.permission.READ_EXTERNAL_STORAGE,
                   Manifest.permission.WRITE_EXTERNAL_STORAGE,
                   Manifest.permission.CAMERA
           }, 1);
       }
       return false;
   }


   public void stopLive(View view) {
   }
}

三.RTMPPackage

public class RTMPPackage {
   private byte[] buffer;
   private long tms;

   public RTMPPackage(byte[] buffer, long tms) {
       this.buffer = buffer;
       this.tms = tms;
   }

   public byte[] getBuffer() {
       return buffer;
   }

   public void setBuffer(byte[] buffer) {
       this.buffer = buffer;
   }

   public long getTms() {
       return tms;
   }

   public void setTms(long tms) {
       this.tms = tms;
   }
}

四.VideoCodec

public class VideoCodec extends  Thread {
   private MediaProjection mediaProjection;
   private VirtualDisplay virtualDisplay;
   private MediaCodec mediaCodec;
   private ScreenLive screenLive;
   private boolean isLiving;
   private long timeStamp;
   private long startTime;
   public VideoCodec(ScreenLive screenLive) {
       this.screenLive = screenLive;
   }

   public void startLive(MediaProjection mediaProjection) {
       this.mediaProjection = mediaProjection;
       MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 720, 1280);
       format.setInteger(MediaFormat.KEY_COLOR_FORMAT,  MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
       format.setInteger(MediaFormat.KEY_BIT_RATE, 400_000);
       format.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
       format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
       try {
           mediaCodec = MediaCodec.createEncoderByType("video/avc");
           mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
           Surface surface = mediaCodec.createInputSurface();
           virtualDisplay = mediaProjection.createVirtualDisplay(
                   "screen-codec",
                   720, 1280, 1,
                   DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                   surface, null, null);
       } catch (IOException e) {
           e.printStackTrace();
       }
       start();
   }

   @Override
   public void run() {
       isLiving = true;
       mediaCodec.start();
       MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
       while (isLiving) {
           if (System.currentTimeMillis() - timeStamp >= 2000) {
               Bundle params = new Bundle();
               params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
               //dsp 芯片触发I帧
               mediaCodec.setParameters(params);
               timeStamp = System.currentTimeMillis();
           }
           int index = mediaCodec.dequeueOutputBuffer(bufferInfo, 100000);
           if (index >= 0) {
               if (startTime == 0) {
                   startTime = bufferInfo.presentationTimeUs / 1000;
               }
               ByteBuffer buffer = mediaCodec.getOutputBuffer(index);
               byte[] outData = new byte[bufferInfo.size];
               buffer.get(outData);
               RTMPPackage rtmpPackage = new RTMPPackage(outData, (bufferInfo.presentationTimeUs / 1000) - startTime);
               screenLive.addPackage(rtmpPackage);
               mediaCodec.releaseOutputBuffer(index, false);
           }
       }
       isLiving = false;
       mediaCodec.stop();
       mediaCodec.release();
       mediaCodec = null;
       virtualDisplay.release();
       virtualDisplay = null;
       mediaProjection.stop();
       mediaProjection = null;
       startTime = 0;
   }


}

五.ScreenLive

public class ScreenLive extends Thread {
   private String url;
   private MediaProjection mediaProjection;
   private LinkedBlockingQueue<RTMPPackage> queue = new LinkedBlockingQueue<>();
   private boolean isLiving;
   static {
       System.loadLibrary("native-lib");
   }

   public void startLive(String url, MediaProjection mediaProjection) {
       this.url = url;
       this.mediaProjection = mediaProjection;
       start();
   }

   @Override
   public void run() {
       if (!connect(url)) {
           Log.i("liuyi", "run: ----------->推送失败");
           return;
       }

       VideoCodec videoCodec = new VideoCodec(this);
       videoCodec.startLive(mediaProjection);

       isLiving = true;
       while (isLiving) {
           RTMPPackage rtmpPackage = null;
           try {
               rtmpPackage = queue.take();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           if (rtmpPackage.getBuffer() != null && rtmpPackage.getBuffer().length != 0) {
               Log.i("liuyi","java sendData");
               sendData(rtmpPackage.getBuffer(), rtmpPackage.getBuffer() .length , rtmpPackage.getTms());
           }
       }
   }

   public void addPackage(RTMPPackage rtmpPackage) {
       if (!isLiving) {
           return;
       }
       queue.add(rtmpPackage);
   }

   private native boolean connect(String url);

   private native boolean sendData(byte[] data, int len, long tms);

}

六.native-lib.cpp

#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_INFO,"liuyi",__VA_ARGS__)

extern "C"{
#include  "librtmp/rtmp.h"
}

typedef  struct {
   RTMP *rtmp;
   int16_t sps_len;
   int8_t *sps;
   int16_t pps_len;
   int8_t *pps;
}Live;
Live *live = NULL;

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_luisliuyi_demo_camera1_ScreenLive_connect(JNIEnv *env, jobject thiz, jstring url_) {
   const char *url = env->GetStringUTFChars(url_, 0);
   int ret;
   do {
       live = (Live*)malloc(sizeof(Live));
       memset(live, 0, sizeof(Live));

       live->rtmp = RTMP_Alloc();
       RTMP_Init(live->rtmp);

       live->rtmp->Link.timeout = 10;

       LOGE("connect %s", url);
       if (!(ret = RTMP_SetupURL(live->rtmp, (char*)url))) break;

       RTMP_EnableWrite(live->rtmp);

       LOGE("RTMP_Connect");
       if (!(ret = RTMP_Connect(live->rtmp, 0))) break;

       LOGE("RTMP_ConnectStream ");
       if (!(ret = RTMP_ConnectStream(live->rtmp, 0))) break;
       LOGE("connect success");
   }  while (0);

   if (!ret && live) {
       free(live);
       live = nullptr;
   }

   env->ReleaseStringUTFChars(url_, url);
   return ret;
}

// 传递第一帧 00 00 00 01 67 64 00 28ACB402201E3CBCA41408081B4284D4  00000001 68 EE 06 F2 C0
void prepareVideo(int8_t *data, int len, Live *live) {
   for (int i = 0; i < len; i++) {
       if (i + 4 < len) {
           if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x00 && data[i + 3] == 0x01) {
               if (data[i + 4]  == 0x68) {
                   //sps解析
                   live->sps_len = i - 4;
                   live->sps = static_cast<int8_t *>(malloc(live->sps_len));
                   memcpy(live->sps, data + 4, live->sps_len);

                   //pps解析
                   live->pps_len = len - (4 + live->sps_len) - 4;
                   live->pps = static_cast<int8_t *>(malloc(live->pps_len));
                   memcpy(live->pps, data + 4 + live->sps_len + 4, live->pps_len);
                   break;
               }
           }
       }
   }
}

//sps  pps 的 packaet
RTMPPacket *createVideoPackage(Live *live) {
   int body_size = 16 + live->sps_len + live->pps_len; //为什么是16????参考rtmp视频包结构
   RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
   RTMPPacket_Alloc(packet, body_size);
   int i = 0;
   packet->m_body[i++] = 0x17;
   packet->m_body[i++] = 0x00;
   packet->m_body[i++] = 0x00;
   packet->m_body[i++] = 0x00;
   packet->m_body[i++] = 0x00;

   packet->m_body[i++] = 0x01;

   packet->m_body[i++] = live->sps[1]; //profile 如baseline、main、 high
   packet->m_body[i++] = live->sps[2]; //profile_compatibility 兼容性
   packet->m_body[i++] = live->sps[3]; //profile level

   packet->m_body[i++] = 0xFF;
   packet->m_body[i++] = 0xE1;

   //sps length
   packet->m_body[i++] = (live->sps_len >> 8) & 0xFF;//高八位
   packet->m_body[i++] = live->sps_len & 0xff;//低八位

   //拷贝sps的内容
   memcpy(&packet->m_body[i], live->sps, live->sps_len);

   i +=live->sps_len;

   packet->m_body[i++] = 0x01;

   //pps length
   packet->m_body[i++] = (live->pps_len >> 8) & 0xff; //高八位
   packet->m_body[i++] = live->pps_len & 0xff;//低八位

   // 拷贝pps内容
   memcpy(&packet->m_body[i], live->pps, live->pps_len);

   //视频类型
   packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
   packet->m_nBodySize = body_size;
   packet->m_nChannel = 0x04;
   packet->m_nTimeStamp = 0;
   packet->m_hasAbsTimestamp = 0;
   packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
   packet->m_nInfoField2 = live->rtmp->m_stream_id;
   return packet;
}

RTMPPacket *createVideoPackage(int8_t *buf, int len, const long tms, Live *live) {
   buf += 4;
   RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
   int body_size = len + 9;

   //初始化RTMP内部的body数组
   RTMPPacket_Alloc(packet, body_size);

   if (buf[0] == 0x65) {//
       packet->m_body[0] = 0x17;
       LOGE("发送关键帧 data");
   } else{
       packet->m_body[0] = 0x27;
       LOGE("发送非关键帧 data");
   }

   packet->m_body[1] = 0x01;
   packet->m_body[2] = 0x00;
   packet->m_body[3] = 0x00;
   packet->m_body[4] = 0x00;

   //长度
   packet->m_body[5] = (len >> 24) & 0xff;
   packet->m_body[6] = (len >> 16) & 0xff;
   packet->m_body[7] = (len >> 8) & 0xff;
   packet->m_body[8] = (len) & 0xff;

   //数据
   memcpy(&packet->m_body[9], buf, len);//为什么是9????参考rtmp视频包结构

   packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
   packet->m_nBodySize = body_size;
   packet->m_nChannel = 0x04;
   packet->m_nTimeStamp = tms;
   packet->m_hasAbsTimestamp = 0;
   packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
   packet->m_nInfoField2 = live->rtmp->m_stream_id;
   return packet;
}

int sendPacket(RTMPPacket *packet) {
   int r = RTMP_SendPacket(live->rtmp, packet, 1);
   if(r){
       LOGE("发送rtmp包成功");
   }
   RTMPPacket_Free(packet);
   free(packet);
   return r;
}

// 传递第一帧 00 00 00 01 67 64 00 28ACB402201E3CBCA41408081B4284D4  0000000168 EE 06 F2 C0
int sendVideo(int8_t *buf, int len, long tms) {
   int ret = 0;
   if (buf[4] == 0x67) {
       // 缓存sps 和pps 到全局遍历 不需要推流
       if (live && (!live->pps || !live->sps)) {
           prepareVideo(buf, len, live);
       }
       return ret;
   }

   if (buf[4] == 0x65) {//关键帧
       RTMPPacket *packet = createVideoPackage(live);
       sendPacket(packet);
   }

   RTMPPacket *packet2 = createVideoPackage(buf, len, tms, live);
   ret = sendPacket(packet2);
   return ret;
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_luisliuyi_demo_camera1_ScreenLive_sendData(JNIEnv *env, jobject thiz, jbyteArray data_,
                                                   jint len, jlong tms) {
   int ret;
   jbyte *data = env->GetByteArrayElements(data_, NULL);
   ret = sendVideo(data, len, tms);
   env->ReleaseByteArrayElements(data_, data, 0);
   return ret;
}

七.代码地址

https://gitee.com/luisliuyi/android-rtmp-mediaprojection.git

相关文章

网友评论

      本文标题:Android Media 09 --- RTMP推流(libr

      本文链接:https://www.haomeiwen.com/subject/tbttyrtx.html