<commit_before>package net.ossrs.yasea;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import com.github.faucamp.simplertmp.RtmpHandler;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by winlin on 5/2/15.
 * Updated by leoma on 4/1/16.
 * to POST the h.264/avc annexb frame over RTMP.
 *
 * @see android.media.MediaMuxer https://developer.android.com/reference/android/media/MediaMuxer.html
 */
public class SrsFlvMuxer {

    private static final int VIDEO_ALLOC_SIZE = 128 * 1024;
    private static final int AUDIO_ALLOC_SIZE = 4 * 1024;

    private volatile boolean connected = false;
    private SrsRtmpPublisher publisher;
    private RtmpHandler mHandler;

    private Thread worker;
    private final Object txFrameLock = new Object();

    public SrsFlv flv = new SrsFlv();
    public boolean needToFindKeyFrame = true;
    private SrsFlvFrame mVideoSequenceHeader;
    private SrsFlvFrame mAudioSequenceHeader;
    private final SrsAllocator mVideoAllocator = new SrsAllocator(VIDEO_ALLOC_SIZE);
    private final SrsAllocator mAudioAllocator = new SrsAllocator(AUDIO_ALLOC_SIZE);
    private final ConcurrentLinkedQueue<SrsFlvFrame> mFlvTagCache = new ConcurrentLinkedQueue<>();

    public static final int VIDEO_TRACK = 100;
    public static final int AUDIO_TRACK = 101;
    private static final String TAG = "SrsFlvMuxer";

    /**
     * constructor.
     *
     * @param handler the rtmp event handler.
     */
    public SrsFlvMuxer(RtmpHandler handler) {
        mHandler = handler;
        publisher = new SrsRtmpPublisher(handler);
    }

    /**
     * get cached video frame number in publisher
     */
    public AtomicInteger getVideoFrameCacheNumber() {
        return publisher.getVideoFrameCacheNumber();
    }

    /**
     * set video resolution for publisher
     *
     * @param width  width
     * @param height height
     */
    public void setVideoResolution(int width, int height) {
        publisher.setVideoResolution(width, height);
    }

    /**
     * Adds a track with the specified format.
     *
     * @param format The media format for the track.
     * @return The track index for this newly added track.
     */
    public int addTrack(MediaFormat format) {
        if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
            flv.setVideoTrack(format);
            publisher.setVideoFormat(format);
            return VIDEO_TRACK;
        } else {
            flv.setAudioTrack(format);
            publisher.setAudioFormat(format);
            return AUDIO_TRACK;
        }
    }

    public void disconnect() {
        try {
            publisher.close();
        } catch (IllegalStateException e) {
            // Ignore illegal state.
        }
        mFlvTagCache.clear();
        flv.reset();

        connected = false;
        mVideoSequenceHeader = null;
        mAudioSequenceHeader = null;
        Log.i(TAG, "worker: disconnect ok.");
    }

    public boolean connect(String url) {
        needToFindKeyFrame = true;

        if (!connected) {
            Log.i(TAG, String.format("worker: connecting to RTMP server by url=%s\n", url));
            if (publisher.connect(url)) {
                connected = publisher.publish("live");
            }
            mVideoSequenceHeader = null;
            mAudioSequenceHeader = null;
        }
        return connected;
    }

    private void sendFlvTag(SrsFlvFrame frame) {
        if (!connected || frame == null) {
            return;
        }

        if (frame.isVideo()) {
            if (frame.isKeyFrame()) {
                Log.d(TAG, String.format("worker: send frame type=%d, dts=%d, size=%dB",
                        frame.type, frame.dts, frame.flvTag.array().length));
            }
            publisher.publishVideoData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
            mVideoAllocator.release(frame.flvTag);
        } else if (frame.isAudio()) {
            publisher.publishAudioData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
            mAudioAllocator.release(frame.flvTag);
        }
    }

    /**
     * start to the remote server for remux.
     */
    public void start(final String rtmpUrl) {
        worker = new Thread(() -> {
            Log.i(TAG, "SrsFlvMuxer started");

            if (!connect(rtmpUrl)) {
                Log.e(TAG, "SrsFlvMuxer disconnected");
                return;
            }
            Log.i(TAG, "SrsFlvMuxer connected");

            while (worker != null) {
                while (!mFlvTagCache.isEmpty()) {
                    SrsFlvFrame frame = mFlvTagCache.poll();
                    if (frame.isSequenceHeader()) {
                        if (frame.isVideo()) {
                            mVideoSequenceHeader = frame;<commit_msg>5beaaf213f88741503cc959a Audio/video alignment -Added a null check, but since we're checking !mFlvTagCache.isEmpty frame should never be null<commit_after>package net.ossrs.yasea;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import com.github.faucamp.simplertmp.RtmpHandler;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by winlin on 5/2/15.
 * Updated by leoma on 4/1/16.
 * to POST the h.264/avc annexb frame over RTMP.
 *
 * @see android.media.MediaMuxer https://developer.android.com/reference/android/media/MediaMuxer.html
 */
public class SrsFlvMuxer {

    private static final int VIDEO_ALLOC_SIZE = 128 * 1024;
    private static final int AUDIO_ALLOC_SIZE = 4 * 1024;

    private volatile boolean connected = false;
    private SrsRtmpPublisher publisher;
    private RtmpHandler mHandler;

    private Thread worker;
    private final Object txFrameLock = new Object();

    public SrsFlv flv = new SrsFlv();
    public boolean needToFindKeyFrame = true;
    private SrsFlvFrame mVideoSequenceHeader;
    private SrsFlvFrame mAudioSequenceHeader;
    private final SrsAllocator mVideoAllocator = new SrsAllocator(VIDEO_ALLOC_SIZE);
    private final SrsAllocator mAudioAllocator = new SrsAllocator(AUDIO_ALLOC_SIZE);
    private final ConcurrentLinkedQueue<SrsFlvFrame> mFlvTagCache = new ConcurrentLinkedQueue<>();

    public static final int VIDEO_TRACK = 100;
    public static final int AUDIO_TRACK = 101;
    private static final String TAG = "SrsFlvMuxer";

    /**
     * constructor.
     *
     * @param handler the rtmp event handler.
     */
    public SrsFlvMuxer(RtmpHandler handler) {
        mHandler = handler;
        publisher = new SrsRtmpPublisher(handler);
    }

    /**
     * get cached video frame number in publisher
     */
    public AtomicInteger getVideoFrameCacheNumber() {
        return publisher.getVideoFrameCacheNumber();
    }

    /**
     * set video resolution for publisher
     *
     * @param width  width
     * @param height height
     */
    public void setVideoResolution(int width, int height) {
        publisher.setVideoResolution(width, height);
    }

    /**
     * Adds a track with the specified format.
     *
     * @param format The media format for the track.
     * @return The track index for this newly added track.
     */
    public int addTrack(MediaFormat format) {
        if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
            flv.setVideoTrack(format);
            publisher.setVideoFormat(format);
            return VIDEO_TRACK;
        } else {
            flv.setAudioTrack(format);
            publisher.setAudioFormat(format);
            return AUDIO_TRACK;
        }
    }

    public void disconnect() {
        try {
            publisher.close();
        } catch (IllegalStateException e) {
            // Ignore illegal state.
        }
        mFlvTagCache.clear();
        flv.reset();

        connected = false;
        mVideoSequenceHeader = null;
        mAudioSequenceHeader = null;
        Log.i(TAG, "worker: disconnect ok.");
    }

    public boolean connect(String url) {
        needToFindKeyFrame = true;

        if (!connected) {
            Log.i(TAG, String.format("worker: connecting to RTMP server by url=%s\n", url));
            if (publisher.connect(url)) {
                connected = publisher.publish("live");
            }
            mVideoSequenceHeader = null;
            mAudioSequenceHeader = null;
        }
        return connected;
    }

    private void sendFlvTag(SrsFlvFrame frame) {
        if (!connected || frame == null) {
            return;
        }

        if (frame.isVideo()) {
            if (frame.isKeyFrame()) {
                Log.d(TAG, String.format("worker: send frame type=%d, dts=%d, size=%dB",
                        frame.type, frame.dts, frame.flvTag.array().length));
            }
            publisher.publishVideoData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
            mVideoAllocator.release(frame.flvTag);
        } else if (frame.isAudio()) {
            publisher.publishAudioData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
            mAudioAllocator.release(frame.flvTag);
        }
    }

    /**
     * start to the remote server for remux.
     */
    public void start(final String rtmpUrl) {
        worker = new Thread(() -> {
            Log.i(TAG, "SrsFlvMuxer started");

            if (!connect(rtmpUrl)) {
                Log.e(TAG, "SrsFlvMuxer disconnected");
                return;
            }
            Log.i(TAG, "SrsFlvMuxer connected");

            while (worker != null) {
                while (!mFlvTagCache.isEmpty()) {
                    SrsFlvFrame frame = mFlvTagCache.poll();
                    if (frame == null) break;
                    if (frame.isSequenceHeader()) {
                        if (frame.isVideo()) {
                            mVideoSequenceHeader = frame;
{'fraction_non_alphanumeric': 0.09781529294935452, 'fraction_numerical': 0.007249255213505462, 'mean_word_length': 2.2476620445017734, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1559194', 'n_tokens_mistral': 2985, 'n_tokens_neox': 2861, 'n_words': 693}