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) {
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip基于H264、AAC和RTMP的直播推流项目C源码.zip 【资源说明】 适用人群:该项目属于高分优质项目,上传之前都本地运行验证过。适合小白、高校学生、教师、科研人员、公司员工下载学习借鉴使用。 用途:学习借鉴,也可在此基础上二次开发,当然也可以直接用于课设、作业、毕设、实际项目等。 技术支持:关于项目的技术细节或更详细的介绍,可以私信与我沟通,或看项目内的项目说明(若有)、代码等,很乐意交流学习。 【特别强调】 若自己基础实在太差,自己不懂运行,可以与我私聊,可远程教学指导。当然也可以做项目二次开发和定制。
资源推荐
资源详情
资源评论





























收起资源包目录





































































































共 2000 条
- 1
- 2
- 3
- 4
- 5
- 6
- 20
资源评论


Scikit-learn
- 粉丝: 6692
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助


最新资源
- 继电器在电气工程及自动化低压电器中的应用.docx
- 典型网络工程的案例分析.doc
- 全国计算机等考试二C笔试试卷.doc
- 大学计算机实验报告记录样本.doc
- 科大讯飞人工智能定义城市1.0版本发布.docx
- 软件学院软件工程硕士版培养方案终稿单证.doc
- 基于单片机的数字万用表研究设计.doc
- 集团公司大数据平台建设方案.docx
- 南京大学关于机器学习的 PPT 教学课件
- 热电厂建设项目管理控制研究.docx
- 项目管理的难点与对策.doc
- Oracle程序设计.docx
- 不依赖 sk-learn 库的纯 Python 机器学习算法实现
- 基于单片机的抢答器的方案设计书.doc
- 试论大数据环境下的企业财务管理改革路径.docx
- 初中英语教师基于网络平台的自主发展.docx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈



安全验证
文档复制为VIP权益,开通VIP直接复制
