package com.live.muxer;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import com.live.simplertmp.DefaultRtmpPublisher;
import com.live.simplertmp.RtmpHandler;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 主要对视音频原始数据h264和aac数据进行flv tag格式的封装,然后将封装好的数据传递给rtmp模块进行推理发送
* 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 started = false;
private DefaultRtmpPublisher publisher;
private Thread worker;
private final Object txFrameLock = new Object();
private SrsFlv flv = new SrsFlv(); //flv格式封装相关
private boolean needToFindKeyFrame = true;
private SrsFlvFrame mVideoSequenceHeader; //视频头帧
private SrsFlvFrame mAudioSequenceHeader; //音频头帧
private SrsAllocator mVideoAllocator = new SrsAllocator(VIDEO_ALLOC_SIZE);
private SrsAllocator mAudioAllocator = new SrsAllocator(AUDIO_ALLOC_SIZE);
//ConcurrentLinkedQueue是一个基于链表的无界非阻塞队列,并且是线程安全的,它采用的是先进先出的规则,当我们增加一个元素时,
// 它会添加到队列的末尾,当我们取一个元素时,它会返回一个队列头部的元素,用来实现高并发的一个队列
private ConcurrentLinkedQueue<SrsFlvFrame> mFlvTagCache = new ConcurrentLinkedQueue<>();
private static final int VIDEO_TRACK = 100;
private static final int AUDIO_TRACK = 101;
private static final String TAG = "SrsFlvMuxer";
/**
* constructor.
* @param handler the rtmp event handler.
*/
public SrsFlvMuxer(RtmpHandler handler) {
publisher = new DefaultRtmpPublisher(handler);
}
/**
* get cached video frame number in publisher
*/
public AtomicInteger getVideoFrameCacheNumber() {
return publisher == null ? null : publisher.getVideoFrameCacheNumber();
}
/**
* set video resolution for publisher
* @param width width
* @param height height
*/
public void setVideoResolution(int width, int height) {
if (publisher != null) {
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("video/avc")) {
flv.setVideoTrack(format);
return VIDEO_TRACK;
} else {
flv.setAudioTrack(format);
return AUDIO_TRACK;
}
}
//rtmp断开连接
private void disconnect() {
try {
publisher.close();
} catch (IllegalStateException e) {
// Ignore illegal state.
}
mVideoSequenceHeader = null;
mAudioSequenceHeader = null;
Log.i(TAG, "worker: disconnect ok.");
}
private boolean connect(String url) {
boolean connected = false;
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;
}
//通过publisher推送flv Tag数据
private void sendFlvTag(SrsFlvFrame frame) {
if (frame == null) {
return;
}
if (frame.isVideo()) {
if (frame.isKeyFrame()) {
Log.i(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) {
started = true;
worker = new Thread(new Runnable() {
@Override
public void run() {
//连接url
if (!connect(rtmpUrl)) {
return;
}
while (!Thread.interrupted()) {
//循环从缓冲中取数据,因为是非阻塞队列,所以要先判断是否为空
while (!mFlvTagCache.isEmpty()) {
//如果队列为空,则返回null
SrsFlvFrame frame = mFlvTagCache.poll();
if (frame.isSequenceHeader()) {
if (frame.isVideo()) {
mVideoSequenceHeader = frame;
sendFlvTag(mVideoSequenceHeader);
} else if (frame.isAudio()) {
mAudioSequenceHeader = frame;
sendFlvTag(mAudioSequenceHeader);
}
} else {
if (frame.isVideo() && mVideoSequenceHeader != null) {
sendFlvTag(frame);
} else if (frame.isAudio() && mAudioSequenceHeader != null) {
sendFlvTag(frame);
}
}
}
// Waiting for next frame
synchronized (txFrameLock) {
try {
// isEmpty() may take some time, so we set timeout to detect next frame
txFrameLock.wait(500);
} catch (InterruptedException ie) {
worker.interrupt();
}
}
}
}
});
worker.start();
}
/**
* stop the muxer, disconnect RTMP connection.
*/
public void stop() {
started = false;
mFlvTagCache.clear();
if (worker != null) {
worker.interrupt();
try {
worker.join();
} catch (InterruptedException e) {
e.printStackTrace();
worker.interrupt();
}
worker = null;
}
flv.reset();
needToFindKeyFrame = true;
Log.i(TAG, "SrsFlvMuxer closed");
// We should not block the main thread
new Thread(new Runnable() {
@Override
public void run() {
disconnect();
}
}).start();
}
/**
* send the annexb frame over RTMP.
* @param trackIndex The track index for this sample.
* @param byteBuf The encoded sample.
* @param bufferInfo The buffer information related to this sample.
*/
public void writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
if (bufferInfo.offset > 0) {
Log.w(TAG, String.format("encoded frame %dB, offset=%d pts=%dms",
bufferInfo.size, bufferInfo.offset, bufferInfo.presentationTimeUs / 1000
));
}
if (VIDEO_TRACK == trackIndex) {

Scikit-learn
- 粉丝: 5433
- 资源: 5032
最新资源
- java图书管理系统(源码+数据库+开题报告+两份毕业论文).zip
- java学生教务系统管理系统(源码+数据库+开题报告+两份毕业论文).zip
- 电机模型中的电压方程、PI控制器与PLL锁相环的标幺化处理详解及采样时间研究,电机模型中的电压方程、PI控制器与PLL锁相环的标幺化处理详解及采样时间研究,电压方程标幺化、PI标幺化、锁相环PLL标幺
- 语音编辑软件,用于处理语音用于大模型训练
- 基于SSM的高校图书管理系统-源码+数据库
- COMSOL模拟燃料电池冷启动过程:温度、电流、物质浓度等分布研究,COMSOL燃料电池冷启动仿真模型:探索冰形成、温度电流分布及物质浓度等特性,三种启动方式全面解析,COMSOL 燃料电池,冷启动仿
- 基于OpenSees平台建立的单柱墩模型:考虑滑移粘接捏缩效应,包含建模全过程、钢筋混凝土粘接滑移及位移控制滞回分析代码实践,基于OpenSees平台建立的单柱墩模型滑移粘接分析及其建模全过程与滞回分
- “国家级大数据综合试验区”试点城市DID(2000-2022年).zip
- deepseekr1 技术报告,中文版
- 群智能算法优化BP神经网络:结合思维进化算法与两层BP,高效全局搜索与局部优化,预测回归数据新策略,基于思维进化算法优化BP神经网络:全局搜索与局部拟合的高效组合,两层BP预测回归新探 ,群智能算法优
- 基于STM32F4xx的永磁同步电机(PMSM)控制器电路设计及其Simulink模型代码自动生成研究,基于STM32F4xx的永磁同步电机(PMSM)控制器电路设计及其Simulink模型代码自动生
- 最全面的MTK手机开发平台MTK资料大全(最新版)
- 机器视觉技术:OpenCV与Qt驱动的工业相机图像采集与处理实践-卡尺工具辅助下的找线、找圆、颜色检测及模板与形状匹配算法封装与调用,机器视觉技术:OpenCV与Qt驱动的工业相机图像采集与处理全解
- PSI5标准协议:V2.1
- 基于MATLAB的智能优化:改进带记忆模拟退火算法在TSP问题中的应用及性能测试,基于MATLAB的带记忆模拟退火算法:TSP问题求解及多普勒降温曲线应用研究,基于matlab的改进的带记忆的模拟 火
- 8.python-OpenCV2024-10-05.wmv
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈


