2017-10-09 22:11:01 +08:00
|
|
|
|
/*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
2017-09-27 16:20:30 +08:00
|
|
|
|
*
|
2021-01-17 18:31:50 +08:00
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
|
2017-09-27 16:20:30 +08:00
|
|
|
|
*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Use of this source code is governed by MIT license that can be found in the
|
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
2017-04-01 16:35:56 +08:00
|
|
|
|
*/
|
2020-04-04 20:30:09 +08:00
|
|
|
|
|
2022-07-29 14:19:16 +08:00
|
|
|
|
#include <algorithm>
|
2017-04-01 16:35:56 +08:00
|
|
|
|
#include "RtmpProtocol.h"
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include "Rtmp/utils.h"
|
2021-02-04 17:58:51 +08:00
|
|
|
|
#include "RtmpMediaSource.h"
|
2022-11-29 11:07:13 +08:00
|
|
|
|
#include "Util/util.h"
|
2022-07-29 14:19:16 +08:00
|
|
|
|
|
2022-02-02 20:34:50 +08:00
|
|
|
|
using namespace std;
|
2022-07-29 14:19:16 +08:00
|
|
|
|
using namespace toolkit;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
#define C1_DIGEST_SIZE 32
|
|
|
|
|
#define C1_KEY_SIZE 128
|
|
|
|
|
#define C1_SCHEMA_SIZE 764
|
|
|
|
|
#define C1_HANDSHARK_SIZE (RANDOM_LEN + 8)
|
|
|
|
|
#define C1_FPKEY_SIZE 30
|
|
|
|
|
#define S1_FMS_KEY_SIZE 36
|
|
|
|
|
#define S2_FMS_KEY_SIZE 68
|
|
|
|
|
#define C1_OFFSET_SIZE 4
|
|
|
|
|
|
2020-08-30 11:40:03 +08:00
|
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
|
#include "Util/SSLBox.h"
|
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
|
#include <openssl/opensslv.h>
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
static string openssl_HMACsha256(const void *key, size_t key_len, const void *data, size_t data_len){
|
2020-08-30 10:48:34 +08:00
|
|
|
|
std::shared_ptr<char> out(new char[32], [](char *ptr) { delete[] ptr; });
|
2020-03-20 11:51:24 +08:00
|
|
|
|
unsigned int out_len;
|
2017-08-09 18:39:30 +08:00
|
|
|
|
|
2018-03-02 13:40:17 +08:00
|
|
|
|
#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
|
|
|
|
|
//openssl 1.1.0新增api,老版本api作废
|
2020-03-20 11:51:24 +08:00
|
|
|
|
HMAC_CTX *ctx = HMAC_CTX_new();
|
|
|
|
|
HMAC_CTX_reset(ctx);
|
2021-01-17 18:31:50 +08:00
|
|
|
|
HMAC_Init_ex(ctx, key, (int)key_len, EVP_sha256(), NULL);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
HMAC_Update(ctx, (unsigned char*)data, data_len);
|
|
|
|
|
HMAC_Final(ctx, (unsigned char *)out.get(), &out_len);
|
|
|
|
|
HMAC_CTX_reset(ctx);
|
|
|
|
|
HMAC_CTX_free(ctx);
|
2017-08-09 18:39:30 +08:00
|
|
|
|
#else
|
2020-03-20 11:51:24 +08:00
|
|
|
|
HMAC_CTX ctx;
|
|
|
|
|
HMAC_CTX_init(&ctx);
|
|
|
|
|
HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
|
|
|
|
|
HMAC_Update(&ctx, (unsigned char*)data, data_len);
|
|
|
|
|
HMAC_Final(&ctx, (unsigned char *)out.get(), &out_len);
|
|
|
|
|
HMAC_CTX_cleanup(&ctx);
|
2018-03-02 13:40:57 +08:00
|
|
|
|
#endif //defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
|
2020-03-20 11:51:24 +08:00
|
|
|
|
return string(out.get(),out_len);
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
|
|
|
|
#endif //ENABLE_OPENSSL
|
|
|
|
|
|
2018-10-24 17:17:55 +08:00
|
|
|
|
namespace mediakit {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
|
|
|
|
RtmpProtocol::RtmpProtocol() {
|
2021-11-21 11:34:20 +08:00
|
|
|
|
_packet_pool.setSize(64);
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_C0C1(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
RtmpProtocol::~RtmpProtocol() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
reset();
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-08-03 13:55:46 +08:00
|
|
|
|
void RtmpProtocol::reset() {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
////////////ChunkSize////////////
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_chunk_size_in = DEFAULT_CHUNK_LEN;
|
|
|
|
|
_chunk_size_out = DEFAULT_CHUNK_LEN;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
////////////Acknowledgement////////////
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_bytes_sent = 0;
|
|
|
|
|
_bytes_sent_last = 0;
|
|
|
|
|
_windows_size = 0;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
///////////PeerBandwidth///////////
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_bandwidth = 2500000;
|
|
|
|
|
_band_limit_type = 2;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
////////////Chunk////////////
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_map_chunk_data.clear();
|
|
|
|
|
_now_stream_index = 0;
|
|
|
|
|
_now_chunk_id = 0;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//////////Invoke Request//////////
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_send_req_id = 0;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//////////Rtmp parser//////////
|
2020-11-01 03:41:35 +08:00
|
|
|
|
HttpRequestSplitter::reset();
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_stream_index = STREAM_CONTROL;
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_C0C1(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendAcknowledgement(uint32_t size) {
|
|
|
|
|
size = htonl(size);
|
|
|
|
|
std::string acknowledgement((char *) &size, 4);
|
|
|
|
|
sendRequest(MSG_ACK, acknowledgement);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendAcknowledgementSize(uint32_t size) {
|
|
|
|
|
size = htonl(size);
|
|
|
|
|
std::string set_windowSize((char *) &size, 4);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendRequest(MSG_WIN_SIZE, set_windowSize);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendPeerBandwidth(uint32_t size) {
|
|
|
|
|
size = htonl(size);
|
|
|
|
|
std::string set_peerBandwidth((char *) &size, 4);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
set_peerBandwidth.push_back((char) 0x02);
|
|
|
|
|
sendRequest(MSG_SET_PEER_BW, set_peerBandwidth);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendChunkSize(uint32_t size) {
|
|
|
|
|
uint32_t len = htonl(size);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
std::string set_chunk((char *) &len, 4);
|
|
|
|
|
sendRequest(MSG_SET_CHUNK, set_chunk);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_chunk_size_out = size;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendPingRequest(uint32_t stamp) {
|
|
|
|
|
sendUserControl(CONTROL_PING_REQUEST, stamp);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendPingResponse(uint32_t time_stamp) {
|
|
|
|
|
sendUserControl(CONTROL_PING_RESPONSE, time_stamp);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendSetBufferLength(uint32_t stream_index, uint32_t len) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
std::string control;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
stream_index = htonl(stream_index);
|
|
|
|
|
control.append((char *) &stream_index, 4);
|
|
|
|
|
|
|
|
|
|
len = htonl(len);
|
|
|
|
|
control.append((char *) &len, 4);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendUserControl(CONTROL_SETBUFFER, control);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendUserControl(uint16_t event_type, uint32_t event_data) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
std::string control;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
event_type = htons(event_type);
|
|
|
|
|
control.append((char *) &event_type, 2);
|
|
|
|
|
|
|
|
|
|
event_data = htonl(event_data);
|
|
|
|
|
control.append((char *) &event_data, 4);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendRequest(MSG_USER_CONTROL, control);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendUserControl(uint16_t event_type, const string &event_data) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
std::string control;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
event_type = htons(event_type);
|
|
|
|
|
control.append((char *) &event_type, 2);
|
|
|
|
|
control.append(event_data);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendRequest(MSG_USER_CONTROL, control);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendResponse(int type, const string &str) {
|
|
|
|
|
if(!_data_started && (type == MSG_DATA)){
|
|
|
|
|
_data_started = true;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
sendRtmp(type, _now_stream_index, str, 0, _data_started ? CHUNK_CLIENT_REQUEST_AFTER : CHUNK_CLIENT_REQUEST_BEFORE);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendInvoke(const string &cmd, const AMFValue &val) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
AMFEncoder enc;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
enc << cmd << ++_send_req_id << val;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendRequest(MSG_CMD, enc.data());
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendRequest(int cmd, const string& str) {
|
2021-09-29 16:57:19 +08:00
|
|
|
|
if (cmd <= MSG_SET_PEER_BW) {
|
|
|
|
|
// 若 cmd 属于 Protocol Control Messages ,则应使用 chunk id 2 发送
|
|
|
|
|
sendRtmp(cmd, _stream_index, str, 0, CHUNK_NETWORK);
|
|
|
|
|
} else {
|
|
|
|
|
// 否则使用 chunk id 发送(任意值3-128,参见 obs 及 ffmpeg 选取 3)
|
|
|
|
|
sendRtmp(cmd, _stream_index, str, 0, CHUNK_SYSTEM);
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 14:37:03 +08:00
|
|
|
|
class BufferPartial : public Buffer {
|
|
|
|
|
public:
|
2021-01-17 18:31:50 +08:00
|
|
|
|
BufferPartial(const Buffer::Ptr &buffer, size_t offset, size_t size){
|
2019-03-22 14:37:03 +08:00
|
|
|
|
_buffer = buffer;
|
|
|
|
|
_data = buffer->data() + offset;
|
|
|
|
|
_size = size;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
~BufferPartial() override{}
|
2019-03-22 14:37:03 +08:00
|
|
|
|
|
|
|
|
|
char *data() const override {
|
|
|
|
|
return _data;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t size() const override{
|
2019-03-22 14:37:03 +08:00
|
|
|
|
return _size;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2019-03-22 14:37:03 +08:00
|
|
|
|
private:
|
|
|
|
|
char *_data;
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t _size;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
Buffer::Ptr _buffer;
|
2019-03-22 14:37:03 +08:00
|
|
|
|
};
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id) {
|
|
|
|
|
sendRtmp(type, stream_index, std::make_shared<BufferString>(buffer), stamp, chunk_id);
|
2019-03-22 14:37:03 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
void RtmpProtocol::sendRtmp(uint8_t type, uint32_t stream_index, const Buffer::Ptr &buf, uint32_t stamp, int chunk_id){
|
|
|
|
|
if (chunk_id < 2 || chunk_id > 63) {
|
|
|
|
|
auto strErr = StrPrinter << "不支持发送该类型的块流 ID:" << chunk_id << endl;
|
2019-03-22 14:37:03 +08:00
|
|
|
|
throw std::runtime_error(strErr);
|
|
|
|
|
}
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//是否有扩展时间戳
|
2020-08-30 10:48:34 +08:00
|
|
|
|
bool ext_stamp = stamp >= 0xFFFFFF;
|
2018-01-30 11:23:57 +08:00
|
|
|
|
|
2019-03-23 22:00:16 +08:00
|
|
|
|
//rtmp头
|
2020-08-30 10:48:34 +08:00
|
|
|
|
BufferRaw::Ptr buffer_header = obtainBuffer();
|
|
|
|
|
buffer_header->setCapacity(sizeof(RtmpHeader));
|
|
|
|
|
buffer_header->setSize(sizeof(RtmpHeader));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//对rtmp头赋值,如果使用整形赋值,在arm android上可能由于数据对齐导致总线错误的问题
|
2020-08-30 10:48:34 +08:00
|
|
|
|
RtmpHeader *header = (RtmpHeader *) buffer_header->data();
|
2021-06-22 10:39:16 +08:00
|
|
|
|
header->fmt = 0;
|
|
|
|
|
header->chunk_id = chunk_id;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
header->type_id = type;
|
|
|
|
|
set_be24(header->time_stamp, ext_stamp ? 0xFFFFFF : stamp);
|
2021-01-17 18:31:50 +08:00
|
|
|
|
set_be24(header->body_size, (uint32_t)buf->size());
|
2020-08-30 10:48:34 +08:00
|
|
|
|
set_le32(header->stream_index, stream_index);
|
2019-03-23 22:00:16 +08:00
|
|
|
|
//发送rtmp头
|
2020-11-01 03:41:35 +08:00
|
|
|
|
onSendRawData(std::move(buffer_header));
|
2019-03-22 14:37:03 +08:00
|
|
|
|
|
2019-03-23 22:00:16 +08:00
|
|
|
|
//扩展时间戳字段
|
2020-08-30 10:48:34 +08:00
|
|
|
|
BufferRaw::Ptr buffer_ext_stamp;
|
|
|
|
|
if (ext_stamp) {
|
2019-03-23 22:00:16 +08:00
|
|
|
|
//生成扩展时间戳
|
2020-08-30 10:48:34 +08:00
|
|
|
|
buffer_ext_stamp = obtainBuffer();
|
|
|
|
|
buffer_ext_stamp->setCapacity(4);
|
|
|
|
|
buffer_ext_stamp->setSize(4);
|
|
|
|
|
set_be32(buffer_ext_stamp->data(), stamp);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//生成一个字节的flag,标明是什么chunkId
|
2020-08-30 10:48:34 +08:00
|
|
|
|
BufferRaw::Ptr buffer_flags = obtainBuffer();
|
|
|
|
|
buffer_flags->setCapacity(1);
|
|
|
|
|
buffer_flags->setSize(1);
|
2021-06-22 10:39:16 +08:00
|
|
|
|
header = (RtmpHeader *) buffer_flags->data();
|
|
|
|
|
header->fmt = 3;
|
|
|
|
|
header->chunk_id = chunk_id;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2019-03-23 22:00:16 +08:00
|
|
|
|
size_t offset = 0;
|
2021-01-17 18:31:50 +08:00
|
|
|
|
size_t totalSize = sizeof(RtmpHeader);
|
2019-03-23 22:00:16 +08:00
|
|
|
|
while (offset < buf->size()) {
|
|
|
|
|
if (offset) {
|
2020-08-30 10:48:34 +08:00
|
|
|
|
onSendRawData(buffer_flags);
|
2019-03-22 14:37:03 +08:00
|
|
|
|
totalSize += 1;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (ext_stamp) {
|
2019-03-22 14:37:03 +08:00
|
|
|
|
//扩展时间戳
|
2020-08-30 10:48:34 +08:00
|
|
|
|
onSendRawData(buffer_ext_stamp);
|
2019-03-22 14:37:03 +08:00
|
|
|
|
totalSize += 4;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
size_t chunk = min(_chunk_size_out, buf->size() - offset);
|
|
|
|
|
onSendRawData(std::make_shared<BufferPartial>(buf, offset, chunk));
|
2019-03-22 14:37:03 +08:00
|
|
|
|
totalSize += chunk;
|
2019-03-23 22:00:16 +08:00
|
|
|
|
offset += chunk;
|
2019-03-22 14:37:03 +08:00
|
|
|
|
}
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_bytes_sent += (uint32_t)totalSize;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (_windows_size > 0 && _bytes_sent - _bytes_sent_last >= _windows_size) {
|
|
|
|
|
_bytes_sent_last = _bytes_sent;
|
|
|
|
|
sendAcknowledgement(_bytes_sent);
|
2019-03-22 14:37:03 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
void RtmpProtocol::onParseRtmp(const char *data, size_t size) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
input(data, size);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
const char *RtmpProtocol::onSearchPacketTail(const char *data,size_t len){
|
2020-08-30 10:48:34 +08:00
|
|
|
|
//移动拷贝提高性能
|
2020-11-01 03:41:35 +08:00
|
|
|
|
auto next_step_func(std::move(_next_step_func));
|
2020-08-30 10:48:34 +08:00
|
|
|
|
//执行下一步
|
2020-11-01 03:41:35 +08:00
|
|
|
|
auto ret = next_step_func(data, len);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (!_next_step_func) {
|
|
|
|
|
//为设置下一步,恢复之
|
|
|
|
|
next_step_func.swap(_next_step_func);
|
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ret;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////for client////
|
2022-09-30 13:34:56 +08:00
|
|
|
|
void RtmpProtocol::startClientSession(const function<void()> &func, bool complex) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//发送 C0C1
|
|
|
|
|
char handshake_head = HANDSHAKE_PLAINTEXT;
|
|
|
|
|
onSendRawData(obtainBuffer(&handshake_head, 1));
|
|
|
|
|
RtmpHandshake c1(0);
|
2022-09-30 13:34:56 +08:00
|
|
|
|
if (complex) {
|
|
|
|
|
c1.create_complex_c0c1();
|
|
|
|
|
}
|
2020-03-20 11:51:24 +08:00
|
|
|
|
onSendRawData(obtainBuffer((char *) (&c1), sizeof(c1)));
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this, func](const char *data, size_t len) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//等待 S0+S1+S2
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_S0S1S2(data, len, func);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
const char* RtmpProtocol::handle_S0S1S2(const char *data, size_t len, const function<void()> &func) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < 1 + 2 * C1_HANDSHARK_SIZE) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//数据不够
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return nullptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (data[0] != HANDSHAKE_PLAINTEXT) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("only plaintext[0x03] handshake supported");
|
|
|
|
|
}
|
|
|
|
|
//发送 C2
|
2020-11-01 03:41:35 +08:00
|
|
|
|
const char *pcC2 = data + 1;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE));
|
|
|
|
|
//握手结束
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//握手结束并且开始进入解析命令模式
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_rtmp(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2020-08-30 10:48:34 +08:00
|
|
|
|
func();
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return data + 1 + 2 * C1_HANDSHARK_SIZE;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
////for server ////
|
2021-01-17 18:31:50 +08:00
|
|
|
|
const char * RtmpProtocol::handle_C0C1(const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < 1 + C1_HANDSHARK_SIZE) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//need more data!
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return nullptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (data[0] != HANDSHAKE_PLAINTEXT) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("only plaintext[0x03] handshake supported");
|
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (memcmp(data + 5, "\x00\x00\x00\x00", 4) == 0) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//simple handsharke
|
2020-11-01 03:41:35 +08:00
|
|
|
|
handle_C1_simple(data);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
} else {
|
2017-05-16 11:45:36 +08:00
|
|
|
|
#ifdef ENABLE_OPENSSL
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//complex handsharke
|
2020-11-01 03:41:35 +08:00
|
|
|
|
handle_C1_complex(data);
|
2017-05-16 11:45:36 +08:00
|
|
|
|
#else
|
2020-03-20 11:51:24 +08:00
|
|
|
|
WarnL << "未打开ENABLE_OPENSSL宏,复杂握手采用简单方式处理,flash播放器可能无法播放!";
|
2020-11-01 03:41:35 +08:00
|
|
|
|
handle_C1_simple(data);
|
2017-05-16 11:45:36 +08:00
|
|
|
|
#endif//ENABLE_OPENSSL
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return data + 1 + C1_HANDSHARK_SIZE;
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2020-11-01 03:41:35 +08:00
|
|
|
|
void RtmpProtocol::handle_C1_simple(const char *data){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//发送S0
|
|
|
|
|
char handshake_head = HANDSHAKE_PLAINTEXT;
|
|
|
|
|
onSendRawData(obtainBuffer(&handshake_head, 1));
|
|
|
|
|
//发送S1
|
|
|
|
|
RtmpHandshake s1(0);
|
|
|
|
|
onSendRawData(obtainBuffer((char *) &s1, C1_HANDSHARK_SIZE));
|
|
|
|
|
//发送S2
|
2020-11-01 03:41:35 +08:00
|
|
|
|
onSendRawData(obtainBuffer(data + 1, C1_HANDSHARK_SIZE));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//等待C2
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
//握手结束并且开始进入解析命令模式
|
|
|
|
|
return handle_C2(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-05-16 11:51:18 +08:00
|
|
|
|
#ifdef ENABLE_OPENSSL
|
2020-11-01 03:41:35 +08:00
|
|
|
|
void RtmpProtocol::handle_C1_complex(const char *data){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//参考自:http://blog.csdn.net/win_lin/article/details/13006803
|
|
|
|
|
//skip c0,time,version
|
2020-11-01 03:41:35 +08:00
|
|
|
|
const char *c1_start = data + 1;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
const char *schema_start = c1_start + 8;
|
|
|
|
|
char *digest_start;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
try {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* c1s1 schema0
|
|
|
|
|
time: 4bytes
|
|
|
|
|
version: 4bytes
|
|
|
|
|
key: 764bytes
|
|
|
|
|
digest: 764bytes
|
|
|
|
|
*/
|
2020-08-30 10:48:34 +08:00
|
|
|
|
auto digest = get_C1_digest((uint8_t *) schema_start + C1_SCHEMA_SIZE, &digest_start);
|
|
|
|
|
string c1_joined(c1_start, C1_HANDSHARK_SIZE);
|
|
|
|
|
c1_joined.erase(digest_start - c1_start, C1_DIGEST_SIZE);
|
|
|
|
|
check_C1_Digest(digest, c1_joined);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
send_complex_S0S1S2(0, digest);
|
2020-01-08 14:00:53 +08:00
|
|
|
|
// InfoL << "schema0";
|
2021-01-17 18:31:50 +08:00
|
|
|
|
} catch (std::exception &) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//貌似flash从来都不用schema1
|
2020-01-08 14:00:53 +08:00
|
|
|
|
// WarnL << "try rtmp complex schema0 failed:" << ex.what();
|
2020-08-30 10:48:34 +08:00
|
|
|
|
try {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* c1s1 schema1
|
|
|
|
|
time: 4bytes
|
|
|
|
|
version: 4bytes
|
|
|
|
|
digest: 764bytes
|
|
|
|
|
key: 764bytes
|
|
|
|
|
*/
|
2020-08-30 10:48:34 +08:00
|
|
|
|
auto digest = get_C1_digest((uint8_t *) schema_start, &digest_start);
|
|
|
|
|
string c1_joined(c1_start, C1_HANDSHARK_SIZE);
|
|
|
|
|
c1_joined.erase(digest_start - c1_start, C1_DIGEST_SIZE);
|
|
|
|
|
check_C1_Digest(digest, c1_joined);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
send_complex_S0S1S2(1, digest);
|
2020-01-08 14:00:53 +08:00
|
|
|
|
// InfoL << "schema1";
|
2021-01-17 18:31:50 +08:00
|
|
|
|
} catch (std::exception &) {
|
2020-01-08 14:00:53 +08:00
|
|
|
|
// WarnL << "try rtmp complex schema1 failed:" << ex.what();
|
2020-11-01 03:41:35 +08:00
|
|
|
|
handle_C1_simple(data);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 18:39:30 +08:00
|
|
|
|
#if !defined(u_int8_t)
|
|
|
|
|
#define u_int8_t unsigned char
|
|
|
|
|
#endif // !defined(u_int8_t)
|
2017-05-16 11:45:36 +08:00
|
|
|
|
|
|
|
|
|
static u_int8_t FMSKey[] = {
|
|
|
|
|
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
|
|
|
|
|
0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
|
|
|
|
|
0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
|
|
|
|
|
0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
|
|
|
|
0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
|
|
|
|
|
0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
|
|
|
|
|
0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
|
|
|
|
|
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
|
|
|
|
|
0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
|
|
|
|
|
}; // 68
|
|
|
|
|
|
|
|
|
|
static u_int8_t FPKey[] = {
|
|
|
|
|
0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
|
|
|
|
|
0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
|
|
|
|
|
0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
|
|
|
|
|
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
|
|
|
|
|
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
|
|
|
|
|
0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
|
|
|
|
|
0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
|
|
|
|
|
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
|
|
|
|
|
}; // 62
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-05-16 11:45:36 +08:00
|
|
|
|
void RtmpProtocol::check_C1_Digest(const string &digest,const string &data){
|
2020-08-30 10:48:34 +08:00
|
|
|
|
auto sha256 = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, data.data(), data.size());
|
|
|
|
|
if (sha256 != digest) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("digest mismatched");
|
2020-08-30 10:48:34 +08:00
|
|
|
|
} else {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
InfoL << "check rtmp complex handshark success!";
|
|
|
|
|
}
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-05-16 11:45:36 +08:00
|
|
|
|
string RtmpProtocol::get_C1_digest(const uint8_t *ptr,char **digestPos){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* 764bytes digest结构
|
|
|
|
|
offset: 4bytes
|
|
|
|
|
random-data: (offset)bytes
|
|
|
|
|
digest-data: 32bytes
|
|
|
|
|
random-data: (764-4-offset-32)bytes
|
|
|
|
|
*/
|
|
|
|
|
int offset = 0;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
for (int i = 0; i < C1_OFFSET_SIZE; ++i) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
offset += ptr[i];
|
|
|
|
|
}
|
|
|
|
|
offset %= (C1_SCHEMA_SIZE - C1_DIGEST_SIZE - C1_OFFSET_SIZE);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
*digestPos = (char *) ptr + C1_OFFSET_SIZE + offset;
|
|
|
|
|
string digest(*digestPos, C1_DIGEST_SIZE);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//DebugL << "digest offset:" << offset << ",digest:" << hexdump(digest.data(),digest.size());
|
|
|
|
|
return digest;
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-05-16 11:45:36 +08:00
|
|
|
|
string RtmpProtocol::get_C1_key(const uint8_t *ptr){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* 764bytes key结构
|
|
|
|
|
random-data: (offset)bytes
|
|
|
|
|
key-data: 128bytes
|
|
|
|
|
random-data: (764-offset-128-4)bytes
|
|
|
|
|
offset: 4bytes
|
|
|
|
|
*/
|
|
|
|
|
int offset = 0;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
for (int i = C1_SCHEMA_SIZE - C1_OFFSET_SIZE; i < C1_SCHEMA_SIZE; ++i) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
offset += ptr[i];
|
|
|
|
|
}
|
|
|
|
|
offset %= (C1_SCHEMA_SIZE - C1_KEY_SIZE - C1_OFFSET_SIZE);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
string key((char *) ptr + offset, C1_KEY_SIZE);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//DebugL << "key offset:" << offset << ",key:" << hexdump(key.data(),key.size());
|
|
|
|
|
return key;
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2017-05-16 11:45:36 +08:00
|
|
|
|
void RtmpProtocol::send_complex_S0S1S2(int schemeType,const string &digest){
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go
|
|
|
|
|
//发送S0
|
|
|
|
|
char handshake_head = HANDSHAKE_PLAINTEXT;
|
|
|
|
|
onSendRawData(obtainBuffer(&handshake_head, 1));
|
|
|
|
|
//S1
|
|
|
|
|
RtmpHandshake s1(0);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
memcpy(s1.zero, "\x04\x05\x00\x01", 4);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
char *digestPos;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (schemeType == 0) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* c1s1 schema0
|
|
|
|
|
time: 4bytes
|
|
|
|
|
version: 4bytes
|
|
|
|
|
key: 764bytes
|
|
|
|
|
digest: 764bytes
|
|
|
|
|
*/
|
2020-08-30 10:48:34 +08:00
|
|
|
|
get_C1_digest(s1.random + C1_SCHEMA_SIZE, &digestPos);
|
|
|
|
|
} else {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
/* c1s1 schema1
|
|
|
|
|
time: 4bytes
|
|
|
|
|
version: 4bytes
|
|
|
|
|
digest: 764bytes
|
|
|
|
|
key: 764bytes
|
|
|
|
|
*/
|
2020-08-30 10:48:34 +08:00
|
|
|
|
get_C1_digest(s1.random, &digestPos);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
char *s1_start = (char *) &s1;
|
|
|
|
|
string s1_joined(s1_start, sizeof(s1));
|
|
|
|
|
s1_joined.erase(digestPos - s1_start, C1_DIGEST_SIZE);
|
|
|
|
|
string s1_digest = openssl_HMACsha256(FMSKey, S1_FMS_KEY_SIZE, s1_joined.data(), s1_joined.size());
|
|
|
|
|
memcpy(digestPos, s1_digest.data(), s1_digest.size());
|
2020-03-20 11:51:24 +08:00
|
|
|
|
onSendRawData(obtainBuffer((char *) &s1, sizeof(s1)));
|
|
|
|
|
|
|
|
|
|
//S2
|
2020-08-30 10:48:34 +08:00
|
|
|
|
string s2_key = openssl_HMACsha256(FMSKey, S2_FMS_KEY_SIZE, digest.data(), digest.size());
|
2020-03-20 11:51:24 +08:00
|
|
|
|
RtmpHandshake s2(0);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
s2.random_generate((char *) &s2, 8);
|
|
|
|
|
string s2_digest = openssl_HMACsha256(s2_key.data(), s2_key.size(), &s2, sizeof(s2) - C1_DIGEST_SIZE);
|
|
|
|
|
memcpy((char *) &s2 + C1_HANDSHARK_SIZE - C1_DIGEST_SIZE, s2_digest.data(), C1_DIGEST_SIZE);
|
|
|
|
|
onSendRawData(obtainBuffer((char *) &s2, sizeof(s2)));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//等待C2
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_C2(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2017-05-16 11:45:36 +08:00
|
|
|
|
}
|
2017-05-16 11:51:18 +08:00
|
|
|
|
#endif //ENABLE_OPENSSL
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2021-12-26 19:58:01 +08:00
|
|
|
|
//发送复杂握手c0c1
|
|
|
|
|
void RtmpHandshake::create_complex_c0c1() {
|
|
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
|
memcpy(zero, "\x80\x00\x07\x02", 4);
|
|
|
|
|
//digest随机偏移长度
|
|
|
|
|
auto offset_value = rand() % (C1_SCHEMA_SIZE - C1_OFFSET_SIZE - C1_DIGEST_SIZE);
|
|
|
|
|
//设置digest偏移长度
|
|
|
|
|
auto offset_ptr = random + C1_SCHEMA_SIZE;
|
|
|
|
|
offset_ptr[0] = offset_ptr[1] = offset_ptr[2] = offset_value / 4;
|
|
|
|
|
offset_ptr[3] = offset_value - 3 * (offset_value / 4);
|
|
|
|
|
//去除digest后的剩余随机数据
|
|
|
|
|
string str((char *) this, sizeof(*this));
|
|
|
|
|
str.erase(8 + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, C1_DIGEST_SIZE);
|
|
|
|
|
//获取摘要
|
|
|
|
|
auto digest_value = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, str.data(), str.size());
|
|
|
|
|
//插入摘要
|
|
|
|
|
memcpy(random + C1_SCHEMA_SIZE + C1_OFFSET_SIZE + offset_value, digest_value.data(), digest_value.size());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
const char* RtmpProtocol::handle_C2(const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < C1_HANDSHARK_SIZE) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//need more data!
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return nullptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2021-01-17 18:31:50 +08:00
|
|
|
|
_next_step_func = [this](const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return handle_rtmp(data, len);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
};
|
2020-11-01 03:41:35 +08:00
|
|
|
|
|
|
|
|
|
//握手结束,进入命令模式
|
|
|
|
|
return handle_rtmp(data + C1_HANDSHARK_SIZE, len - C1_HANDSHARK_SIZE);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-22 10:39:16 +08:00
|
|
|
|
static constexpr size_t HEADER_LENGTH[] = {12, 8, 4, 1};
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
const char* RtmpProtocol::handle_rtmp(const char *data, size_t len) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
auto ptr = data;
|
|
|
|
|
while (len) {
|
2021-06-22 10:39:16 +08:00
|
|
|
|
size_t offset = 0;
|
|
|
|
|
auto header = (RtmpHeader *) ptr;
|
|
|
|
|
auto header_len = HEADER_LENGTH[header->fmt];
|
|
|
|
|
_now_chunk_id = header->chunk_id;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
switch (_now_chunk_id) {
|
2020-08-01 10:20:27 +08:00
|
|
|
|
case 0: {
|
|
|
|
|
//0 值表示二字节形式,并且 ID 范围 64 - 319
|
|
|
|
|
//(第二个字节 + 64)。
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < 2) {
|
2020-08-01 10:20:27 +08:00
|
|
|
|
//need more data
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2020-08-01 10:20:27 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
_now_chunk_id = 64 + (uint8_t) (ptr[1]);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
offset = 1;
|
2020-08-01 10:20:27 +08:00
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-01 10:20:27 +08:00
|
|
|
|
case 1: {
|
|
|
|
|
//1 值表示三字节形式,并且 ID 范围为 64 - 65599
|
|
|
|
|
//((第三个字节) * 256 + 第二个字节 + 64)。
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < 3) {
|
2020-08-01 10:20:27 +08:00
|
|
|
|
//need more data
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2020-08-01 10:20:27 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
_now_chunk_id = 64 + ((uint8_t) (ptr[2]) << 8) + (uint8_t) (ptr[1]);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
offset = 2;
|
2020-08-01 10:20:27 +08:00
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。
|
|
|
|
|
default : break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < header_len + offset) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//need more data
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2021-06-22 10:39:16 +08:00
|
|
|
|
header = (RtmpHeader *) (ptr + offset);
|
2021-02-07 23:01:22 +08:00
|
|
|
|
auto &pr = _map_chunk_data[_now_chunk_id];
|
|
|
|
|
auto &now_packet = pr.first;
|
|
|
|
|
auto &last_packet = pr.second;
|
|
|
|
|
if (!now_packet) {
|
|
|
|
|
now_packet = RtmpPacket::create();
|
|
|
|
|
if (last_packet) {
|
|
|
|
|
//恢复chunk上下文
|
|
|
|
|
*now_packet = *last_packet;
|
|
|
|
|
}
|
|
|
|
|
//绝对时间戳标记复位
|
|
|
|
|
now_packet->is_abs_stamp = false;
|
2021-02-04 17:58:51 +08:00
|
|
|
|
}
|
2021-02-07 23:01:22 +08:00
|
|
|
|
auto &chunk_data = *now_packet;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
chunk_data.chunk_id = _now_chunk_id;
|
|
|
|
|
switch (header_len) {
|
2020-08-01 10:20:27 +08:00
|
|
|
|
case 12:
|
2020-08-30 10:48:34 +08:00
|
|
|
|
chunk_data.is_abs_stamp = true;
|
2021-06-22 10:39:16 +08:00
|
|
|
|
chunk_data.stream_index = load_le32(header->stream_index);
|
2020-08-01 10:20:27 +08:00
|
|
|
|
case 8:
|
2021-06-22 10:39:16 +08:00
|
|
|
|
chunk_data.body_size = load_be24(header->body_size);
|
|
|
|
|
chunk_data.type_id = header->type_id;
|
2020-08-01 10:20:27 +08:00
|
|
|
|
case 4:
|
2021-06-22 10:39:16 +08:00
|
|
|
|
chunk_data.ts_field = load_be24(header->time_stamp);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-01 10:20:27 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
auto time_stamp = chunk_data.ts_field;
|
|
|
|
|
if (chunk_data.ts_field == 0xFFFFFF) {
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < header_len + offset + 4) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//need more data
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
time_stamp = load_be32(ptr + offset + header_len);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
offset += 4;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-01 10:20:27 +08:00
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.body_size < chunk_data.buffer.size()) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("非法的bodySize");
|
|
|
|
|
}
|
2020-08-01 10:20:27 +08:00
|
|
|
|
|
2021-02-07 23:01:22 +08:00
|
|
|
|
auto more = min(_chunk_size_in, (size_t) (chunk_data.body_size - chunk_data.buffer.size()));
|
2020-11-01 03:41:35 +08:00
|
|
|
|
if (len < header_len + offset + more) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//need more data
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-12-13 09:51:08 +08:00
|
|
|
|
if (more) {
|
|
|
|
|
chunk_data.buffer.append(ptr + header_len + offset, more);
|
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
ptr += header_len + offset + more;
|
|
|
|
|
len -= header_len + offset + more;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.buffer.size() == chunk_data.body_size) {
|
2017-05-27 09:42:08 +08:00
|
|
|
|
//frame is ready
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_now_stream_index = chunk_data.stream_index;
|
|
|
|
|
chunk_data.time_stamp = time_stamp + (chunk_data.is_abs_stamp ? 0 : chunk_data.time_stamp);
|
2021-02-07 23:01:22 +08:00
|
|
|
|
//保存chunk上下文
|
|
|
|
|
last_packet = now_packet;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.body_size) {
|
2021-02-07 23:01:22 +08:00
|
|
|
|
handle_chunk(std::move(now_packet));
|
2021-02-04 17:58:51 +08:00
|
|
|
|
} else {
|
2021-02-07 23:01:22 +08:00
|
|
|
|
now_packet = nullptr;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-01 03:41:35 +08:00
|
|
|
|
return ptr;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 17:58:51 +08:00
|
|
|
|
void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) {
|
|
|
|
|
auto &chunk_data = *packet;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
switch (chunk_data.type_id) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
case MSG_ACK: {
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("MSG_ACK: Not enough data");
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
//auto bytePeerRecv = load_be32(&chunk_data.buffer[0]);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
//TraceL << "MSG_ACK:" << bytePeerRecv;
|
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 11:51:24 +08:00
|
|
|
|
case MSG_SET_CHUNK: {
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("MSG_SET_CHUNK :Not enough data");
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
_chunk_size_in = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
TraceL << "MSG_SET_CHUNK:" << _chunk_size_in;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 11:51:24 +08:00
|
|
|
|
case MSG_USER_CONTROL: {
|
|
|
|
|
//user control message
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (chunk_data.buffer.size() < 2) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("MSG_USER_CONTROL: Not enough data.");
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
uint16_t event_type = load_be16(&chunk_data.buffer[0]);
|
|
|
|
|
chunk_data.buffer.erase(0, 2);
|
2020-03-20 11:51:24 +08:00
|
|
|
|
switch (event_type) {
|
2020-08-30 10:48:34 +08:00
|
|
|
|
case CONTROL_PING_REQUEST: {
|
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
throw std::runtime_error("CONTROL_PING_REQUEST: Not enough data.");
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
uint32_t timeStamp = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
//TraceL << "CONTROL_PING_REQUEST:" << time_stamp;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
sendUserControl(CONTROL_PING_RESPONSE, timeStamp);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
|
|
|
|
case CONTROL_PING_RESPONSE: {
|
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
|
|
|
|
throw std::runtime_error("CONTROL_PING_RESPONSE: Not enough data.");
|
|
|
|
|
}
|
|
|
|
|
//uint32_t time_stamp = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
//TraceL << "CONTROL_PING_RESPONSE:" << time_stamp;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
|
|
|
|
case CONTROL_STREAM_BEGIN: {
|
|
|
|
|
//开始播放
|
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
2021-12-22 22:06:19 +08:00
|
|
|
|
WarnL << "CONTROL_STREAM_BEGIN: Not enough data:" << chunk_data.buffer.size();
|
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
uint32_t stream_index = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
onStreamBegin(stream_index);
|
|
|
|
|
TraceL << "CONTROL_STREAM_BEGIN:" << stream_index;
|
|
|
|
|
break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-30 10:48:34 +08:00
|
|
|
|
case CONTROL_STREAM_EOF: {
|
|
|
|
|
//暂停
|
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
|
|
|
|
throw std::runtime_error("CONTROL_STREAM_EOF: Not enough data.");
|
|
|
|
|
}
|
|
|
|
|
uint32_t stream_index = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
onStreamEof(stream_index);
|
|
|
|
|
TraceL << "CONTROL_STREAM_EOF:" << stream_index;
|
|
|
|
|
break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
|
|
|
|
case CONTROL_STREAM_DRY: {
|
|
|
|
|
//停止播放
|
|
|
|
|
if (chunk_data.buffer.size() < 4) {
|
|
|
|
|
throw std::runtime_error("CONTROL_STREAM_DRY: Not enough data.");
|
|
|
|
|
}
|
|
|
|
|
uint32_t stream_index = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
onStreamDry(stream_index);
|
|
|
|
|
TraceL << "CONTROL_STREAM_DRY:" << stream_index;
|
|
|
|
|
break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
|
|
|
|
default: /*WarnL << "unhandled user control:" << event_type; */ break;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
2020-03-20 11:51:24 +08:00
|
|
|
|
|
|
|
|
|
case MSG_WIN_SIZE: {
|
2022-07-29 12:02:00 +08:00
|
|
|
|
//如果窗口太小,会导致发送sendAcknowledgement时无限递归:https://github.com/ZLMediaKit/ZLMediaKit/issues/1839
|
2022-09-30 13:34:56 +08:00
|
|
|
|
//窗口太大,也可能导致fms服务器认为播放器心跳超时
|
|
|
|
|
_windows_size = min(max(load_be32(&chunk_data.buffer[0]), 32 * 1024U), 1280 * 1024U);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
TraceL << "MSG_WIN_SIZE:" << _windows_size;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
|
|
|
|
case MSG_SET_PEER_BW: {
|
|
|
|
|
_bandwidth = load_be32(&chunk_data.buffer[0]);
|
|
|
|
|
_band_limit_type = chunk_data.buffer[4];
|
2022-07-29 12:02:00 +08:00
|
|
|
|
TraceL << "MSG_SET_PEER_BW:" << _bandwidth << " " << (int)_band_limit_type;
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 11:51:24 +08:00
|
|
|
|
case MSG_AGGREGATE: {
|
2020-08-30 10:48:34 +08:00
|
|
|
|
auto ptr = (uint8_t *) chunk_data.buffer.data();
|
2020-11-01 03:41:35 +08:00
|
|
|
|
auto ptr_tail = ptr + chunk_data.buffer.size();
|
2022-04-28 14:13:09 +08:00
|
|
|
|
uint32_t latest_ts, timestamp;
|
|
|
|
|
timestamp = chunk_data.time_stamp;
|
|
|
|
|
bool first_message = true;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
while (ptr + 8 + 3 < ptr_tail) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
auto type = *ptr;
|
|
|
|
|
ptr += 1;
|
|
|
|
|
auto size = load_be24(ptr);
|
|
|
|
|
ptr += 3;
|
|
|
|
|
auto ts = load_be24(ptr);
|
|
|
|
|
ptr += 3;
|
|
|
|
|
ts |= (*ptr << 24);
|
|
|
|
|
ptr += 1;
|
|
|
|
|
ptr += 3;
|
|
|
|
|
//参考FFmpeg多拷贝了4个字节
|
|
|
|
|
size += 4;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
if (ptr + size > ptr_tail) {
|
2020-03-20 11:51:24 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2022-04-28 14:13:09 +08:00
|
|
|
|
if (!first_message) {
|
|
|
|
|
timestamp += ts - latest_ts;
|
|
|
|
|
}
|
|
|
|
|
first_message = false;
|
|
|
|
|
latest_ts = ts;
|
2021-02-04 17:58:51 +08:00
|
|
|
|
auto sub_packet_ptr = RtmpPacket::create();
|
|
|
|
|
auto &sub_packet = *sub_packet_ptr;
|
2020-11-01 03:41:35 +08:00
|
|
|
|
sub_packet.buffer.assign((char *)ptr, size);
|
2020-08-30 10:48:34 +08:00
|
|
|
|
sub_packet.type_id = type;
|
|
|
|
|
sub_packet.body_size = size;
|
2022-04-28 14:13:09 +08:00
|
|
|
|
sub_packet.time_stamp = timestamp;
|
2020-08-30 10:48:34 +08:00
|
|
|
|
sub_packet.stream_index = chunk_data.stream_index;
|
|
|
|
|
sub_packet.chunk_id = chunk_data.chunk_id;
|
2021-02-04 17:58:51 +08:00
|
|
|
|
handle_chunk(std::move(sub_packet_ptr));
|
2020-03-20 11:51:24 +08:00
|
|
|
|
ptr += size;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
|
2022-09-30 13:34:56 +08:00
|
|
|
|
default: {
|
|
|
|
|
_bytes_recv += packet->size();
|
|
|
|
|
if (_windows_size > 0 && _bytes_recv - _bytes_recv_last >= _windows_size) {
|
|
|
|
|
_bytes_recv_last = _bytes_recv;
|
|
|
|
|
sendAcknowledgement(_bytes_recv);
|
|
|
|
|
}
|
|
|
|
|
onRtmpChunk(std::move(packet));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-30 10:48:34 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 18:31:50 +08:00
|
|
|
|
BufferRaw::Ptr RtmpProtocol::obtainBuffer(const void *data, size_t len) {
|
2022-01-06 14:30:44 +08:00
|
|
|
|
auto buffer = _packet_pool.obtain2();
|
2021-02-07 14:55:09 +08:00
|
|
|
|
if (data && len) {
|
|
|
|
|
buffer->assign((const char *) data, len);
|
|
|
|
|
}
|
2022-01-06 14:30:44 +08:00
|
|
|
|
return buffer;
|
2018-09-26 23:12:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-24 17:17:55 +08:00
|
|
|
|
} /* namespace mediakit */
|