美文网首页
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