Based on multi-channel live screen cast IjkPlayer

This article describes a multi-channel live screen cast, mainly intelligent interactive conference, while many people voted screen application scenarios, but not limited thereto. In real life, multi-channel video surveillance applications had already appeared. In order to improve the efficiency of communication meeting, multiplayer collaborative, multi-screen interactive scenes came into being cast. Meeting to vote on-screen real-time requirements are very high, can now do live video streaming 1080P delay of about 130ms, than the game live, live anchor delay requirements much higher. Therefore, based on the second modification do IjkPlayer, from the buffer queue, the decoding time-consuming, three optimizing render queue.

Related article:

RTSP broadcast delay depth optimization

FFmpeg from the source to solve the problem Huaping live IJKPlayer

 

1, page layout

With horizontal, vertical two lines dividing the entire screen into four screens, one for each four channels IjkVideoView.

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/divider1"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/ijk_color_blue_800"
        android:layout_centerVertical="true"/>

    <View
        android:id="@+id/divider2"
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:background="@color/ijk_color_blue_800"
        android:layout_centerHorizontal="true"/>

    <com.frank.living.widget.IjkVideoView
        android:id="@+id/video_view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/divider1"
        android:layout_toStartOf="@id/divider2"/>

    <com.frank.living.widget.IjkVideoView
        android:id="@+id/video_view2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/divider1"
        android:layout_toEndOf="@id/divider2"/>

    <com.frank.living.widget.IjkVideoView
        android:id="@+id/video_view3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/divider1"
        android:layout_toStartOf="@id/divider2"/>

    <com.frank.living.widget.IjkVideoView
        android:id="@+id/video_view4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/divider1"
        android:layout_toEndOf="@id/divider2"/>

</RelativeLayout>

2, the player parameter initialization

IjkPlayer player parameter is divided into: PLAYER, FORMAT, CODEC, SWS four categories. Comprising a probe packet number, duration stream analysis, TCP / UDP connection, a loop filter, frame dropping network Caton, hard decoding configuration, buffer size and so on.

private void setOptions(IjkMediaPlayer ijkPlayer){
    if (ijkPlayer == null)
        return;
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", 1);//不额外优化
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 200);
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);//是否开启缓冲
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);
    //0:代表关闭,1:代表开启
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);//开启硬解
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);//自动旋屏
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);//处理分辨率变化

    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 0);//最大缓存数
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2);//默认最小帧数2
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 30);//最大缓存时长
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);//是否限制输入缓存数
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
    //设置播放前的最大探测时间,分析码流时长:默认1024*1000
    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100);
    //ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");//tcp传输数据
}

3, initialize player

Create a player and initialized. When single-screen cast, we default to full screen.

private void setupView(){
    //第一路投屏默认全屏
    enterFullScreen(1);
    mVideoView1.setVideoPath(url);
    mVideoView1.setIjkPlayerListener(new IjkPlayerListener() {
        @Override
        public void onIjkPlayer(IjkMediaPlayer ijkMediaPlayer) {
            //设置播放器option
            setOptions(ijkMediaPlayer);
        }
    });
    mVideoView1.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(IMediaPlayer iMediaPlayer) {

            ijkPlayer = iMediaPlayer;
        }
    });
    mVideoView1.start();
}

4, cast-screen channel processing

We use a HashMap to hold the vote screen ip client correspondence between the number of channels, in addition to recording each channel using TreeMap voted screen state.

//四分屏模式还是全屏模式
private boolean isMultiScreen;
//保存客户端ip与通道数对应关系
private HashMap<String, Integer> clientMap = new HashMap<>();
//记录每个通道的投屏状态
private TreeMap<Integer, Boolean> channelMap = new TreeMap<>();

5, increase investment and screen

Upon receiving the broadcast screen cast increases, select an idle channel to vote screen. Special attention is required when the second path to vote screen, automatically switch to full-screen multi-screen mode.

  int clientNum = intent.getIntExtra("clientNum", 0);
  String otherUrl = intent.getStringExtra("url");
  String ipAddress = intent.getStringExtra("ip");
  //选择空闲通道
  int channel = selectIdleChannel(clientNum);
  clientMap.put(ipAddress, channel);
  channelMap.put(channel, true);
  addClient(channel, otherUrl);
  //单屏变为两路投屏时,自动切换为多屏模式
  if (clientNum == 2){
      exitFullScreen();
  }
/**
 * 选择空闲通道
 * @param clientNum clientNum
 * @return idleChannel
 */
private int selectIdleChannel(int clientNum){
    for (int channel = 1; channel < clientNum; channel++){
        if (!channelMap.get(channel)){
            return channel;
        }
    }
    return clientNum;
}

 

6, screen cast removed

Upon receipt of the broadcast screen cast is removed, to be removed according to the client's ip address, find the need to be removed from the HashMap traversal client. If the current screen is the total number of two-way investment, which removed all the way, all the way to the last remaining cast screen automatically switches to full screen.

int num = intent.getIntExtra("clientNum", 0);
if (num == 0){
    Process.killProcess(Process.myPid());
}else if (num > 0){
    String ipAddress = intent.getStringExtra("ipAddress");
    int target = clientMap.get(ipAddress);
    removeClient(target);
    clientMap.remove(ipAddress);
    channelMap.put(target, false);
    //多屏变为单屏时,自动切换为全屏
    if (num == 1){
        int castingChannel = getCastingChannel();
        enterFullScreen(castingChannel);
    }
}

7, with the full-screen split-screen switching

Double-screen when a road cast channel, and multi-screen full screen of each switch.

/**
 * 进入全屏模式
 * @param channel channel
 */
private void enterFullScreen(int channel){
    hideDivider();
    switch (channel){
        case 1:
            mVideoView1.setVisibility(View.VISIBLE);
            mVideoView2.setVisibility(View.GONE);
            mVideoView3.setVisibility(View.GONE);
            mVideoView4.setVisibility(View.GONE);
            break;
        case 2:
            mVideoView1.setVisibility(View.GONE);
            mVideoView2.setVisibility(View.VISIBLE);
            mVideoView3.setVisibility(View.GONE);
            mVideoView4.setVisibility(View.GONE);
            break;
        case 3:
            mVideoView1.setVisibility(View.GONE);
            mVideoView2.setVisibility(View.GONE);
            mVideoView3.setVisibility(View.VISIBLE);
            mVideoView4.setVisibility(View.GONE);
            break;
        case 4:
            mVideoView1.setVisibility(View.GONE);
            mVideoView2.setVisibility(View.GONE);
            mVideoView3.setVisibility(View.GONE);
            mVideoView4.setVisibility(View.VISIBLE);
            break;
        default:
            break;
    }
}
/**
 * 退出全屏模式
 */
private void exitFullScreen(){
    showDivider();
    mVideoView1.setVisibility(View.VISIBLE);
    mVideoView2.setVisibility(View.VISIBLE);
    mVideoView3.setVisibility(View.VISIBLE);
    mVideoView4.setVisibility(View.VISIBLE);
}

Flag recorded by the current full-screen mode or the multi-screen mode, and then switch to full-screen / split screen according to the flag bit.

/**
 * 切换分屏模式
 * @param channel channel
 */
private void changeScreenMode(int channel){
    isMultiScreen = !isMultiScreen;
    if (isMultiScreen){
        enterFullScreen(channel);
    }else {
        exitFullScreen();
    }
}

At this point, we can achieve the basic functions of multi-channel live screen cast. 5G wait era, multi-screen interaction will become more mature, and gradually into real life.

发布了63 篇原创文章 · 获赞 179 · 访问量 18万+

Guess you like

Origin blog.csdn.net/u011686167/article/details/88048139