前置步骤和录屏是一样的,见我的上一篇文章

https://www.cnblogs.com/billin/p/17219558.html

bool obs_output_actual_start(obs_output_t *output)在上文的这个函数中,如果是启用推流直播,函数指针会转到这里

//碧麟精简批注版
//rtmp推流开始
static bool rtmp_stream_start(void *data)
{
struct rtmp_stream *stream = data; os_atomic_set_bool(&stream->connecting, true); //开启推流线程
return pthread_create(&stream->connect_thread, NULL, connect_thread,
stream) == 0;
}

推流使用的是obs-outputs插件

上面函数就是新开一个线程,来执行

这个函数static void *connect_thread(void *data),线程传入参数是stream

先看一下rtmp_stream结构

//rtmp stream结构
struct rtmp_stream {
//obs_output
obs_output_t *output; //packet信息
struct circlebuf packets;
bool sent_headers; bool got_first_video;
int64_t start_dts_offset; volatile bool connecting;
//连接线程地址
pthread_t connect_thread;
//发送线程地址
pthread_t send_thread; os_sem_t *send_sem; //推流地址,key
//比如b站,推流地址是rtmp://live-push.bilivideo.com/live-bvc/
struct dstr path, key;
//用户名,密码
struct dstr username, password;
//编码器名字:
//我用的是FMLE/3.0 (compatible; FMSc/1.0)
struct dstr encoder_name;
struct dstr bind_ip; int64_t last_dts_usec; uint64_t total_bytes_sent;
int dropped_frames; pthread_mutex_t dbr_mutex;
struct circlebuf dbr_frames;
size_t dbr_data_size; long audio_bitrate;
long dbr_est_bitrate;
long dbr_orig_bitrate;
long dbr_prev_bitrate;
long dbr_cur_bitrate;
long dbr_inc_bitrate;
bool dbr_enabled; // RTMP结构对象
RTMP rtmp; pthread_t socket_thread;
uint8_t *write_buf;
size_t write_buf_len;
size_t write_buf_size;
pthread_mutex_t write_buf_mutex; };

rtmp_stream结构里保存了推流的所有关键信息,包括推流地址,key,流的编码器等参数

还保存了几个关键的指针,用于多线程中调度。有连接线程connect_thread,也有发送线程pthread_t send_thread;

//rtmp连接线程
static void *connect_thread(void *data)
{
struct rtmp_stream *stream = data;
int ret;
//设置线程名
os_set_thread_name("rtmp-stream: connect_thread"); //初始化
if (!silently_reconnecting(stream)) {
if (!init_connect(stream)) {
obs_output_signal_stop(stream->output,
OBS_OUTPUT_BAD_PATH);
os_atomic_set_bool(&stream->silent_reconnect, false);
return NULL;
}
} else {
struct encoder_packet packet;
peek_next_packet(stream, &packet);
stream->start_dts_offset = get_ms_time(&packet, packet.dts);
} //连接
ret = try_connect(stream); if (ret != OBS_OUTPUT_SUCCESS) {
obs_output_signal_stop(stream->output, ret);
info("Connection to %s failed: %d", stream->path.array, ret);
} if (!stopping(stream))
pthread_detach(stream->connect_thread); os_atomic_set_bool(&stream->silent_reconnect, false);
os_atomic_set_bool(&stream->connecting, false);
return NULL;
}

上面的方法是在单独的线程中执行的,主要是做了下面几件事:

1 设置线程名为“rtmp-stream :connect_thread”

2 初始化 init_connect

3 通过调用try_connect(stream)执行实际连接逻辑

//rtmp connect
static int try_connect(struct rtmp_stream *stream)
{
info("Connecting to RTMP URL %s...", stream->path.array); //rtmp初始化
RTMP_Init(&stream->rtmp); //设置URL
if (!RTMP_SetupURL(&stream->rtmp, stream->path.array))
return OBS_OUTPUT_BAD_PATH; RTMP_EnableWrite(&stream->rtmp); //设置流编码格式
dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; FMSc/1.0)"); //设置用户名密码
set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username);
set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password);
set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name);
stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl;
stream->rtmp.Link.customConnectEncode = add_connect_data; if (dstr_is_empty(&stream->bind_ip) ||
dstr_cmp(&stream->bind_ip, "default") == 0) {
memset(&stream->rtmp.m_bindIP, 0,
sizeof(stream->rtmp.m_bindIP));
} else {
bool success = netif_str_to_addr(&stream->rtmp.m_bindIP.addr,
&stream->rtmp.m_bindIP.addrLen,
stream->bind_ip.array);
if (success) {
int len = stream->rtmp.m_bindIP.addrLen;
bool ipv6 = len == sizeof(struct sockaddr_in6);
info("Binding to IPv%d", ipv6 ? 6 : 4);
}
} RTMP_AddStream(&stream->rtmp, stream->key.array); stream->rtmp.m_outChunkSize = 4096;
stream->rtmp.m_bSendChunkSizeInfo = true;
stream->rtmp.m_bUseNagle = true; //连接
if (!RTMP_Connect(&stream->rtmp, NULL)) {
set_output_error(stream);
return OBS_OUTPUT_CONNECT_FAILED;
} if (!RTMP_ConnectStream(&stream->rtmp, 0))
return OBS_OUTPUT_INVALID_STREAM; info("Connection to %s successful", stream->path.array); //到这里说明连接成功,开始初始化send逻辑,准备推流
return init_send(stream);
}

当连接成功,开始调用init_send函数,初始化send,准备推流

需要注意,send也是在单独的线

程处理的,因为传视频比较大,如果不单开线程,肯定会造成阻塞。

//初始化rtmp send逻辑
static int init_send(struct rtmp_stream *stream)
{
int ret;
obs_output_t *context = stream->output; //创建send线程,执行send_thread方法,参数是stream
ret = pthread_create(&stream->send_thread, NULL, send_thread, stream); if (stream->new_socket_loop) {
int one = 1;
#ifdef _WIN32
if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
stream->rtmp.last_error_code = WSAGetLastError();
#else
if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
stream->rtmp.last_error_code = errno;
#endif
warn("Failed to set non-blocking socket");
return OBS_OUTPUT_ERROR;
} os_event_reset(stream->send_thread_signaled_exit); info("New socket loop enabled by user");
if (stream->low_latency_mode)
info("Low latency mode enabled by user"); if (stream->write_buf)
bfree(stream->write_buf); int total_bitrate = 0; obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
if (vencoder) {
obs_data_t *params = obs_encoder_get_settings(vencoder);
if (params) {
int bitrate =
obs_data_get_int(params, "bitrate");
if (!bitrate) {
warn("Video encoder didn't return a "
"valid bitrate, new network "
"code may function poorly. "
"Low latency mode disabled.");
stream->low_latency_mode = false;
bitrate = 10000;
}
total_bitrate += bitrate;
obs_data_release(params);
}
} obs_encoder_t *aencoder =
obs_output_get_audio_encoder(context, 0);
if (aencoder) {
obs_data_t *params = obs_encoder_get_settings(aencoder);
if (params) {
int bitrate =
obs_data_get_int(params, "bitrate");
if (!bitrate)
bitrate = 160;
total_bitrate += bitrate;
obs_data_release(params);
}
} // to bytes/sec
int ideal_buffer_size = total_bitrate * 128; if (ideal_buffer_size < 131072)
ideal_buffer_size = 131072; stream->write_buf_size = ideal_buffer_size;
stream->write_buf = bmalloc(ideal_buffer_size); #ifdef _WIN32
ret = pthread_create(&stream->socket_thread, NULL,
socket_thread_windows, stream);
#else
warn("New socket loop not supported on this platform");
return OBS_OUTPUT_ERROR;
#endif if (ret != 0) {
RTMP_Close(&stream->rtmp);
warn("Failed to create socket thread");
return OBS_OUTPUT_ERROR;
} stream->socket_thread_active = true;
stream->rtmp.m_bCustomSend = true;
stream->rtmp.m_customSendFunc = socket_queue_data;
stream->rtmp.m_customSendParam = stream;
} os_atomic_set_bool(&stream->active, true); if (!send_meta_data(stream)) {
warn("Disconnected while attempting to send metadata");
set_output_error(stream);
return OBS_OUTPUT_DISCONNECTED;
} obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 1);
if (aencoder && !send_additional_meta_data(stream)) {
warn("Disconnected while attempting to send additional "
"metadata");
return OBS_OUTPUT_DISCONNECTED;
} if (obs_output_get_audio_encoder(context, 2) != NULL) {
warn("Additional audio streams not supported");
return OBS_OUTPUT_DISCONNECTED;
} if (!silently_reconnecting(stream))
obs_output_begin_data_capture(stream->output, 0); return OBS_OUTPUT_SUCCESS;
}

核心推流线程,在单独的线程里完成推流逻辑

//推流核心线程
static void *send_thread(void *data)
{
struct rtmp_stream *stream = data; //设置线程名
os_set_thread_name("rtmp-stream: send_thread"); //设定buffersize
#if defined(_WIN32)
// Despite MSDN claiming otherwise, send buffer auto tuning on
// Windows 7 doesn't seem to work very well.
if (get_win_ver_int() == 0x601) {
DWORD cur_sendbuf_size;
DWORD desired_sendbuf_size = 524288;
socklen_t int_size = sizeof(int); if (!getsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET,
SO_SNDBUF, (char *)&cur_sendbuf_size,
&int_size) &&
cur_sendbuf_size < desired_sendbuf_size) { setsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET,
SO_SNDBUF, (char *)&desired_sendbuf_size,
sizeof(desired_sendbuf_size));
}
} log_sndbuf_size(stream);
#endif //推流主循环
while (os_sem_wait(stream->send_sem) == 0) {
struct encoder_packet packet;
struct dbr_frame dbr_frame; if (stopping(stream) && stream->stop_ts == 0) {
break;
} if (!get_next_packet(stream, &packet))
continue; if (stopping(stream)) {
if (can_shutdown_stream(stream, &packet)) {
obs_encoder_packet_release(&packet);
break;
}
} if (!stream->sent_headers) {
if (!send_headers(stream)) {
os_atomic_set_bool(&stream->disconnected, true);
break;
}
} /* silent reconnect signal received from server, reconnect on
* next keyframe */
if (silently_reconnecting(stream) &&
packet.type == OBS_ENCODER_VIDEO && packet.keyframe) {
reinsert_packet_at_front(stream, &packet);
break;
} if (stream->dbr_enabled) {
dbr_frame.send_beg = os_gettime_ns();
dbr_frame.size = packet.size;
} if (send_packet(stream, &packet, false, packet.track_idx) < 0) {
os_atomic_set_bool(&stream->disconnected, true);
break;
} if (stream->dbr_enabled) {
dbr_frame.send_end = os_gettime_ns(); pthread_mutex_lock(&stream->dbr_mutex);
dbr_add_frame(stream, &dbr_frame);
pthread_mutex_unlock(&stream->dbr_mutex);
}
} bool encode_error = os_atomic_load_bool(&stream->encode_error); if (disconnected(stream)) {
info("Disconnected from %s", stream->path.array);
} else if (encode_error) {
info("Encoder error, disconnecting");
} else if (silently_reconnecting(stream)) {
info("Silent reconnect signal received from server");
} else {
info("User stopped the stream");
} #if defined(_WIN32)
log_sndbuf_size(stream);
#endif if (stream->new_socket_loop) {
os_event_signal(stream->send_thread_signaled_exit);
os_event_signal(stream->buffer_has_data_event);
pthread_join(stream->socket_thread, NULL);
stream->socket_thread_active = false;
stream->rtmp.m_bCustomSend = false;
} set_output_error(stream); if (silently_reconnecting(stream)) {
/* manually close the socket to prevent librtmp from sending
* unpublish / deletestream messages when we call RTMP_Close,
* since we want to re-use this stream when we reconnect */
RTMPSockBuf_Close(&stream->rtmp.m_sb);
stream->rtmp.m_sb.sb_socket = -1;
} RTMP_Close(&stream->rtmp); /* reset bitrate on stop */
if (stream->dbr_enabled) {
if (stream->dbr_cur_bitrate != stream->dbr_orig_bitrate) {
stream->dbr_cur_bitrate = stream->dbr_orig_bitrate;
dbr_set_bitrate(stream);
}
} if (!stopping(stream)) {
pthread_detach(stream->send_thread);
if (!silently_reconnecting(stream))
obs_output_signal_stop(stream->output,
OBS_OUTPUT_DISCONNECTED);
} else if (encode_error) {
obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR);
} else {
obs_output_end_data_capture(stream->output);
} if (!silently_reconnecting(stream)) {
free_packets(stream);
os_event_reset(stream->stop_event);
os_atomic_set_bool(&stream->active, false);
} stream->sent_headers = false; /* reset bitrate on stop */
if (stream->dbr_enabled) {
if (stream->dbr_cur_bitrate != stream->dbr_orig_bitrate) {
stream->dbr_cur_bitrate = stream->dbr_orig_bitrate;
dbr_set_bitrate(stream);
}
} if (silently_reconnecting(stream)) {
rtmp_stream_start(stream);
} return NULL;
}

obs推流核心流程分析的更多相关文章

  1. Mybatis源码学习第六天(核心流程分析)之Executor分析(补充)

    补充上一章没有讲解的三个Executor执行器; 还是贴一下之前的代码吧;我发现其实有些分析注释还是写在代码里面比较好,方便大家理解,之前是我的疏忽,不好意思 @Override public < ...

  2. Mybatis源码学习第六天(核心流程分析)之Executor分析

    今Executor这个类,Mybatis虽然表面是SqlSession做的增删改查,其实底层统一调用的是Executor这个接口 在这里贴一下Mybatis查询体系结构图 Executor组件分析 E ...

  3. 【转】Hostapd工作流程分析

    [转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...

  4. Android7.0 Phone应用源码分析(二) phone来电流程分析

    接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...

  5. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  6. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  7. HDFS2.x之RPC流程分析

    HDFS2.x之RPC流程分析 1 概述 Hadoop提供了一个统一的RPC机制来处理client-namenode, namenode-dataname,client-dataname之间的通信.R ...

  8. 【转】linux文件系统之mount流程分析

    本质上,Ext3 mount的过程实际上是inode被替代的过程. 例如,/dev/sdb块设备被mount到/mnt/alan目录.命令:mount -t ext3 /dev/sdb /mnt/al ...

  9. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  10. SpringMVC的流程分析(一)—— 整体流程概括

    SpringMVC的整体概括 之前也写过springmvc的流程分析,只是当时理解的还不透彻所以那篇文章就放弃了,现在比之前好了些,想着写下来分享下,也能增强记忆,也希望可以帮助到人,如果文章中有什么 ...

随机推荐

  1. 请求接口类型blob,下载文件

    axiosGet () { var This = this var url = '/group1/M00/00/21/MejEvGOX_zOAL2kiAAAAUhB5Iqg138.txt?token= ...

  2. ant design vue的tooltip的宽度修改不生效

    // 注意,vue 项目,style 标签不能加 scoped 选项,否则样式不生效 <a-tooltip placement="top" :overlayClassName ...

  3. 基于rabbitmq之MQTT协议的智能家居

    智能家居项目 智能可燃气体报警器 产品是一款可燃气体报警器,如果家中燃气泄露浓度到达一定阈值,报警器检测到并上传气体浓度值给后台,后台以电话.短信.微信等方式,提醒用户家中可能有气体泄漏. 用户还可能 ...

  4. 【七侠传】冲刺阶段--Day6

    [七侠传]冲刺阶段--Day6 团队成员 20181221曾宇涛 20181202李祎铭 20181209沙桐 20181215薛胜瀚 20181216杨越麒 20181223何家豪 20181232 ...

  5. Luogu P3368 【模板】树状数组 2 [区间修改-单点查询]

    P3368 [模板]树状数组 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表 ...

  6. ethcat开发记录 一

    一.方案 1.移植开源方案SOEM 2.专用芯片 二.SOEM移植 (一)硬件 stm32f407,168M PHY:LAN8720A (ii) Points to note 1, the PHY a ...

  7. ES6的Map和Set的了解和练习

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 分布式-Etcd介绍

    Etcd客户端基本操作 提供了如下操作接口: put - 添加一个新的 key-value 到存储中 get - 获取一个 key 的 value delete - 删除节点 range - 获取一个 ...

  9. daimayuan第二课(1.二叉树的遍历,2.二叉树的最近公共祖先,3.二叉搜索树)

    二叉树的最近公共祖先: 1:概念:就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 2:因为是基础课,所以tarjan和倍增lca就留到中级课再 ...

  10. suse 关闭 图形界面

    图形界面 /etc/systemd/system/default.target -> /usr/lib/systemd/system/graphical.target ln -s /usr/li ...