Many developers, when doing the smart classroom with the same screen, conference with the same screen and so on, based on the acquisition of The Andriod platform, often encounter a variety of problems, the following points, to draw inspiration:

1. Multicast or RTMP in an Intranet environment?

Answer: this question, asked by countless developers, therefore, a separate wrote blog argument: blog.csdn.net/renhui1112/… If it is the same screen with no concurrency bottleneck in Intranet environment, you can start the built-in RTSP service (unicast), and then pull flow of other terminals is also a good solution.

2. How to set or scale the push resolution?

Answer: generally speaking, a lot of Android devices, especially high screen, get the video of the original width is very high, if the original resolution, coding and upward pressure big, so the general advice, appropriate scale, such as high zoom to two-thirds, wide zoom general advice such as scaling, in addition, the scaling of wide high recommended 16 byte alignment.

Without further ado, example code:

    private void createScreenEnvironment() {

        sreenWindowWidth = mWindowManager.getDefaultDisplay().getWidth();
        screenWindowHeight = mWindowManager.getDefaultDisplay().getHeight();

        Log.i(TAG, "screenWindowWidth: " + sreenWindowWidth + ",screenWindowHeight: "
                + screenWindowHeight);

        if (sreenWindowWidth > 800)
        {
            if (screenResolution == SCREEN_RESOLUTION_STANDARD)
            {
                scale_rate = SCALE_RATE_HALF;
                sreenWindowWidth = align(sreenWindowWidth / 2, 16);
                screenWindowHeight = align(screenWindowHeight / 2, 16);
            }
            else if(screenResolution == SCREEN_RESOLUTION_LOW)
            {
                scale_rate = SCALE_RATE_TWO_FIFTHS;
                sreenWindowWidth = align(sreenWindowWidth * 2 / 5, 16);

            }
        }

        Log.i(TAG, "After adjust mWindowWidth: " + sreenWindowWidth + ", mWindowHeight: " + screenWindowHeight);

        int pf = mWindowManager.getDefaultDisplay().getPixelFormat();
        Log.i(TAG, "display format:" + pf);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
        mScreenDensity = displayMetrics.densityDpi;

        mImageReader = ImageReader.newInstance(sreenWindowWidth,
                screenWindowHeight, 0x1, 6);

        mMediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    }
Copy the code

3. Horizontal and vertical screen automatic adaptation

Answer: because somehow screen state, the acquisition of high screen width is different, if somehow the screen switch, at this time, namely screen adaptation problems need to be taken into account, to ensure that such as vertical screen mode, switch to the landscape, can be automatically adapted on both ends of the push-pull flow, namely screen automatically adaptation, need to restart the encoder side, pull flow automatically to fit wide high change, the automatic playback.

4. Certain frame complement strategy

Answer: Many people don’t understand why want to fill the frame, in fact, the acquisition of the screen, the screen does not move, not always have the data, this time, a better approach is to save the last frame data, setting a certain frame interval, not because the frame spacing is too large, lead to broadcast data terminal receives less than a few seconds, and, of course, if the server can cache the GOP, The problem was solved.

5. Abnormal network handling and event callback mechanism

Answer: If RTMP is used, network jitter or other network exceptions occur, a good reconnection mechanism and status feedback mechanism are required.

    class EventHandeV2 implements NTSmartEventCallbackV2 {
        @Override
        public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

            Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

            String publisher_event = "";

            switch (id) {
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
                    publisher_event = "Start...";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
                    publisher_event = "In connection with...";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
                    publisher_event = "Connection failed..";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
                    publisher_event = "Connection successful..";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
                    publisher_event = "Connection down...";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
                    publisher_event = "Close..";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
                    publisher_event = "Start a new video file:" + param3;
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
                    publisher_event = "Generated a video file:" + param3;
                    break;

                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
                    publisher_event = "Transmission delay:" + param1 + "Frames." + param2;
                    break;

                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
                    publisher_event = "Snapshot:" + param1 + "Path:" + param3;

                    if (param1 == 0) {
                        publisher_event = publisher_event + "Snapshot intercepted successfully..";
                    } else {
                        publisher_event = publisher_event + "Failed to capture snapshot...";
                    }
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
                    publisher_event = "RTSP service URL:" + param3;
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:
                    publisher_event ="RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:
                    publisher_event ="Server does not support RTSP push, push RTSP URL:" + param3;
                    break;
            }

            String str = "Current callback status:" + publisher_event;

            Log.i(TAG, str);

            Message message = newMessage(); message.what = PUBLISHER_EVENT_MSG; message.obj = publisher_event; handler.sendMessage(message); }}Copy the code

6. Collect partial screen data

Answer: In many scenarios we encounter, 3/4 of the area in the classroom will be used for delivering to students, and 1/4 of the area will be used for doing some operations such as instructions. At this time, it is necessary to consider the screen area cutting, and the interface can be designed as follows:

	/** * Drop clipped RGBA data **@param data: RGBA data
	 *
	 * @param rowStride: stride information
	 *
	 * @param width: width
	 *
	 * @param height: height
	 *
	 * @paramClipedLeft: left; ClipedTop:; Clipedwidth: clipped width; ClipedHeight: clipped height; Make sure that the width and height of the cut down are even * *@return {0} if successful
	 */
	public native int SmartPublisherOnCaptureVideoClipedRGBAData(long handle,  ByteBuffer data, int rowStride, int width, int height, int clipedLeft, int clipedTop, int clipedWidth, int clipedHeight);
Copy the code
                        // The actual cutting ratio can be adjusted accordingly
                        int left = 100;
                        int cliped_left = 0;

                        int top = 0;
                        int cliped_top = 0;

                        int cliped_width = width_;
                        int cliped_height = height_;

                        if(scale_rate == SCALE_RATE_HALF)
                        {
                            cliped_left = left / 2;
                            cliped_top = top / 2;

                            // Width cropped to show 3/4 scale
                            cliped_width = (width_ *3) /4;
                            // Height is not clipped
                            cliped_height = height_;
                        }
                        else if(scale_rate == SCALE_RATE_TWO_FIFTHS)
                        {
                            cliped_left = left * 2 / 5;
                            cliped_top = top * 2 / 5;

                            // Width cropped to show 3/4 scale
                            cliped_width = (width_ *3) /4;
                            // Height is not clipped
                            cliped_height = height_;
                        }

                        if(cliped_width % 2! =0)
                        {
                            cliped_width = cliped_width + 1;
                        }

                        if(cliped_height % 2! =0)
                        {
                            cliped_height = cliped_height + 1;
                        }

                        if ( (cliped_left + cliped_width) > width_)
                        {
                            Log.e(TAG, " invalid cliped region settings, cliped_left: " + cliped_left + " cliped_width:" + cliped_width + " width:" + width_);

                            return;
                        }

                        if ( (cliped_top + cliped_height) > height_)
                        {
                            Log.e(TAG, "invalid cliped region settings, cliped_top: " + cliped_top + " cliped_height:" + cliped_height + " height:" + height_);

                            return;
                        }

                        //Log.i(TAG, " clipLeft: " + cliped_left + " clipTop: " + cliped_top + " clipWidth: " + cliped_width + " clipHeight: " + cliped_height);

                        libPublisher.SmartPublisherOnCaptureVideoClipedRGBAData(publisherHandle, last_buffer, row_stride_,
                                width_, height_, cliped_left, cliped_top, cliped_width, cliped_height );
Copy the code

7. Text and picture watermarking

Answer: In many scenarios, the user with the same screen will display the company logo and certain text information on the push terminal. At this time, the watermarking of text and picture needs to be considered. For details, please refer to the following interface Settings:

   /** * Set Text water-mark **@param fontSize: it should be "MEDIUM", "SMALL", "BIG"
     * 
     * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".
     * 
     * @param xPading, yPading: the distance of the original picture.
     * 
     * <pre> The interface is only used for setting font water-mark when publishing stream. </pre>  
     * 
     * @return {0} if successful
     */
    public native int SmartPublisherSetTextWatermark(long handle, String waterText, int isAppendTime, int fontSize, int waterPostion, int xPading, int yPading);
    
    
    /** * Set Text water-mark font file name **@param fontFileName:  font full file name, e.g: /system/fonts/DroidSansFallback.ttf
	 *
	 * @return {0} if successful
     */
    public native int SmartPublisherSetTextWatermarkFontFileName(long handle, String fontFileName);
	
    /** * Set picture water-mark **@param picPath: the picture working path, e.g: /sdcard/logo.png
     * 
     * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".
     * 
     * @param picWidth, picHeight: picture width & height
     * 
     * @param xPading, yPading: the distance of the original picture.
     * 
     * <pre> The interface is only used for setting picture(logo) water-mark when publishing stream, with "*.png" format </pre>  
     * 
     * @return {0} if successful
     */
    public native int SmartPublisherSetPictureWatermark(long handle, String picPath, int waterPostion, int picWidth, int picHeight, int xPading, int yPading);
Copy the code

Conclusion: In fact, a good same screen system, need to consider far more than the above points, such as encoding parameter strategy, etc., need to consider, and then have the opportunity to share with you further.