From 82699ff616a49e878afe6171c6b7466477a215c2 Mon Sep 17 00:00:00 2001 From: winlin Date: Sun, 5 Aug 2018 10:39:52 +0800 Subject: [PATCH] Refine code for librtmp --- trunk/src/libs/srs_librtmp.cpp | 4310 ++++++++++++++++---------------- trunk/src/libs/srs_librtmp.hpp | 2214 ++++++++-------- 2 files changed, 3262 insertions(+), 3262 deletions(-) diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 824610d1c..388604607 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -522,2370 +522,2370 @@ int srs_librtmp_context_connect(Context* context) extern "C"{ #endif - int srs_version_major() - { - return VERSION_MAJOR; - } +int srs_version_major() +{ + return VERSION_MAJOR; +} + +int srs_version_minor() +{ + return VERSION_MINOR; +} + +int srs_version_revision() +{ + return VERSION_REVISION; +} + +srs_rtmp_t srs_rtmp_create(const char* url) +{ + int ret = ERROR_SUCCESS; - int srs_version_minor() - { - return VERSION_MINOR; - } + Context* context = new Context(); + context->url = url; - int srs_version_revision() - { - return VERSION_REVISION; - } + // create socket + srs_freep(context->skt); + context->skt = new SimpleSocketStream(); - srs_rtmp_t srs_rtmp_create(const char* url) - { - int ret = ERROR_SUCCESS; - - Context* context = new Context(); - context->url = url; - - // create socket - srs_freep(context->skt); - context->skt = new SimpleSocketStream(); - - if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { - srs_human_error("Create socket failed, ret=%d", ret); - - // free the context and return NULL - srs_freep(context); - return NULL; - } - - return context; - } - - int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms) - { - int ret = ERROR_SUCCESS; - - if (!rtmp) { - return ret; - } - - Context* context = (Context*)rtmp; - - context->stimeout = send_timeout_ms; - context->rtimeout = recv_timeout_ms; - - context->skt->set_recv_timeout(context->rtimeout); - context->skt->set_send_timeout(context->stimeout); - - return ret; - } - - void srs_rtmp_destroy(srs_rtmp_t rtmp) - { - if (!rtmp) { - return; - } - - Context* context = (Context*)rtmp; + if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { + srs_human_error("Create socket failed, ret=%d", ret); + // free the context and return NULL srs_freep(context); + return NULL; } - int srs_rtmp_handshake(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - if ((ret = srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { - return ret; - } - + return context; +} + +int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms) +{ + int ret = ERROR_SUCCESS; + + if (!rtmp) { return ret; } - int srs_rtmp_dns_resolve(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // parse uri - if ((ret = srs_librtmp_context_parse_uri(context)) != ERROR_SUCCESS) { - return ret; - } - // resolve host - if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) { - return ret; - } - + Context* context = (Context*)rtmp; + + context->stimeout = send_timeout_ms; + context->rtimeout = recv_timeout_ms; + + context->skt->set_recv_timeout(context->rtimeout); + context->skt->set_send_timeout(context->stimeout); + + return ret; +} + +void srs_rtmp_destroy(srs_rtmp_t rtmp) +{ + if (!rtmp) { + return; + } + + Context* context = (Context*)rtmp; + + srs_freep(context); +} + +int srs_rtmp_handshake(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_connect_server(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // set timeout if user not set. - if (context->stimeout == SRS_CONSTS_NO_TMMS) { - context->stimeout = SRS_SOCKET_DEFAULT_TMMS; - context->skt->set_send_timeout(context->stimeout); - } - if (context->rtimeout == SRS_CONSTS_NO_TMMS) { - context->rtimeout = SRS_SOCKET_DEFAULT_TMMS; - context->skt->set_recv_timeout(context->rtimeout); - } - - if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) { - return ret; - } - + if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp) - { + if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_rtmp_dns_resolve(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // parse uri + if ((ret = srs_librtmp_context_parse_uri(context)) != ERROR_SUCCESS) { + return ret; + } + // resolve host + if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_rtmp_connect_server(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // set timeout if user not set. + if (context->stimeout == SRS_CONSTS_NO_TMMS) { + context->stimeout = SRS_SOCKET_DEFAULT_TMMS; + context->skt->set_send_timeout(context->stimeout); + } + if (context->rtimeout == SRS_CONSTS_NO_TMMS) { + context->rtimeout = SRS_SOCKET_DEFAULT_TMMS; + context->skt->set_recv_timeout(context->rtimeout); + } + + if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp) +{ #ifndef SRS_AUTO_SSL - // complex handshake requires ssl - return ERROR_RTMP_HS_SSL_REQUIRE; + // complex handshake requires ssl + return ERROR_RTMP_HS_SSL_REQUIRE; #else - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - srs_assert(context->skt != NULL); - - // simple handshake - srs_freep(context->rtmp); - context->rtmp = new SrsRtmpClient(context->skt); - - if ((err = context->rtmp->complex_handshake()) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + srs_assert(context->skt != NULL); + + // simple handshake + srs_freep(context->rtmp); + context->rtmp = new SrsRtmpClient(context->skt); + + if ((err = context->rtmp->complex_handshake()) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; + } + + return ret; #endif - } +} + +int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; - int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - srs_assert(context->skt != NULL); - - // simple handshake - srs_freep(context->rtmp); - context->rtmp = new SrsRtmpClient(context->skt); - - if ((err = context->rtmp->simple_handshake()) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + srs_assert(context->skt != NULL); + + // simple handshake + srs_freep(context->rtmp); + context->rtmp = new SrsRtmpClient(context->skt); + + if ((err = context->rtmp->simple_handshake()) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - srs_freep(context->req); - context->req = new SrsRequest(); - - if (args) { - context->req->args = (SrsAmf0Object*)args; - } - if (tcUrl) { - context->req->tcUrl = tcUrl; - } - if (swfUrl) { - context->req->swfUrl = swfUrl; - } - if (pageUrl) { - context->req->pageUrl = pageUrl; - } - + return ret; +} + +int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + srs_freep(context->req); + context->req = new SrsRequest(); + + if (args) { + context->req->args = (SrsAmf0Object*)args; + } + if (tcUrl) { + context->req->tcUrl = tcUrl; + } + if (swfUrl) { + context->req->swfUrl = swfUrl; + } + if (pageUrl) { + context->req->pageUrl = pageUrl; + } + + return ret; +} + +int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema) +{ + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + context->schema = schema; + + return ret; +} + +int srs_rtmp_connect_app(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + string tcUrl; + switch(context->schema) { + // For SRS3, only use one format url. + case srs_url_schema_normal: + case srs_url_schema_via: + case srs_url_schema_vis: + case srs_url_schema_vis2: + tcUrl = srs_generate_tc_url(context->ip, context->vhost, context->app, context->port); + default: + break; + } + + Context* c = context; + if ((err = context->rtmp->connect_app(c->app, tcUrl, c->req, true, &c->si)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema) - { - int ret = ERROR_SUCCESS; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - context->schema = schema; - + return ret; +} + +int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid) +{ + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + *pid = context->si.pid; + *cid = context->si.cid; + *ip = context->si.ip.empty()? NULL:(char*)context->si.ip.c_str(); + + return ret; +} + +int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig) +{ + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + *sig = context->si.sig.empty()? NULL:(char*)context->si.sig.c_str(); + + return ret; +} + +int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build) +{ + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + *major = context->si.major; + *minor = context->si.minor; + *revision = context->si.revision; + *build = context->si.build; + + return ret; +} + +int srs_rtmp_play_stream(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + if ((err = context->rtmp->create_stream(context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_connect_app(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - string tcUrl; - switch(context->schema) { - // For SRS3, only use one format url. - case srs_url_schema_normal: - case srs_url_schema_via: - case srs_url_schema_vis: - case srs_url_schema_vis2: - tcUrl = srs_generate_tc_url(context->ip, context->vhost, context->app, context->port); - default: - break; - } - - Context* c = context; - if ((err = context->rtmp->connect_app(c->app, tcUrl, c->req, true, &c->si)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->play(stream, context->stream_id, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *pid = context->si.pid; - *cid = context->si.cid; - *ip = context->si.ip.empty()? NULL:(char*)context->si.ip.c_str(); - + return ret; +} + +int srs_rtmp_publish_stream(srs_rtmp_t rtmp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 + string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); + + if ((err = context->rtmp->fmle_publish(stream, context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *sig = context->si.sig.empty()? NULL:(char*)context->si.sig.c_str(); - + return ret; +} + +int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, + int64_t* start_time, int64_t* end_time, + int* play_kbps, int* publish_kbps, + int* play_bytes, int* publish_bytes, + int* play_duration, int* publish_duration +) { + *start_time = 0; + *end_time = 0; + *play_kbps = 0; + *publish_kbps = 0; + *play_bytes = 0; + *publish_bytes = 0; + *play_duration = 0; + *publish_duration = 0; + + int ret = ERROR_SUCCESS; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsBandwidthClient client; + + if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) { return ret; } - int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build) - { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - *major = context->si.major; - *minor = context->si.minor; - *revision = context->si.revision; - *build = context->si.build; - - return ret; - } - - int srs_rtmp_play_stream(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - if ((err = context->rtmp->create_stream(context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); - - if ((err = context->rtmp->play(stream, context->stream_id, SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; - } - - int srs_rtmp_publish_stream(srs_rtmp_t rtmp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - // Pass params in stream, @see https://github.com/ossrs/srs/issues/1031#issuecomment-409745733 - string stream = srs_generate_stream_with_query(context->host, context->vhost, context->stream, context->param); - - if ((err = context->rtmp->fmle_publish(stream, context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; - } - - int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, - int64_t* start_time, int64_t* end_time, - int* play_kbps, int* publish_kbps, - int* play_bytes, int* publish_bytes, - int* play_duration, int* publish_duration + if ((ret = client.bandwidth_check( + start_time, end_time, play_kbps, publish_kbps, + play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS ) { - *start_time = 0; - *end_time = 0; - *play_kbps = 0; - *publish_kbps = 0; - *play_bytes = 0; - *publish_bytes = 0; - *play_duration = 0; - *publish_duration = 0; + return ret; + } + + return ret; +} + + +int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + SrsBuffer* stream = new SrsBuffer(msg->payload, msg->size); + SrsAutoFree(SrsBuffer, stream); + + // the aggregate message always use abs time. + int delta = -1; + + while (!stream->empty()) { + if (!stream->require(1)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message type. ret=%d", ret); + return ret; + } + int8_t type = stream->read_1bytes(); - int ret = ERROR_SUCCESS; + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message size. ret=%d", ret); + return ret; + } + int32_t data_size = stream->read_3bytes(); - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsBandwidthClient client; - - if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) { + if (data_size < 0) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message size(negative). ret=%d", ret); return ret; } - if ((ret = client.bandwidth_check( - start_time, end_time, play_kbps, publish_kbps, - play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS - ) { + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message time. ret=%d", ret); + return ret; + } + int32_t timestamp = stream->read_3bytes(); + + if (!stream->require(1)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message time(high). ret=%d", ret); + return ret; + } + int32_t time_h = stream->read_1bytes(); + + timestamp |= time_h<<24; + timestamp &= 0x7FFFFFFF; + + // adjust abs timestamp in aggregate msg. + if (delta < 0) { + delta = (int)msg->header.timestamp - (int)timestamp; + } + timestamp += delta; + + if (!stream->require(3)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message stream_id. ret=%d", ret); + return ret; + } + int32_t stream_id = stream->read_3bytes(); + + if (data_size > 0 && !stream->require(data_size)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message data. ret=%d", ret); return ret; } - return ret; - } - - - int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg) - { - int ret = ERROR_SUCCESS; + // to common message. + SrsCommonMessage o; - SrsBuffer* stream = new SrsBuffer(msg->payload, msg->size); - SrsAutoFree(SrsBuffer, stream); + o.header.message_type = type; + o.header.payload_length = data_size; + o.header.timestamp_delta = timestamp; + o.header.timestamp = timestamp; + o.header.stream_id = stream_id; + o.header.perfer_cid = msg->header.perfer_cid; - // the aggregate message always use abs time. - int delta = -1; - - while (!stream->empty()) { - if (!stream->require(1)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message type. ret=%d", ret); - return ret; - } - int8_t type = stream->read_1bytes(); - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message size. ret=%d", ret); - return ret; - } - int32_t data_size = stream->read_3bytes(); - - if (data_size < 0) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message size(negative). ret=%d", ret); - return ret; - } - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message time. ret=%d", ret); - return ret; - } - int32_t timestamp = stream->read_3bytes(); - - if (!stream->require(1)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message time(high). ret=%d", ret); - return ret; - } - int32_t time_h = stream->read_1bytes(); - - timestamp |= time_h<<24; - timestamp &= 0x7FFFFFFF; - - // adjust abs timestamp in aggregate msg. - if (delta < 0) { - delta = (int)msg->header.timestamp - (int)timestamp; - } - timestamp += delta; - - if (!stream->require(3)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message stream_id. ret=%d", ret); - return ret; - } - int32_t stream_id = stream->read_3bytes(); - - if (data_size > 0 && !stream->require(data_size)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message data. ret=%d", ret); - return ret; - } - - // to common message. - SrsCommonMessage o; - - o.header.message_type = type; - o.header.payload_length = data_size; - o.header.timestamp_delta = timestamp; - o.header.timestamp = timestamp; - o.header.stream_id = stream_id; - o.header.perfer_cid = msg->header.perfer_cid; - - if (data_size > 0) { - o.size = data_size; - o.payload = new char[o.size]; - stream->read_bytes(o.payload, o.size); - } - - if (!stream->require(4)) { - ret = ERROR_RTMP_AGGREGATE; - srs_error("invalid aggregate message previous tag size. ret=%d", ret); - return ret; - } - stream->read_4bytes(); - - // process parsed message - SrsCommonMessage* parsed_msg = new SrsCommonMessage(); - parsed_msg->header = o.header; - parsed_msg->payload = o.payload; - parsed_msg->size = o.size; - o.payload = NULL; - context->msgs.push_back(parsed_msg); + if (data_size > 0) { + o.size = data_size; + o.payload = new char[o.size]; + stream->read_bytes(o.payload, o.size); } - return ret; + if (!stream->require(4)) { + ret = ERROR_RTMP_AGGREGATE; + srs_error("invalid aggregate message previous tag size. ret=%d", ret); + return ret; + } + stream->read_4bytes(); + + // process parsed message + SrsCommonMessage* parsed_msg = new SrsCommonMessage(); + parsed_msg->header = o.header; + parsed_msg->payload = o.payload; + parsed_msg->size = o.size; + o.payload = NULL; + context->msgs.push_back(parsed_msg); } - int srs_rtmp_go_packet(Context* context, SrsCommonMessage* msg, - char* type, uint32_t* timestamp, char** data, int* size, - bool* got_msg - ) { - int ret = ERROR_SUCCESS; + return ret; +} + +int srs_rtmp_go_packet(Context* context, SrsCommonMessage* msg, + char* type, uint32_t* timestamp, char** data, int* size, + bool* got_msg +) { + int ret = ERROR_SUCCESS; + + // generally we got a message. + *got_msg = true; + + if (msg->header.is_audio()) { + *type = SRS_RTMP_TYPE_AUDIO; + *timestamp = (uint32_t)msg->header.timestamp; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_video()) { + *type = SRS_RTMP_TYPE_VIDEO; + *timestamp = (uint32_t)msg->header.timestamp; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { + *type = SRS_RTMP_TYPE_SCRIPT; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } else if (msg->header.is_aggregate()) { + if ((ret = srs_rtmp_on_aggregate(context, msg)) != ERROR_SUCCESS) { + return ret; + } + *got_msg = false; + } else { + *type = msg->header.message_type; + *data = (char*)msg->payload; + *size = (int)msg->size; + // detach bytes from packet. + msg->payload = NULL; + } + + return ret; +} + +int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size) +{ + *type = 0; + *timestamp = 0; + *data = NULL; + *size = 0; + + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + for (;;) { + SrsCommonMessage* msg = NULL; - // generally we got a message. - *got_msg = true; - - if (msg->header.is_audio()) { - *type = SRS_RTMP_TYPE_AUDIO; - *timestamp = (uint32_t)msg->header.timestamp; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_video()) { - *type = SRS_RTMP_TYPE_VIDEO; - *timestamp = (uint32_t)msg->header.timestamp; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { - *type = SRS_RTMP_TYPE_SCRIPT; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; - } else if (msg->header.is_aggregate()) { - if ((ret = srs_rtmp_on_aggregate(context, msg)) != ERROR_SUCCESS) { - return ret; - } - *got_msg = false; - } else { - *type = msg->header.message_type; - *data = (char*)msg->payload; - *size = (int)msg->size; - // detach bytes from packet. - msg->payload = NULL; + // read from cache first. + if (!context->msgs.empty()) { + std::vector::iterator it = context->msgs.begin(); + msg = *it; + context->msgs.erase(it); } - return ret; - } - - int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size) - { - *type = 0; - *timestamp = 0; - *data = NULL; - *size = 0; - - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - for (;;) { - SrsCommonMessage* msg = NULL; - - // read from cache first. - if (!context->msgs.empty()) { - std::vector::iterator it = context->msgs.begin(); - msg = *it; - context->msgs.erase(it); - } - - // read from protocol sdk. - if (!msg && (err = context->rtmp->recv_message(&msg)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // no msg, try again. - if (!msg) { - continue; - } - - SrsAutoFree(SrsCommonMessage, msg); - - // process the got packet, if nothing, try again. - bool got_msg; - if ((ret = srs_rtmp_go_packet(context, msg, type, timestamp, data, size, &got_msg)) != ERROR_SUCCESS) { - return ret; - } - - // got expected message. - if (got_msg) { - break; - } - } - - return ret; - } - - int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsSharedPtrMessage* msg = NULL; - - if ((err = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != srs_success) { + // read from protocol sdk. + if (!msg && (err = context->rtmp->recv_message(&msg)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - srs_assert(msg); + // no msg, try again. + if (!msg) { + continue; + } - // send out encoded msg. - if ((err = context->rtmp->send_and_free_message(msg, context->stream_id)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); + SrsAutoFree(SrsCommonMessage, msg); + + // process the got packet, if nothing, try again. + bool got_msg; + if ((ret = srs_rtmp_go_packet(context, msg, type, timestamp, data, size, &got_msg)) != ERROR_SUCCESS) { return ret; } + // got expected message. + if (got_msg) { + break; + } + } + + return ret; +} + +int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsSharedPtrMessage* msg = NULL; + + if ((err = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - void srs_rtmp_free_packet(char* data) - { - srs_freepa(data); + srs_assert(msg); + + // send out encoded msg. + if ((err = context->rtmp->send_and_free_message(msg, context->stream_id)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; } - srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size) - { - srs_error_t err = srs_success; - - if (type != SRS_RTMP_TYPE_SCRIPT) { - return false; - } - - SrsBuffer stream(data, size); - - std::string name; - if ((err = srs_amf0_read_string(&stream, name)) != srs_success) { - srs_freep(err); - return false; - } - - if (name == SRS_CONSTS_RTMP_ON_METADATA) { - return true; - } - - if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { - return true; - } - + return ret; +} + +void srs_rtmp_free_packet(char* data) +{ + srs_freepa(data); +} + +srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size) +{ + srs_error_t err = srs_success; + + if (type != SRS_RTMP_TYPE_SCRIPT) { return false; } - /** - * directly write a audio frame. - */ - int srs_write_audio_raw_frame(Context* context, char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t timestamp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + SrsBuffer stream(data, size); + + std::string name; + if ((err = srs_amf0_read_string(&stream, name)) != srs_success) { + srs_freep(err); + return false; + } + + if (name == SRS_CONSTS_RTMP_ON_METADATA) { + return true; + } + + if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { + return true; + } + + return false; +} + +/** + * directly write a audio frame. + */ +int srs_write_audio_raw_frame(Context* context, char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t timestamp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + char* data = NULL; + int size = 0; + if ((err = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); +} + +/** + * write aac frame in adts. + */ +int srs_write_aac_adts_frame(Context* context, SrsRawAacStreamCodec* codec, char* frame, int frame_size, uint32_t timestamp) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // send out aac sequence header if not sent. + if (context->aac_specific_config.empty()) { + std::string sh; + if ((err = context->aac_raw.mux_sequence_header(codec, sh)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + context->aac_specific_config = sh; - char* data = NULL; - int size = 0; - if ((err = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != srs_success) { + codec->aac_packet_type = 0; + + if ((ret = srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) { + return ret; + } + } + + codec->aac_packet_type = 1; + return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp); +} + +/** + * write aac frames in adts. + */ +int srs_write_aac_adts_frames(Context* context, char sound_format, char sound_rate, + char sound_size, char sound_type, char* frames, int frames_size, uint32_t timestamp +) { + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + SrsBuffer* stream = new SrsBuffer(frames, frames_size); + SrsAutoFree(SrsBuffer, stream); + + while (!stream->empty()) { + char* frame = NULL; + int frame_size = 0; + SrsRawAacStreamCodec codec; + if ((err = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size); + // override by user specified. + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; + + if ((ret = srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) { + return ret; + } } - /** - * write aac frame in adts. - */ - int srs_write_aac_adts_frame(Context* context, SrsRawAacStreamCodec* codec, char* frame, int frame_size, uint32_t timestamp) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // send out aac sequence header if not sent. - if (context->aac_specific_config.empty()) { - std::string sh; - if ((err = context->aac_raw.mux_sequence_header(codec, sh)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - context->aac_specific_config = sh; - - codec->aac_packet_type = 0; - - if ((ret = srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) { - return ret; - } + return ret; +} + +/** + * write audio raw frame to SRS. + */ +int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, + char sound_size, char sound_type, char* frame, int frame_size, uint32_t timestamp +) { + int ret = ERROR_SUCCESS; + + Context* context = (Context*)rtmp; + srs_assert(context); + + if (sound_format == SrsAudioCodecIdAAC) { + // for aac, the frame must be ADTS format. + if (!srs_aac_is_adts(frame, frame_size)) { + return ERROR_AAC_REQUIRED_ADTS; } - codec->aac_packet_type = 1; - return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp); + // for aac, demux the ADTS to RTMP format. + return srs_write_aac_adts_frames(context, sound_format, sound_rate, sound_size, sound_type, frame, frame_size, timestamp); + } else { + // use codec info for aac. + SrsRawAacStreamCodec codec; + codec.sound_format = sound_format; + codec.sound_rate = sound_rate; + codec.sound_size = sound_size; + codec.sound_type = sound_type; + codec.aac_packet_type = 0; + + // for other data, directly write frame. + return srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp); } - /** - * write aac frames in adts. - */ - int srs_write_aac_adts_frames(Context* context, char sound_format, char sound_rate, - char sound_size, char sound_type, char* frames, int frames_size, uint32_t timestamp - ) { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - SrsBuffer* stream = new SrsBuffer(frames, frames_size); - SrsAutoFree(SrsBuffer, stream); - - while (!stream->empty()) { - char* frame = NULL; - int frame_size = 0; - SrsRawAacStreamCodec codec; - if ((err = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // override by user specified. - codec.sound_format = sound_format; - codec.sound_rate = sound_rate; - codec.sound_size = sound_size; - codec.sound_type = sound_type; - - if ((ret = srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) { - return ret; - } - } - - return ret; - } + return ret; +} + +/** + * whether aac raw data is in adts format, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + */ +srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) +{ + SrsBuffer stream(aac_raw_data, ac_raw_size); + return srs_aac_startswith_adts(&stream); +} + +/** + * parse the adts header to get the frame size. + */ +int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) +{ + int size = -1; - /** - * write audio raw frame to SRS. - */ - int srs_audio_write_raw_frame(srs_rtmp_t rtmp, char sound_format, char sound_rate, - char sound_size, char sound_type, char* frame, int frame_size, uint32_t timestamp - ) { - int ret = ERROR_SUCCESS; - - Context* context = (Context*)rtmp; - srs_assert(context); - - if (sound_format == SrsAudioCodecIdAAC) { - // for aac, the frame must be ADTS format. - if (!srs_aac_is_adts(frame, frame_size)) { - return ERROR_AAC_REQUIRED_ADTS; - } - - // for aac, demux the ADTS to RTMP format. - return srs_write_aac_adts_frames(context, sound_format, sound_rate, sound_size, sound_type, frame, frame_size, timestamp); - } else { - // use codec info for aac. - SrsRawAacStreamCodec codec; - codec.sound_format = sound_format; - codec.sound_rate = sound_rate; - codec.sound_size = sound_size; - codec.sound_type = sound_type; - codec.aac_packet_type = 0; - - // for other data, directly write frame. - return srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp); - } - - return ret; - } - - /** - * whether aac raw data is in adts format, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - */ - srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size) - { - SrsBuffer stream(aac_raw_data, ac_raw_size); - return srs_aac_startswith_adts(&stream); - } - - /** - * parse the adts header to get the frame size. - */ - int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) - { - int size = -1; - - if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { - return size; - } - - // adts always 7bytes. - if (ac_raw_size <= 7) { - return size; - } - - // last 2bits - int16_t ch3 = aac_raw_data[3]; - // whole 8bits - int16_t ch4 = aac_raw_data[4]; - // first 3bits - int16_t ch5 = aac_raw_data[5]; - - size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); - + if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) { return size; } - /** - * write h264 IPB-frame. - */ - int srs_write_h264_ipb_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // when sps or pps not sent, ignore the packet. - // @see https://github.com/ossrs/srs/issues/203 - if (!context->h264_sps_pps_sent) { - return ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - // 5bits, 7.3.1 NAL unit syntax, - // ISO_IEC_14496-10-AVC-2003.pdf, page 44. - // 5: I Frame, 1: P/B Frame - // @remark we already group sps/pps to sequence header frame; - // for I/P NALU, we send them in isolate frame, each NALU in a frame; - // for other NALU, for example, AUD/SEI, we just ignore them, because - // AUD used in annexb to split frame, while SEI generally we can ignore it. - // TODO: maybe we should group all NALUs split by AUD to a frame. - SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); - if (nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR) { - return ret; - } - - // for IDR frame, the frame is keyframe. - SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; - if (nut == SrsAvcNaluTypeIDR) { - frame_type = SrsVideoAvcFrameTypeKeyFrame; - } - - std::string ibp; - if ((err = context->avc_raw.mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; - char* flv = NULL; - int nb_flv = 0; - if ((err = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); + // adts always 7bytes. + if (ac_raw_size <= 7) { + return size; } - /** - * write the h264 sps/pps in context over RTMP. - */ - int srs_write_h264_sps_pps(Context* context, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - // send when sps or pps changed. - if (!context->h264_sps_changed && !context->h264_pps_changed) { - return ret; - } - - // h264 raw to h264 packet. - std::string sh; - if ((err = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // h264 packet to flv packet. - int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; - int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; - char* flv = NULL; - int nb_flv = 0; - if ((err = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // reset sps and pps. - context->h264_sps_changed = false; - context->h264_pps_changed = false; - context->h264_sps_pps_sent = true; - - // the timestamp in rtmp message header is dts. - uint32_t timestamp = dts; - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); + // last 2bits + int16_t ch3 = aac_raw_data[3]; + // whole 8bits + int16_t ch4 = aac_raw_data[4]; + // first 3bits + int16_t ch5 = aac_raw_data[5]; + + size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007); + + return size; +} + +/** + * write h264 IPB-frame. + */ +int srs_write_h264_ipb_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // when sps or pps not sent, ignore the packet. + // @see https://github.com/ossrs/srs/issues/203 + if (!context->h264_sps_pps_sent) { + return ERROR_H264_DROP_BEFORE_SPS_PPS; } - /** - * write h264 raw frame, maybe sps/pps/IPB-frame. - */ - int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 5: I Frame, 1: P/B Frame + // @remark we already group sps/pps to sequence header frame; + // for I/P NALU, we send them in isolate frame, each NALU in a frame; + // for other NALU, for example, AUD/SEI, we just ignore them, because + // AUD used in annexb to split frame, while SEI generally we can ignore it. + // TODO: maybe we should group all NALUs split by AUD to a frame. + SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); + if (nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR) { + return ret; + } + + // for IDR frame, the frame is keyframe. + SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; + if (nut == SrsAvcNaluTypeIDR) { + frame_type = SrsVideoAvcFrameTypeKeyFrame; + } + + std::string ibp; + if ((err = context->avc_raw.mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; + char* flv = NULL; + int nb_flv = 0; + if ((err = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); +} + +/** + * write the h264 sps/pps in context over RTMP. + */ +int srs_write_h264_sps_pps(Context* context, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // send when sps or pps changed. + if (!context->h264_sps_changed && !context->h264_pps_changed) { + return ret; + } + + // h264 raw to h264 packet. + std::string sh; + if ((err = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + // h264 packet to flv packet. + int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; + int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; + char* flv = NULL; + int nb_flv = 0; + if ((err = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + // reset sps and pps. + context->h264_sps_changed = false; + context->h264_pps_changed = false; + context->h264_sps_pps_sent = true; + + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); +} + +/** + * write h264 raw frame, maybe sps/pps/IPB-frame. + */ +int srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + // empty frame. + if (frame_size <= 0) { + return ret; + } + + // for sps + if (context->avc_raw.is_sps(frame, frame_size)) { + std::string sps; + if ((err = context->avc_raw.sps_demux(frame, frame_size, sps)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } - // empty frame. + if (context->h264_sps == sps) { + return ERROR_H264_DUPLICATED_SPS; + } + context->h264_sps_changed = true; + context->h264_sps = sps; + + return ret; + } + + // for pps + if (context->avc_raw.is_pps(frame, frame_size)) { + std::string pps; + if ((err = context->avc_raw.pps_demux(frame, frame_size, pps)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + if (context->h264_pps == pps) { + return ERROR_H264_DUPLICATED_PPS; + } + context->h264_pps_changed = true; + context->h264_pps = pps; + + return ret; + } + + // ignore others. + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD + SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); + if (nut != SrsAvcNaluTypeSPS && nut != SrsAvcNaluTypePPS + && nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR + && nut != SrsAvcNaluTypeAccessUnitDelimiter + ) { + return ret; + } + + // send pps+sps before ipb frames when sps/pps changed. + if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) { + return ret; + } + + // ibp frame. + return srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); +} + +/** + * write h264 multiple frames, in annexb format. + */ +int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + srs_assert(frames != NULL); + srs_assert(frames_size > 0); + + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + + SrsBuffer* stream = new SrsBuffer(frames, frames_size); + SrsAutoFree(SrsBuffer, stream); + + // use the last error + // @see https://github.com/ossrs/srs/issues/203 + // @see https://github.com/ossrs/srs/issues/204 + int error_code_return = ret; + + // send each frame. + while (!stream->empty()) { + char* frame = NULL; + int frame_size = 0; + if ((err = context->avc_raw.annexb_demux(stream, &frame, &frame_size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + // ignore invalid frame, + // atleast 1bytes for SPS to decode the type if (frame_size <= 0) { - return ret; + continue; } - // for sps - if (context->avc_raw.is_sps(frame, frame_size)) { - std::string sps; - if ((err = context->avc_raw.sps_demux(frame, frame_size, sps)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } + // it may be return error, but we must process all packets. + if ((ret = srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) { + error_code_return = ret; - if (context->h264_sps == sps) { - return ERROR_H264_DUPLICATED_SPS; - } - context->h264_sps_changed = true; - context->h264_sps = sps; - - return ret; - } - - // for pps - if (context->avc_raw.is_pps(frame, frame_size)) { - std::string pps; - if ((err = context->avc_raw.pps_demux(frame, frame_size, pps)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (context->h264_pps == pps) { - return ERROR_H264_DUPLICATED_PPS; - } - context->h264_pps_changed = true; - context->h264_pps = pps; - - return ret; - } - - // ignore others. - // 5bits, 7.3.1 NAL unit syntax, - // ISO_IEC_14496-10-AVC-2003.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD - SrsAvcNaluType nut = (SrsAvcNaluType)(frame[0] & 0x1f); - if (nut != SrsAvcNaluTypeSPS && nut != SrsAvcNaluTypePPS - && nut != SrsAvcNaluTypeIDR && nut != SrsAvcNaluTypeNonIDR - && nut != SrsAvcNaluTypeAccessUnitDelimiter - ) { - return ret; - } - - // send pps+sps before ipb frames when sps/pps changed. - if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) { - return ret; - } - - // ibp frame. - return srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); - } - - /** - * write h264 multiple frames, in annexb format. - */ - int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - srs_assert(frames != NULL); - srs_assert(frames_size > 0); - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - - SrsBuffer* stream = new SrsBuffer(frames, frames_size); - SrsAutoFree(SrsBuffer, stream); - - // use the last error - // @see https://github.com/ossrs/srs/issues/203 - // @see https://github.com/ossrs/srs/issues/204 - int error_code_return = ret; - - // send each frame. - while (!stream->empty()) { - char* frame = NULL; - int frame_size = 0; - if ((err = context->avc_raw.annexb_demux(stream, &frame, &frame_size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - // ignore invalid frame, - // atleast 1bytes for SPS to decode the type - if (frame_size <= 0) { + // ignore known error, process all packets. + if (srs_h264_is_dvbsp_error(ret) + || srs_h264_is_duplicated_sps_error(ret) + || srs_h264_is_duplicated_pps_error(ret) + ) { continue; } - // it may be return error, but we must process all packets. - if ((ret = srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) { - error_code_return = ret; - - // ignore known error, process all packets. - if (srs_h264_is_dvbsp_error(ret) - || srs_h264_is_duplicated_sps_error(ret) - || srs_h264_is_duplicated_pps_error(ret) - ) { - continue; - } - - return ret; - } - } - - return error_code_return; - } - - srs_bool srs_h264_is_dvbsp_error(int error_code) - { - return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - srs_bool srs_h264_is_duplicated_sps_error(int error_code) - { - return error_code == ERROR_H264_DUPLICATED_SPS; - } - - srs_bool srs_h264_is_duplicated_pps_error(int error_code) - { - return error_code == ERROR_H264_DUPLICATED_PPS; - } - - srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) - { - SrsBuffer stream(h264_raw_data, h264_raw_size); - return srs_avc_startswith_annexb(&stream, pnb_start_code); - } - - struct Mp4Context - { - SrsFileReader reader; - SrsMp4Decoder dec; - }; - - srs_mp4_t srs_mp4_open_read(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* mp4 = new Mp4Context(); - - if ((err = mp4->reader.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open MP4 file failed, ret=%d", ret); - - srs_freep(mp4); - return NULL; - } - - return mp4; - } - - void srs_mp4_close(srs_mp4_t mp4) - { - Mp4Context* context = (Mp4Context*)mp4; - srs_freep(context); - } - - int srs_mp4_init_demuxer(srs_mp4_t mp4) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* context = (Mp4Context*)mp4; - - if ((err = context->dec.initialize(&context->reader)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); return ret; } + } + + return error_code_return; +} + +srs_bool srs_h264_is_dvbsp_error(int error_code) +{ + return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS; +} + +srs_bool srs_h264_is_duplicated_sps_error(int error_code) +{ + return error_code == ERROR_H264_DUPLICATED_SPS; +} + +srs_bool srs_h264_is_duplicated_pps_error(int error_code) +{ + return error_code == ERROR_H264_DUPLICATED_PPS; +} + +srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code) +{ + SrsBuffer stream(h264_raw_data, h264_raw_size); + return srs_avc_startswith_annexb(&stream, pnb_start_code); +} + +struct Mp4Context +{ + SrsFileReader reader; + SrsMp4Decoder dec; +}; + +srs_mp4_t srs_mp4_open_read(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* mp4 = new Mp4Context(); + + if ((err = mp4->reader.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open MP4 file failed, ret=%d", ret); + srs_freep(mp4); + return NULL; + } + + return mp4; +} + +void srs_mp4_close(srs_mp4_t mp4) +{ + Mp4Context* context = (Mp4Context*)mp4; + srs_freep(context); +} + +int srs_mp4_init_demuxer(srs_mp4_t mp4) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* context = (Mp4Context*)mp4; + + if ((err = context->dec.initialize(&context->reader)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* s) - { - s->sample = NULL; - - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - Mp4Context* context = (Mp4Context*)mp4; - SrsMp4Decoder* dec = &context->dec; - - SrsMp4HandlerType ht = SrsMp4HandlerTypeForbidden; - if ((err = dec->read_sample(&ht, &s->frame_type, &s->frame_trait, &s->dts, &s->pts, &s->sample, &s->nb_sample)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - if (ht == SrsMp4HandlerTypeForbidden) { - return ERROR_MP4_ILLEGAL_HANDLER; - } - - if (ht == SrsMp4HandlerTypeSOUN) { - s->codec = (uint16_t)dec->acodec; - s->sample_rate = dec->sample_rate; - s->channels = dec->channels; - s->sound_bits = dec->sound_bits; - } else { - s->codec = (uint16_t)dec->vcodec; - } - s->handler_type = (uint32_t)ht; - + return ret; +} + +int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* s) +{ + s->sample = NULL; + + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + Mp4Context* context = (Mp4Context*)mp4; + SrsMp4Decoder* dec = &context->dec; + + SrsMp4HandlerType ht = SrsMp4HandlerTypeForbidden; + if ((err = dec->read_sample(&ht, &s->frame_type, &s->frame_trait, &s->dts, &s->pts, &s->sample, &s->nb_sample)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - void srs_mp4_free_sample(srs_mp4_sample_t* s) - { - srs_freepa(s->sample); + if (ht == SrsMp4HandlerTypeForbidden) { + return ERROR_MP4_ILLEGAL_HANDLER; } - int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* s) - { - if (s->handler_type == SrsMp4HandlerTypeSOUN) { - if (s->codec == (uint16_t)SrsAudioCodecIdAAC) { - return s->nb_sample + 2; - } - return s->nb_sample + 1; - } - - if (s->codec == (uint16_t)SrsVideoCodecIdAVC) { - return s->nb_sample + 5; + if (ht == SrsMp4HandlerTypeSOUN) { + s->codec = (uint16_t)dec->acodec; + s->sample_rate = dec->sample_rate; + s->channels = dec->channels; + s->sound_bits = dec->sound_bits; + } else { + s->codec = (uint16_t)dec->vcodec; + } + s->handler_type = (uint32_t)ht; + + return ret; +} + +void srs_mp4_free_sample(srs_mp4_sample_t* s) +{ + srs_freepa(s->sample); +} + +int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* s) +{ + if (s->handler_type == SrsMp4HandlerTypeSOUN) { + if (s->codec == (uint16_t)SrsAudioCodecIdAAC) { + return s->nb_sample + 2; } return s->nb_sample + 1; } - int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* s, char* type, uint32_t* time, char* data, int32_t size) - { - int ret = ERROR_SUCCESS; + if (s->codec == (uint16_t)SrsVideoCodecIdAVC) { + return s->nb_sample + 5; + } + return s->nb_sample + 1; +} + +int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* s, char* type, uint32_t* time, char* data, int32_t size) +{ + int ret = ERROR_SUCCESS; + + *time = s->dts; + + SrsBuffer p(data, size); + if (s->handler_type == SrsMp4HandlerTypeSOUN) { + *type = SRS_RTMP_TYPE_AUDIO; - *time = s->dts; - - SrsBuffer p(data, size); - if (s->handler_type == SrsMp4HandlerTypeSOUN) { - *type = SRS_RTMP_TYPE_AUDIO; - - // E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3 - p.write_1bytes(uint8_t(s->codec << 4) | uint8_t(s->sample_rate << 2) | uint8_t(s->sound_bits << 1) | s->channels); - if (s->codec == SrsAudioCodecIdAAC) { - p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsAudioAacFrameTraitSequenceHeader? 0:1)); - } - - p.write_bytes((char*)s->sample, s->nb_sample); - return ret; + // E.4.2.1 AUDIODATA, flv_v10_1.pdf, page 3 + p.write_1bytes(uint8_t(s->codec << 4) | uint8_t(s->sample_rate << 2) | uint8_t(s->sound_bits << 1) | s->channels); + if (s->codec == SrsAudioCodecIdAAC) { + p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsAudioAacFrameTraitSequenceHeader? 0:1)); } - // E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5 - p.write_1bytes(uint8_t(s->frame_type<<4) | uint8_t(s->codec)); - if (s->codec == SrsVideoCodecIdAVC) { - *type = SRS_RTMP_TYPE_VIDEO; - - p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsVideoAvcFrameTraitSequenceHeader? 0:1)); - // cts = pts - dts, where dts = flvheader->timestamp. - uint32_t cts = s->pts - s->dts; - p.write_3bytes(cts); - } p.write_bytes((char*)s->sample, s->nb_sample); - return ret; } - srs_bool srs_mp4_is_eof(int error_code) - { - return error_code == ERROR_SYSTEM_FILE_EOF; + // E.4.3.1 VIDEODATA, flv_v10_1.pdf, page 5 + p.write_1bytes(uint8_t(s->frame_type<<4) | uint8_t(s->codec)); + if (s->codec == SrsVideoCodecIdAVC) { + *type = SRS_RTMP_TYPE_VIDEO; + + p.write_1bytes(uint8_t(s->frame_trait == (uint16_t)SrsVideoAvcFrameTraitSequenceHeader? 0:1)); + // cts = pts - dts, where dts = flvheader->timestamp. + uint32_t cts = s->pts - s->dts; + p.write_3bytes(cts); + } + p.write_bytes((char*)s->sample, s->nb_sample); + + return ret; +} + +srs_bool srs_mp4_is_eof(int error_code) +{ + return error_code == ERROR_SYSTEM_FILE_EOF; +} + +struct FlvContext +{ + SrsFileReader reader; + SrsFileWriter writer; + SrsFlvTransmuxer enc; + SrsFlvDecoder dec; +}; + +srs_flv_t srs_flv_open_read(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* flv = new FlvContext(); + + if ((err = flv->reader.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open FLV file failed, ret=%d", ret); + + srs_freep(flv); + return NULL; } - struct FlvContext - { - SrsFileReader reader; - SrsFileWriter writer; - SrsFlvTransmuxer enc; - SrsFlvDecoder dec; - }; - - srs_flv_t srs_flv_open_read(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + if ((err = flv->dec.initialize(&flv->reader)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Initialize FLV demuxer failed, ret=%d", ret); - FlvContext* flv = new FlvContext(); - - if ((err = flv->reader.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open FLV file failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - if ((err = flv->dec.initialize(&flv->reader)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Initialize FLV demuxer failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - return flv; + srs_freep(flv); + return NULL; } - srs_flv_t srs_flv_open_write(const char* file) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; + return flv; +} + +srs_flv_t srs_flv_open_write(const char* file) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* flv = new FlvContext(); + + if ((err = flv->writer.open(file)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Open FLV file failed, ret=%d", ret); - FlvContext* flv = new FlvContext(); - - if ((err = flv->writer.open(file)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Open FLV file failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - if ((err = flv->enc.initialize(&flv->writer)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - srs_human_error("Initilize FLV muxer failed, ret=%d", ret); - - srs_freep(flv); - return NULL; - } - - return flv; + srs_freep(flv); + return NULL; } - void srs_flv_close(srs_flv_t flv) - { - FlvContext* context = (FlvContext*)flv; - srs_freep(context); + if ((err = flv->enc.initialize(&flv->writer)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + srs_human_error("Initilize FLV muxer failed, ret=%d", ret); + + srs_freep(flv); + return NULL; } - int srs_flv_read_header(srs_flv_t flv, char header[9]) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->dec.read_header(header)) != srs_success) { + return flv; +} + +void srs_flv_close(srs_flv_t flv) +{ + FlvContext* context = (FlvContext*)flv; + srs_freep(context); +} + +int srs_flv_read_header(srs_flv_t flv, char header[9]) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_header(header)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + char ts[4]; // tag size + if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return ret; +} + +int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_tag_header(ptype, pdata_size, ptime)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return ret; +} + +int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->reader.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->dec.read_tag_data(data, size)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + char ts[4]; // tag size + if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return ret; +} + +int srs_flv_write_header(srs_flv_t flv, char header[9]) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if ((err = context->enc.write_header(header)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); + return ret; + } + + return ret; +} + +int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + FlvContext* context = (FlvContext*)flv; + + if (!context->writer.is_open()) { + return ERROR_SYSTEM_IO_INVALID; + } + + if (type == SRS_RTMP_TYPE_AUDIO) { + if ((err = context->enc.write_audio(time, data, size)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - - char ts[4]; // tag size - if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { + } else if (type == SRS_RTMP_TYPE_VIDEO) { + if ((err = context->enc.write_video(time, data, size)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - - return ret; - } - - int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->dec.read_tag_header(ptype, pdata_size, ptime)) != srs_success) { + } else { + if ((err = context->enc.write_metadata(type, data, size)) != srs_success) { ret = srs_error_code(err); srs_freep(err); return ret; } - - return ret; } - int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->reader.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->dec.read_tag_data(data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - char ts[4]; // tag size - if ((err = context->dec.read_previous_tag_size(ts)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; - } + return ret; +} + +int srs_flv_size_tag(int data_size) +{ + return SrsFlvTransmuxer::size_tag(data_size); +} + +int64_t srs_flv_tellg(srs_flv_t flv) +{ + FlvContext* context = (FlvContext*)flv; + return context->reader.tellg(); +} + +void srs_flv_lseek(srs_flv_t flv, int64_t offset) +{ + FlvContext* context = (FlvContext*)flv; + int64_t r0 = context->reader.seek2(offset); + srs_assert(r0 != -1); +} + +srs_bool srs_flv_is_eof(int error_code) +{ + return error_code == ERROR_SYSTEM_FILE_EOF; +} + +srs_bool srs_flv_is_sequence_header(char* data, int32_t size) +{ + return SrsFlvVideo::sh(data, (int)size); +} + +srs_bool srs_flv_is_keyframe(char* data, int32_t size) +{ + return SrsFlvVideo::keyframe(data, (int)size); +} + +srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) +{ + srs_error_t err = srs_success; - int srs_flv_write_header(srs_flv_t flv, char header[9]) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if ((err = context->enc.write_header(header)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - - return ret; - } + srs_amf0_t amf0 = NULL; - int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - FlvContext* context = (FlvContext*)flv; - - if (!context->writer.is_open()) { - return ERROR_SYSTEM_IO_INVALID; - } - - if (type == SRS_RTMP_TYPE_AUDIO) { - if ((err = context->enc.write_audio(time, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } else if (type == SRS_RTMP_TYPE_VIDEO) { - if ((err = context->enc.write_video(time, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } else { - if ((err = context->enc.write_metadata(type, data, size)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - } - - return ret; - } + SrsBuffer stream(data, size); - int srs_flv_size_tag(int data_size) - { - return SrsFlvTransmuxer::size_tag(data_size); - } - - int64_t srs_flv_tellg(srs_flv_t flv) - { - FlvContext* context = (FlvContext*)flv; - return context->reader.tellg(); - } - - void srs_flv_lseek(srs_flv_t flv, int64_t offset) - { - FlvContext* context = (FlvContext*)flv; - int64_t r0 = context->reader.seek2(offset); - srs_assert(r0 != -1); - } - - srs_bool srs_flv_is_eof(int error_code) - { - return error_code == ERROR_SYSTEM_FILE_EOF; - } - - srs_bool srs_flv_is_sequence_header(char* data, int32_t size) - { - return SrsFlvVideo::sh(data, (int)size); - } - - srs_bool srs_flv_is_keyframe(char* data, int32_t size) - { - return SrsFlvVideo::keyframe(data, (int)size); - } - - srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed) - { - srs_error_t err = srs_success; - - srs_amf0_t amf0 = NULL; - - SrsBuffer stream(data, size); - - SrsAmf0Any* any = NULL; - if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) { - srs_freep(err); - return amf0; - } - - stream.skip(-1 * stream.pos()); - if ((err = any->read(&stream)) != srs_success) { - srs_freep(err); - srs_freep(any); - return amf0; - } - - if (nparsed) { - *nparsed = stream.pos(); - } - amf0 = (srs_amf0_t)any; - + SrsAmf0Any* any = NULL; + if ((err = SrsAmf0Any::discovery(&stream, &any)) != srs_success) { + srs_freep(err); return amf0; } - srs_amf0_t srs_amf0_create_string(const char* value) - { - return SrsAmf0Any::str(value); - } - - srs_amf0_t srs_amf0_create_number(srs_amf0_number value) - { - return SrsAmf0Any::number(value); - } - - srs_amf0_t srs_amf0_create_ecma_array() - { - return SrsAmf0Any::ecma_array(); - } - - srs_amf0_t srs_amf0_create_strict_array() - { - return SrsAmf0Any::strict_array(); - } - - srs_amf0_t srs_amf0_create_object() - { - return SrsAmf0Any::object(); - } - - srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr) - { - srs_assert(srs_amf0_is_ecma_array(ecma_arr)); - - SrsAmf0EcmaArray* arr = (SrsAmf0EcmaArray*)ecma_arr; - SrsAmf0Object* obj = SrsAmf0Any::object(); - - for (int i = 0; i < arr->count(); i++) { - std::string key = arr->key_at(i); - SrsAmf0Any* value = arr->value_at(i); - obj->set(key, value->copy()); - } - - return obj; - } - - void srs_amf0_free(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; + stream.skip(-1 * stream.pos()); + if ((err = any->read(&stream)) != srs_success) { + srs_freep(err); srs_freep(any); + return amf0; } - int srs_amf0_size(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->total_size(); + if (nparsed) { + *nparsed = stream.pos(); + } + amf0 = (srs_amf0_t)any; + + return amf0; +} + +srs_amf0_t srs_amf0_create_string(const char* value) +{ + return SrsAmf0Any::str(value); +} + +srs_amf0_t srs_amf0_create_number(srs_amf0_number value) +{ + return SrsAmf0Any::number(value); +} + +srs_amf0_t srs_amf0_create_ecma_array() +{ + return SrsAmf0Any::ecma_array(); +} + +srs_amf0_t srs_amf0_create_strict_array() +{ + return SrsAmf0Any::strict_array(); +} + +srs_amf0_t srs_amf0_create_object() +{ + return SrsAmf0Any::object(); +} + +srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr) +{ + srs_assert(srs_amf0_is_ecma_array(ecma_arr)); + + SrsAmf0EcmaArray* arr = (SrsAmf0EcmaArray*)ecma_arr; + SrsAmf0Object* obj = SrsAmf0Any::object(); + + for (int i = 0; i < arr->count(); i++) { + std::string key = arr->key_at(i); + SrsAmf0Any* value = arr->value_at(i); + obj->set(key, value->copy()); } - int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) - { - int ret = ERROR_SUCCESS; - srs_error_t err = srs_success; - - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - - SrsBuffer stream(data, size); - - if ((err = any->write(&stream)) != srs_success) { - ret = srs_error_code(err); - srs_freep(err); - return ret; - } - + return obj; +} + +void srs_amf0_free(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_freep(any); +} + +int srs_amf0_size(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->total_size(); +} + +int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size) +{ + int ret = ERROR_SUCCESS; + srs_error_t err = srs_success; + + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + + SrsBuffer stream(data, size); + + if ((err = any->write(&stream)) != srs_success) { + ret = srs_error_code(err); + srs_freep(err); return ret; } - srs_bool srs_amf0_is_string(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_string(); - } + return ret; +} + +srs_bool srs_amf0_is_string(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_string(); +} + +srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_boolean(); +} + +srs_bool srs_amf0_is_number(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_number(); +} + +srs_bool srs_amf0_is_null(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_null(); +} + +srs_bool srs_amf0_is_object(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_object(); +} + +srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_ecma_array(); +} + +srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_strict_array(); +} + +const char* srs_amf0_to_string(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_str_raw(); +} + +srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_boolean(); +} + +srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->to_number(); +} + +void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + any->set_number(value); +} + +int srs_amf0_object_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_boolean(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_boolean(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return obj->count(); +} + +const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_number(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_number(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return obj->key_raw_at(index); +} + +srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_null(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_null(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return (srs_amf0_t)obj->value_at(index); +} + +srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_object(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_object(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + return (srs_amf0_t)obj->get_property(name); +} + +void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_ecma_array(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + any = (SrsAmf0Any*)value; + obj->set(name, any); +} + +void srs_amf0_object_clear(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_object()); - srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->is_strict_array(); - } + SrsAmf0Object* obj = (SrsAmf0Object*)amf0; + obj->clear(); +} + +int srs_amf0_ecma_array_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); - const char* srs_amf0_to_string(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_str_raw(); - } + SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; + return obj->count(); +} + +const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); - srs_bool srs_amf0_to_boolean(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_boolean(); - } + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return obj->key_raw_at(index); +} + +srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); - srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - return any->to_number(); - } + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return (srs_amf0_t)obj->value_at(index); +} + +srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); - void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - any->set_number(value); - } + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return (srs_amf0_t)obj->get_property(name); +} + +void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_ecma_array()); - int srs_amf0_object_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return obj->count(); - } + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + any = (SrsAmf0Any*)value; + obj->set(name, any); +} + +int srs_amf0_strict_array_property_count(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); - const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return obj->key_raw_at(index); - } + SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0; + return obj->count(); +} + +srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); - srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return (srs_amf0_t)obj->value_at(index); - } + SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; + return (srs_amf0_t)obj->at(index); +} + +void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + srs_assert(any->is_strict_array()); - srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - return (srs_amf0_t)obj->get_property(name); + SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; + any = (SrsAmf0Any*)value; + obj->append(any); +} + +int64_t srs_utils_time_ms() +{ + return srs_update_system_time_ms(); +} + +int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + if (!context->rtmp) { + return 0; } - - void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - any = (SrsAmf0Any*)value; - obj->set(name, any); + return context->rtmp->get_send_bytes(); +} + +int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp) +{ + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + if (!context->rtmp) { + return 0; } + return context->rtmp->get_recv_bytes(); +} + +int srs_utils_parse_timestamp( + uint32_t time, char type, char* data, int size, + uint32_t* ppts + ) { + int ret = ERROR_SUCCESS; - void srs_amf0_object_clear(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_object()); - - SrsAmf0Object* obj = (SrsAmf0Object*)amf0; - obj->clear(); - } - - int srs_amf0_ecma_array_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; - return obj->count(); - } - - const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return obj->key_raw_at(index); - } - - srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return (srs_amf0_t)obj->value_at(index); - } - - srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - return (srs_amf0_t)obj->get_property(name); - } - - void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_ecma_array()); - - SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; - any = (SrsAmf0Any*)value; - obj->set(name, any); - } - - int srs_amf0_strict_array_property_count(srs_amf0_t amf0) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0; - return obj->count(); - } - - srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; - return (srs_amf0_t)obj->at(index); - } - - void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) - { - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - srs_assert(any->is_strict_array()); - - SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0; - any = (SrsAmf0Any*)value; - obj->append(any); - } - - int64_t srs_utils_time_ms() - { - return srs_update_system_time_ms(); - } - - int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) - { - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - if (!context->rtmp) { - return 0; - } - return context->rtmp->get_send_bytes(); - } - - int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp) - { - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - if (!context->rtmp) { - return 0; - } - return context->rtmp->get_recv_bytes(); - } - - int srs_utils_parse_timestamp( - uint32_t time, char type, char* data, int size, - uint32_t* ppts - ) { - int ret = ERROR_SUCCESS; - - if (type != SRS_RTMP_TYPE_VIDEO) { - *ppts = time; - return ret; - } - - if (!SrsFlvVideo::h264(data, size)) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - if (SrsFlvVideo::sh(data, size)) { - *ppts = time; - return ret; - } - - // 1bytes, frame type and codec id. - // 1bytes, avc packet type. - // 3bytes, cts, composition time, - // pts = dts + cts, or - // cts = pts - dts. - if (size < 5) { - return ERROR_FLV_INVALID_VIDEO_TAG; - } - - uint32_t cts = 0; - char* p = data + 2; - char* pp = (char*)&cts; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - *ppts = time + cts; - + if (type != SRS_RTMP_TYPE_VIDEO) { + *ppts = time; return ret; } - srs_bool srs_utils_flv_tag_is_ok(char type) - { - return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT; + if (!SrsFlvVideo::h264(data, size)) { + return ERROR_FLV_INVALID_VIDEO_TAG; } - srs_bool srs_utils_flv_tag_is_audio(char type) - { - return type == SRS_RTMP_TYPE_AUDIO; - } - - srs_bool srs_utils_flv_tag_is_video(char type) - { - return type == SRS_RTMP_TYPE_VIDEO; - } - - srs_bool srs_utils_flv_tag_is_av(char type) - { - return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO; - } - - char srs_utils_flv_video_codec_id(char* data, int size) - { - if (size < 1) { - return 0; - } - - char codec_id = data[0]; - codec_id = codec_id & 0x0F; - - return codec_id; - } - - char srs_utils_flv_video_avc_packet_type(char* data, int size) - { - if (size < 2) { - return -1; - } - - if (!SrsFlvVideo::h264(data, size)) { - return -1; - } - - uint8_t avc_packet_type = data[1]; - - if (avc_packet_type > 2) { - return -1; - } - - return avc_packet_type; - } - - char srs_utils_flv_video_frame_type(char* data, int size) - { - if (size < 1) { - return -1; - } - - if (!SrsFlvVideo::h264(data, size)) { - return -1; - } - - uint8_t frame_type = data[0]; - frame_type = (frame_type >> 4) & 0x0f; - if (frame_type < 1 || frame_type > 5) { - return -1; - } - - return frame_type; - } - - char srs_utils_flv_audio_sound_format(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_format = data[0]; - sound_format = (sound_format >> 4) & 0x0f; - if (sound_format > 15 || sound_format == 12 || sound_format == 13) { - return -1; - } - - return sound_format; - } - - char srs_utils_flv_audio_sound_rate(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_rate = data[0]; - sound_rate = (sound_rate >> 2) & 0x03; - - return sound_rate; - } - - char srs_utils_flv_audio_sound_size(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_size = data[0]; - sound_size = (sound_size >> 1) & 0x01; - - return sound_size; - } - - char srs_utils_flv_audio_sound_type(char* data, int size) - { - if (size < 1) { - return -1; - } - - uint8_t sound_type = data[0]; - sound_type = sound_type & 0x01; - - return sound_type; - } - - char srs_utils_flv_audio_aac_packet_type(char* data, int size) - { - if (size < 2) { - return -1; - } - - if (srs_utils_flv_audio_sound_format(data, size) != 10) { - return -1; - } - - uint8_t aac_packet_type = data[1]; - if (aac_packet_type > 1) { - return -1; - } - - return aac_packet_type; - } - - char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) - { - if (!amf0) { - return NULL; - } - - SrsAmf0Any* any = (SrsAmf0Any*)amf0; - - return any->human_print(pdata, psize); - } - - const char* srs_human_flv_tag_type2string(char type) - { - static const char* audio = "Audio"; - static const char* video = "Video"; - static const char* data = "Data"; - static const char* unknown = "Unknown"; - - switch (type) { - case SRS_RTMP_TYPE_AUDIO: return audio; - case SRS_RTMP_TYPE_VIDEO: return video; - case SRS_RTMP_TYPE_SCRIPT: return data; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_video_codec_id2string(char codec_id) - { - static const char* h263 = "H.263"; - static const char* screen = "Screen"; - static const char* vp6 = "VP6"; - static const char* vp6_alpha = "VP6Alpha"; - static const char* screen2 = "Screen2"; - static const char* h264 = "H.264"; - static const char* unknown = "Unknown"; - - switch (codec_id) { - case 2: return h263; - case 3: return screen; - case 4: return vp6; - case 5: return vp6_alpha; - case 6: return screen2; - case 7: return h264; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) - { - static const char* sps_pps = "SH"; - static const char* nalu = "Nalu"; - static const char* sps_pps_end = "SpsPpsEnd"; - static const char* unknown = "Unknown"; - - switch (avc_packet_type) { - case 0: return sps_pps; - case 1: return nalu; - case 2: return sps_pps_end; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_video_frame_type2string(char frame_type) - { - static const char* keyframe = "I"; - static const char* interframe = "P/B"; - static const char* disposable_interframe = "DI"; - static const char* generated_keyframe = "GI"; - static const char* video_infoframe = "VI"; - static const char* unknown = "Unknown"; - - switch (frame_type) { - case 1: return keyframe; - case 2: return interframe; - case 3: return disposable_interframe; - case 4: return generated_keyframe; - case 5: return video_infoframe; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_sound_format2string(char sound_format) - { - static const char* linear_pcm = "LinearPCM"; - static const char* ad_pcm = "ADPCM"; - static const char* mp3 = "MP3"; - static const char* linear_pcm_le = "LinearPCMLe"; - static const char* nellymoser_16khz = "NellymoserKHz16"; - static const char* nellymoser_8khz = "NellymoserKHz8"; - static const char* nellymoser = "Nellymoser"; - static const char* g711_a_pcm = "G711APCM"; - static const char* g711_mu_pcm = "G711MuPCM"; - static const char* reserved = "Reserved"; - static const char* aac = "AAC"; - static const char* speex = "Speex"; - static const char* mp3_8khz = "MP3KHz8"; - static const char* device_specific = "DeviceSpecific"; - static const char* unknown = "Unknown"; - - switch (sound_format) { - case 0: return linear_pcm; - case 1: return ad_pcm; - case 2: return mp3; - case 3: return linear_pcm_le; - case 4: return nellymoser_16khz; - case 5: return nellymoser_8khz; - case 6: return nellymoser; - case 7: return g711_a_pcm; - case 8: return g711_mu_pcm; - case 9: return reserved; - case 10: return aac; - case 11: return speex; - case 14: return mp3_8khz; - case 15: return device_specific; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_sound_rate2string(char sound_rate) - { - static const char* khz_5_5 = "5.5KHz"; - static const char* khz_11 = "11KHz"; - static const char* khz_22 = "22KHz"; - static const char* khz_44 = "44KHz"; - static const char* unknown = "Unknown"; - - switch (sound_rate) { - case 0: return khz_5_5; - case 1: return khz_11; - case 2: return khz_22; - case 3: return khz_44; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_sound_size2string(char sound_size) - { - static const char* bit_8 = "8bit"; - static const char* bit_16 = "16bit"; - static const char* unknown = "Unknown"; - - switch (sound_size) { - case 0: return bit_8; - case 1: return bit_16; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_sound_type2string(char sound_type) - { - static const char* mono = "Mono"; - static const char* stereo = "Stereo"; - static const char* unknown = "Unknown"; - - switch (sound_type) { - case 0: return mono; - case 1: return stereo; - default: return unknown; - } - - return unknown; - } - - const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type) - { - static const char* sps_pps = "SH"; - static const char* raw = "Raw"; - static const char* unknown = "Unknown"; - - switch (aac_packet_type) { - case 0: return sps_pps; - case 1: return raw; - default: return unknown; - } - - return unknown; - } - - int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size) - { - int ret = ERROR_SUCCESS; - - // Initialize to empty NULL terminated string. - buffer[0] = 0; - - char sbytes[40]; - if (true) { - int nb = srs_min(8, size); - int p = 0; - for (int i = 0; i < nb; i++) { - p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); - } - } - - uint32_t pts; - if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { - snprintf(buffer, nb_buffer, "Rtmp packet type=%s, dts=%d, size=%d, DecodeError, (%s), ret=%d", - srs_human_flv_tag_type2string(type), timestamp, size, sbytes, ret); - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - snprintf(buffer, nb_buffer, "Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s), (%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - snprintf(buffer, nb_buffer, "Audio packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s,%s,%s), (%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, size, - srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), - srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), - srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), - srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), - srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - int nb = snprintf(buffer, nb_buffer, "Data packet type=%s, time=%d, size=%d, (%s)", - srs_human_flv_tag_type2string(type), timestamp, size, sbytes); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; - srs_freepa(amf0_str); - } - buffer[nb] = 0; - } else { - snprintf(buffer, nb_buffer, "Rtmp packet type=%#x, dts=%d, pts=%d, size=%d, (%s)", - type, timestamp, pts, size, sbytes); - } - + if (SrsFlvVideo::sh(data, size)) { + *ppts = time; return ret; } - int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets) - { - int ret = ERROR_SUCCESS; - - // Initialize to empty NULL terminated string. - buffer[0] = 0; - - // packets interval in milliseconds. - double pi = 0; - if (pre_now > starttime && nb_packets > 0) { - pi = (pre_now - starttime) / (double)nb_packets; + // 1bytes, frame type and codec id. + // 1bytes, avc packet type. + // 3bytes, cts, composition time, + // pts = dts + cts, or + // cts = pts - dts. + if (size < 5) { + return ERROR_FLV_INVALID_VIDEO_TAG; + } + + uint32_t cts = 0; + char* p = data + 2; + char* pp = (char*)&cts; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + *ppts = time + cts; + + return ret; +} + +srs_bool srs_utils_flv_tag_is_ok(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT; +} + +srs_bool srs_utils_flv_tag_is_audio(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO; +} + +srs_bool srs_utils_flv_tag_is_video(char type) +{ + return type == SRS_RTMP_TYPE_VIDEO; +} + +srs_bool srs_utils_flv_tag_is_av(char type) +{ + return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO; +} + +char srs_utils_flv_video_codec_id(char* data, int size) +{ + if (size < 1) { + return 0; + } + + char codec_id = data[0]; + codec_id = codec_id & 0x0F; + + return codec_id; +} + +char srs_utils_flv_video_avc_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; + } + + if (!SrsFlvVideo::h264(data, size)) { + return -1; + } + + uint8_t avc_packet_type = data[1]; + + if (avc_packet_type > 2) { + return -1; + } + + return avc_packet_type; +} + +char srs_utils_flv_video_frame_type(char* data, int size) +{ + if (size < 1) { + return -1; + } + + if (!SrsFlvVideo::h264(data, size)) { + return -1; + } + + uint8_t frame_type = data[0]; + frame_type = (frame_type >> 4) & 0x0f; + if (frame_type < 1 || frame_type > 5) { + return -1; + } + + return frame_type; +} + +char srs_utils_flv_audio_sound_format(char* data, int size) +{ + if (size < 1) { + return -1; + } + + uint8_t sound_format = data[0]; + sound_format = (sound_format >> 4) & 0x0f; + if (sound_format > 15 || sound_format == 12 || sound_format == 13) { + return -1; + } + + return sound_format; +} + +char srs_utils_flv_audio_sound_rate(char* data, int size) +{ + if (size < 1) { + return -1; + } + + uint8_t sound_rate = data[0]; + sound_rate = (sound_rate >> 2) & 0x03; + + return sound_rate; +} + +char srs_utils_flv_audio_sound_size(char* data, int size) +{ + if (size < 1) { + return -1; + } + + uint8_t sound_size = data[0]; + sound_size = (sound_size >> 1) & 0x01; + + return sound_size; +} + +char srs_utils_flv_audio_sound_type(char* data, int size) +{ + if (size < 1) { + return -1; + } + + uint8_t sound_type = data[0]; + sound_type = sound_type & 0x01; + + return sound_type; +} + +char srs_utils_flv_audio_aac_packet_type(char* data, int size) +{ + if (size < 2) { + return -1; + } + + if (srs_utils_flv_audio_sound_format(data, size) != 10) { + return -1; + } + + uint8_t aac_packet_type = data[1]; + if (aac_packet_type > 1) { + return -1; + } + + return aac_packet_type; +} + +char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize) +{ + if (!amf0) { + return NULL; + } + + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + + return any->human_print(pdata, psize); +} + +const char* srs_human_flv_tag_type2string(char type) +{ + static const char* audio = "Audio"; + static const char* video = "Video"; + static const char* data = "Data"; + static const char* unknown = "Unknown"; + + switch (type) { + case SRS_RTMP_TYPE_AUDIO: return audio; + case SRS_RTMP_TYPE_VIDEO: return video; + case SRS_RTMP_TYPE_SCRIPT: return data; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_codec_id2string(char codec_id) +{ + static const char* h263 = "H.263"; + static const char* screen = "Screen"; + static const char* vp6 = "VP6"; + static const char* vp6_alpha = "VP6Alpha"; + static const char* screen2 = "Screen2"; + static const char* h264 = "H.264"; + static const char* unknown = "Unknown"; + + switch (codec_id) { + case 2: return h263; + case 3: return screen; + case 4: return vp6; + case 5: return vp6_alpha; + case 6: return screen2; + case 7: return h264; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type) +{ + static const char* sps_pps = "SH"; + static const char* nalu = "Nalu"; + static const char* sps_pps_end = "SpsPpsEnd"; + static const char* unknown = "Unknown"; + + switch (avc_packet_type) { + case 0: return sps_pps; + case 1: return nalu; + case 2: return sps_pps_end; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_video_frame_type2string(char frame_type) +{ + static const char* keyframe = "I"; + static const char* interframe = "P/B"; + static const char* disposable_interframe = "DI"; + static const char* generated_keyframe = "GI"; + static const char* video_infoframe = "VI"; + static const char* unknown = "Unknown"; + + switch (frame_type) { + case 1: return keyframe; + case 2: return interframe; + case 3: return disposable_interframe; + case 4: return generated_keyframe; + case 5: return video_infoframe; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_format2string(char sound_format) +{ + static const char* linear_pcm = "LinearPCM"; + static const char* ad_pcm = "ADPCM"; + static const char* mp3 = "MP3"; + static const char* linear_pcm_le = "LinearPCMLe"; + static const char* nellymoser_16khz = "NellymoserKHz16"; + static const char* nellymoser_8khz = "NellymoserKHz8"; + static const char* nellymoser = "Nellymoser"; + static const char* g711_a_pcm = "G711APCM"; + static const char* g711_mu_pcm = "G711MuPCM"; + static const char* reserved = "Reserved"; + static const char* aac = "AAC"; + static const char* speex = "Speex"; + static const char* mp3_8khz = "MP3KHz8"; + static const char* device_specific = "DeviceSpecific"; + static const char* unknown = "Unknown"; + + switch (sound_format) { + case 0: return linear_pcm; + case 1: return ad_pcm; + case 2: return mp3; + case 3: return linear_pcm_le; + case 4: return nellymoser_16khz; + case 5: return nellymoser_8khz; + case 6: return nellymoser; + case 7: return g711_a_pcm; + case 8: return g711_mu_pcm; + case 9: return reserved; + case 10: return aac; + case 11: return speex; + case 14: return mp3_8khz; + case 15: return device_specific; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_rate2string(char sound_rate) +{ + static const char* khz_5_5 = "5.5KHz"; + static const char* khz_11 = "11KHz"; + static const char* khz_22 = "22KHz"; + static const char* khz_44 = "44KHz"; + static const char* unknown = "Unknown"; + + switch (sound_rate) { + case 0: return khz_5_5; + case 1: return khz_11; + case 2: return khz_22; + case 3: return khz_44; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_size2string(char sound_size) +{ + static const char* bit_8 = "8bit"; + static const char* bit_16 = "16bit"; + static const char* unknown = "Unknown"; + + switch (sound_size) { + case 0: return bit_8; + case 1: return bit_16; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_sound_type2string(char sound_type) +{ + static const char* mono = "Mono"; + static const char* stereo = "Stereo"; + static const char* unknown = "Unknown"; + + switch (sound_type) { + case 0: return mono; + case 1: return stereo; + default: return unknown; + } + + return unknown; +} + +const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type) +{ + static const char* sps_pps = "SH"; + static const char* raw = "Raw"; + static const char* unknown = "Unknown"; + + switch (aac_packet_type) { + case 0: return sps_pps; + case 1: return raw; + default: return unknown; + } + + return unknown; +} + +int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + // Initialize to empty NULL terminated string. + buffer[0] = 0; + + char sbytes[40]; + if (true) { + int nb = srs_min(8, size); + int p = 0; + for (int i = 0; i < nb; i++) { + p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); } - - // global fps(video and audio mixed fps). - double gfps = 0; - if (pi > 0) { - gfps = 1000 / pi; - } - - int diff = 0; - if (pre_timestamp > 0) { - diff = (int)timestamp - (int)pre_timestamp; - } - - int ndiff = 0; - if (pre_now > 0) { - ndiff = (int)(srs_utils_time_ms() - pre_now); - } - - char sbytes[40]; - if (true) { - int nb = srs_min(8, size); - int p = 0; - for (int i = 0; i < nb; i++) { - p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); - } - } - - uint32_t pts; - if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { - snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, ndiff=%d, diff=%d, size=%d, DecodeError, (%s), ret=%d", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes, ret); - return ret; - } - - if (type == SRS_RTMP_TYPE_VIDEO) { - snprintf(buffer, nb_buffer, "Video packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s), (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, - srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), - srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), - srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_AUDIO) { - snprintf(buffer, nb_buffer, "Audio packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s,%s,%s), (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, - srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), - srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), - srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), - srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), - srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), - sbytes); - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - int nb = snprintf(buffer, nb_buffer, "Data packet id=%" PRId64 "/%.1f/%.1f, type=%s, time=%d, ndiff=%d, diff=%d, size=%d, (%s)", - nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes); - int nparsed = 0; - while (nparsed < size) { - int nb_parsed_this = 0; - srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); - if (amf0 == NULL) { - break; - } - - nparsed += nb_parsed_this; - - char* amf0_str = NULL; - nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; - srs_freepa(amf0_str); - } - buffer[nb] = 0; - } else { - snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%#x, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, (%s)", - nb_packets, pi, gfps, type, timestamp, pts, ndiff, diff, size, sbytes); - } - + } + + uint32_t pts; + if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { + snprintf(buffer, nb_buffer, "Rtmp packet type=%s, dts=%d, size=%d, DecodeError, (%s), ret=%d", + srs_human_flv_tag_type2string(type), timestamp, size, sbytes, ret); return ret; } - const char* srs_human_format_time() - { - struct timeval tv; - static char buf[24]; - - memset(buf, 0, sizeof(buf)); - - // clock time - if (gettimeofday(&tv, NULL) == -1) { - return buf; + if (type == SRS_RTMP_TYPE_VIDEO) { + snprintf(buffer, nb_buffer, "Video packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s), (%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + snprintf(buffer, nb_buffer, "Audio packet type=%s, dts=%d, pts=%d, size=%d, %s(%s,%s,%s,%s), (%s)", + srs_human_flv_tag_type2string(type), timestamp, pts, size, + srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), + srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), + srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), + srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), + srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + int nb = snprintf(buffer, nb_buffer, "Data packet type=%s, time=%d, size=%d, (%s)", + srs_human_flv_tag_type2string(type), timestamp, size, sbytes); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; + srs_freepa(amf0_str); } - - // to calendar time - struct tm* tm; - if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { - return buf; + buffer[nb] = 0; + } else { + snprintf(buffer, nb_buffer, "Rtmp packet type=%#x, dts=%d, pts=%d, size=%d, (%s)", + type, timestamp, pts, size, sbytes); + } + + return ret; +} + +int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets) +{ + int ret = ERROR_SUCCESS; + + // Initialize to empty NULL terminated string. + buffer[0] = 0; + + // packets interval in milliseconds. + double pi = 0; + if (pre_now > starttime && nb_packets > 0) { + pi = (pre_now - starttime) / (double)nb_packets; + } + + // global fps(video and audio mixed fps). + double gfps = 0; + if (pi > 0) { + gfps = 1000 / pi; + } + + int diff = 0; + if (pre_timestamp > 0) { + diff = (int)timestamp - (int)pre_timestamp; + } + + int ndiff = 0; + if (pre_now > 0) { + ndiff = (int)(srs_utils_time_ms() - pre_now); + } + + char sbytes[40]; + if (true) { + int nb = srs_min(8, size); + int p = 0; + for (int i = 0; i < nb; i++) { + p += snprintf(sbytes+p, 40-p, "0x%02x ", (uint8_t)data[i]); } - - snprintf(buf, sizeof(buf), - "%d-%02d-%02d %02d:%02d:%02d.%03d", - 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - (int)(tv.tv_usec / 1000)); - - // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 - buf[sizeof(buf) - 1] = 0; - + } + + uint32_t pts; + if ((ret = srs_utils_parse_timestamp(timestamp, type, data, size, &pts)) != ERROR_SUCCESS) { + snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, ndiff=%d, diff=%d, size=%d, DecodeError, (%s), ret=%d", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes, ret); + return ret; + } + + if (type == SRS_RTMP_TYPE_VIDEO) { + snprintf(buffer, nb_buffer, "Video packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s), (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, + srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), + srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), + srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_AUDIO) { + snprintf(buffer, nb_buffer, "Audio packet id=%" PRId64 "/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s,%s,%s), (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, + srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), + srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), + srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), + srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)), + srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)), + sbytes); + } else if (type == SRS_RTMP_TYPE_SCRIPT) { + int nb = snprintf(buffer, nb_buffer, "Data packet id=%" PRId64 "/%.1f/%.1f, type=%s, time=%d, ndiff=%d, diff=%d, size=%d, (%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size, sbytes); + int nparsed = 0; + while (nparsed < size) { + int nb_parsed_this = 0; + srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this); + if (amf0 == NULL) { + break; + } + + nparsed += nb_parsed_this; + + char* amf0_str = NULL; + nb += snprintf(buffer + nb, nb_buffer - nb, "\n%s", srs_human_amf0_print(amf0, &amf0_str, NULL)) - 1; + srs_freepa(amf0_str); + } + buffer[nb] = 0; + } else { + snprintf(buffer, nb_buffer, "Rtmp packet id=%" PRId64 "/%.1f/%.1f, type=%#x, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, (%s)", + nb_packets, pi, gfps, type, timestamp, pts, ndiff, diff, size, sbytes); + } + + return ret; +} + +const char* srs_human_format_time() +{ + struct timeval tv; + static char buf[24]; + + memset(buf, 0, sizeof(buf)); + + // clock time + if (gettimeofday(&tv, NULL) == -1) { return buf; } + // to calendar time + struct tm* tm; + if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) { + return buf; + } + snprintf(buf, sizeof(buf), + "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)(tv.tv_usec / 1000)); + + // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 + buf[sizeof(buf) - 1] = 0; + + return buf; +} + + #ifdef SRS_HIJACK_IO - srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) - { - if (!rtmp) { - return NULL; - } - - Context* context = (Context*)rtmp; - if (!context->skt) { - return NULL; - } - - return context->skt->hijack_io(); +srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) +{ + if (!rtmp) { + return NULL; } + + Context* context = (Context*)rtmp; + if (!context->skt) { + return NULL; + } + + return context->skt->hijack_io(); +} #endif + +srs_rtmp_t srs_rtmp_create2(const char* url) +{ + Context* context = new Context(); - srs_rtmp_t srs_rtmp_create2(const char* url) - { - Context* context = new Context(); + // use url as tcUrl. + context->url = url; + // auto append stream. + context->url += "/livestream"; + + // create socket + srs_freep(context->skt); + context->skt = new SimpleSocketStream(); + + int ret = ERROR_SUCCESS; + if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { + srs_human_error("Create socket failed, ret=%d", ret); - // use url as tcUrl. - context->url = url; - // auto append stream. - context->url += "/livestream"; - - // create socket - srs_freep(context->skt); - context->skt = new SimpleSocketStream(); - - int ret = ERROR_SUCCESS; - if ((ret = context->skt->create_socket(context)) != ERROR_SUCCESS) { - srs_human_error("Create socket failed, ret=%d", ret); - - // free the context and return NULL - srs_freep(context); - return NULL; - } - - return context; + // free the context and return NULL + srs_freep(context); + return NULL; } - int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128],char srs_server[128], char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid) - { - srs_server_ip[0] = 0; - srs_server[0] = 0; - srs_primary[0] = 0; - srs_authors[0] = 0; - srs_version[0] = 0; - *srs_id = 0; - *srs_pid = 0; - - int ret = ERROR_SUCCESS; - - if ((ret = srs_rtmp_connect_app(rtmp)) != ERROR_SUCCESS) { - return ret; - } - - srs_assert(rtmp != NULL); - Context* context = (Context*)rtmp; - SrsServerInfo* si = &context->si; - - snprintf(srs_server_ip, 128, "%s", si->ip.c_str()); - snprintf(srs_server, 128, "%s", si->sig.c_str()); - snprintf(srs_version, 32, "%d.%d.%d.%d", si->major, si->minor, si->revision, si->build); - + return context; +} + +int srs_rtmp_connect_app2(srs_rtmp_t rtmp, char srs_server_ip[128],char srs_server[128], char srs_primary[128], char srs_authors[128], char srs_version[32], int* srs_id, int* srs_pid) +{ + srs_server_ip[0] = 0; + srs_server[0] = 0; + srs_primary[0] = 0; + srs_authors[0] = 0; + srs_version[0] = 0; + *srs_id = 0; + *srs_pid = 0; + + int ret = ERROR_SUCCESS; + + if ((ret = srs_rtmp_connect_app(rtmp)) != ERROR_SUCCESS) { return ret; } - int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size) - { - return srs_human_print_rtmp_packet3(type, timestamp, data, size, 0, 0); - } + srs_assert(rtmp != NULL); + Context* context = (Context*)rtmp; + SrsServerInfo* si = &context->si; - int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp) - { - return srs_human_print_rtmp_packet3(type, timestamp, data, size, pre_timestamp, 0); - } + snprintf(srs_server_ip, 128, "%s", si->ip.c_str()); + snprintf(srs_server, 128, "%s", si->sig.c_str()); + snprintf(srs_version, 32, "%d.%d.%d.%d", si->major, si->minor, si->revision, si->build); - int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now) - { - return srs_human_print_rtmp_packet4(type, timestamp, data, size, pre_timestamp, pre_now, 0, 0); - } - - int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, - int64_t starttime, int64_t nb_packets - ) { - char buffer[1024]; - int ret = srs_human_format_rtmp_packet2(buffer, sizeof(buffer), type, timestamp, data, size, pre_timestamp, pre_now, starttime, nb_packets); - srs_human_trace("%s", buffer); - return ret; - } + return ret; +} + +int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size) +{ + return srs_human_print_rtmp_packet3(type, timestamp, data, size, 0, 0); +} + +int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp) +{ + return srs_human_print_rtmp_packet3(type, timestamp, data, size, pre_timestamp, 0); +} + +int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now) +{ + return srs_human_print_rtmp_packet4(type, timestamp, data, size, pre_timestamp, pre_now, 0, 0); +} + +int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, + int64_t starttime, int64_t nb_packets +) { + char buffer[1024]; + int ret = srs_human_format_rtmp_packet2(buffer, sizeof(buffer), type, timestamp, data, size, pre_timestamp, pre_now, starttime, nb_packets); + srs_human_trace("%s", buffer); + return ret; +} #ifdef __cplusplus } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index 416216391..d4191b2b9 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -77,972 +77,972 @@ pid_t getpid(void); extern "C"{ #endif - /** - * The schema of RTMP url, the following are legal urls: - * srs_url_schema_normal: rtmp://vhost:port/app/stream - * srs_url_schema_via : rtmp://ip:port/vhost/app/stream - * srs_url_schema_vis : rtmp://ip:port/app/stream?vhost=xxx - * srs_url_schema_vis2 : rtmp://ip:port/app/stream?domain=xxx - */ - enum srs_url_schema - { - // Forbidden. - srs_url_schema_forbidden = 0, - // Normal RTMP URL, the vhost put in host field, using DNS to resolve the server ip. - // For example, rtmp://vhost:port/app/stream - srs_url_schema_normal, - // VIA(vhost in app), the vhost put in app field. - // For example, rtmp://ip:port/vhost/app/stream - srs_url_schema_via, - // VIS(vhost in stream), the vhost put in query string, keyword use vhost=xxx. - // For example, rtmp://ip:port/app/stream?vhost=xxx - srs_url_schema_vis, - // VIS, keyword use domain=xxx. - // For example, rtmp://ip:port/app/stream?domain=xxx - srs_url_schema_vis2 - }; - - // typedefs - typedef int srs_bool; - - /************************************************************* - ************************************************************** - * srs-librtmp version - ************************************************************** - *************************************************************/ - extern int srs_version_major(); - extern int srs_version_minor(); - extern int srs_version_revision(); - - /************************************************************* - ************************************************************** - * RTMP protocol context - ************************************************************** - *************************************************************/ - // the RTMP handler. - typedef void* srs_rtmp_t; - typedef void* srs_amf0_t; - - /** - * Create a RTMP handler. - * @param url The RTMP url, for example, rtmp://localhost/live/livestream - * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. - * @remark default schema to srs_url_schema_normal, use srs_rtmp_set_schema to change it. - * - * @return a rtmp handler, or NULL if error occured. - */ - extern srs_rtmp_t srs_rtmp_create(const char* url); - /** - * set socket timeout - * @param recv_timeout_ms the timeout for receiving messages in ms. - * @param send_timeout_ms the timeout for sending message in ms. - * @remark user can set timeout once srs_rtmp_create/srs_rtmp_create2, - * or before srs_rtmp_handshake or srs_rtmp_dns_resolve to connect to server. - * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms); - /** - * close and destroy the rtmp stack. - * @remark, user should never use the rtmp again. - */ - extern void srs_rtmp_destroy(srs_rtmp_t rtmp); - - /************************************************************* - ************************************************************** - * RTMP protocol stack - ************************************************************** - *************************************************************/ - /** - * connect and handshake with server - * category: publish/play - * previous: rtmp-create - * next: connect-app - * - * @return 0, success; otherswise, failed. - */ - /** - * simple handshake specifies in rtmp 1.0, - * not depends on ssl. - */ - /** - * srs_rtmp_handshake equals to invoke: - * srs_rtmp_dns_resolve() - * srs_rtmp_connect_server() - * srs_rtmp_do_simple_handshake() - * user can use these functions if needed. - */ - extern int srs_rtmp_handshake(srs_rtmp_t rtmp); - // parse uri, create socket, resolve host - extern int srs_rtmp_dns_resolve(srs_rtmp_t rtmp); - // connect socket to server - extern int srs_rtmp_connect_server(srs_rtmp_t rtmp); - // do simple handshake over socket. - extern int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp); - // do complex handshake over socket. - extern int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp); - - /** - * set the args of connect packet for rtmp. - * @param args, the extra amf0 object args. - * @remark, all params can be NULL to ignore. - * @remark, user should never free the args for we directly use it. - */ - extern int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args); - - /** - * Set the schema of URL when connect to tcUrl by srs_rtmp_connect_app. - * @param schema, The schema of URL, @see srs_url_schema. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema); - - /** - * Connect to RTMP tcUrl(Vhost/App), similar to flash AS3 NetConnection.connect(tcUrl). - * @remark When connected to server, user can retrieve informations from RTMP handler, - * for example, use srs_rtmp_get_server_id to get server ip/pid/cid. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); - - /** - * Retrieve server ip from RTMP handler. - * @param ip A NULL terminated string specifies the server ip. - * @param pid An int specifies the PID of server. -1 is no PID information. - * @param cid An int specifies the CID of connection. -1 is no CID information. - * @remark For SRS, ip/pid/cid is the UUID of a client. For other server, these values maybe unknown. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid); - - /** - * Retrieve server signature from RTMP handler. - * @param sig A NULL terminated string specifies the server signature. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig); - - /** - * Retrieve server version from RTMP handler, which in major.minor.revision.build format. - * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build); - - /** - * play a live/vod stream. - * category: play - * previous: connect-app - * next: destroy - * @return 0, success; otherwise, failed. - */ - extern int srs_rtmp_play_stream(srs_rtmp_t rtmp); - - /** - * publish a live stream. - * category: publish - * previous: connect-app - * next: destroy - * @return 0, success; otherwise, failed. - */ - extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp); - - /** - * do bandwidth check with srs server. - * - * bandwidth info: - * @param start_time, output the start time, in ms. - * @param end_time, output the end time, in ms. - * @param play_kbps, output the play/download kbps. - * @param publish_kbps, output the publish/upload kbps. - * @param play_bytes, output the play/download bytes. - * @param publish_bytes, output the publish/upload bytes. - * @param play_duration, output the play/download test duration, in ms. - * @param publish_duration, output the publish/upload test duration, in ms. - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, - int64_t* start_time, int64_t* end_time, - int* play_kbps, int* publish_kbps, - int* play_bytes, int* publish_bytes, - int* play_duration, int* publish_duration); - - /** - * E.4.1 FLV Tag, page 75 - */ - // 8 = audio +/** + * The schema of RTMP url, the following are legal urls: + * srs_url_schema_normal: rtmp://vhost:port/app/stream + * srs_url_schema_via : rtmp://ip:port/vhost/app/stream + * srs_url_schema_vis : rtmp://ip:port/app/stream?vhost=xxx + * srs_url_schema_vis2 : rtmp://ip:port/app/stream?domain=xxx + */ +enum srs_url_schema +{ + // Forbidden. + srs_url_schema_forbidden = 0, + // Normal RTMP URL, the vhost put in host field, using DNS to resolve the server ip. + // For example, rtmp://vhost:port/app/stream + srs_url_schema_normal, + // VIA(vhost in app), the vhost put in app field. + // For example, rtmp://ip:port/vhost/app/stream + srs_url_schema_via, + // VIS(vhost in stream), the vhost put in query string, keyword use vhost=xxx. + // For example, rtmp://ip:port/app/stream?vhost=xxx + srs_url_schema_vis, + // VIS, keyword use domain=xxx. + // For example, rtmp://ip:port/app/stream?domain=xxx + srs_url_schema_vis2 +}; + +// typedefs +typedef int srs_bool; + +/************************************************************* + ************************************************************** + * srs-librtmp version + ************************************************************** + *************************************************************/ +extern int srs_version_major(); +extern int srs_version_minor(); +extern int srs_version_revision(); + +/************************************************************* + ************************************************************** + * RTMP protocol context + ************************************************************** + *************************************************************/ +// the RTMP handler. +typedef void* srs_rtmp_t; +typedef void* srs_amf0_t; + +/** + * Create a RTMP handler. + * @param url The RTMP url, for example, rtmp://localhost/live/livestream + * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. + * @remark default schema to srs_url_schema_normal, use srs_rtmp_set_schema to change it. + * + * @return a rtmp handler, or NULL if error occured. + */ +extern srs_rtmp_t srs_rtmp_create(const char* url); +/** + * set socket timeout + * @param recv_timeout_ms the timeout for receiving messages in ms. + * @param send_timeout_ms the timeout for sending message in ms. + * @remark user can set timeout once srs_rtmp_create/srs_rtmp_create2, + * or before srs_rtmp_handshake or srs_rtmp_dns_resolve to connect to server. + * @remark default timeout to 30s if not set by srs_rtmp_set_timeout. + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_set_timeout(srs_rtmp_t rtmp, int recv_timeout_ms, int send_timeout_ms); +/** + * close and destroy the rtmp stack. + * @remark, user should never use the rtmp again. + */ +extern void srs_rtmp_destroy(srs_rtmp_t rtmp); + +/************************************************************* + ************************************************************** + * RTMP protocol stack + ************************************************************** + *************************************************************/ +/** + * connect and handshake with server + * category: publish/play + * previous: rtmp-create + * next: connect-app + * + * @return 0, success; otherswise, failed. + */ +/** + * simple handshake specifies in rtmp 1.0, + * not depends on ssl. + */ +/** + * srs_rtmp_handshake equals to invoke: + * srs_rtmp_dns_resolve() + * srs_rtmp_connect_server() + * srs_rtmp_do_simple_handshake() + * user can use these functions if needed. + */ +extern int srs_rtmp_handshake(srs_rtmp_t rtmp); +// parse uri, create socket, resolve host +extern int srs_rtmp_dns_resolve(srs_rtmp_t rtmp); +// connect socket to server +extern int srs_rtmp_connect_server(srs_rtmp_t rtmp); +// do simple handshake over socket. +extern int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp); +// do complex handshake over socket. +extern int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp); + +/** + * set the args of connect packet for rtmp. + * @param args, the extra amf0 object args. + * @remark, all params can be NULL to ignore. + * @remark, user should never free the args for we directly use it. + */ +extern int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args); + +/** + * Set the schema of URL when connect to tcUrl by srs_rtmp_connect_app. + * @param schema, The schema of URL, @see srs_url_schema. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_set_schema(srs_rtmp_t rtmp, enum srs_url_schema schema); + +/** + * Connect to RTMP tcUrl(Vhost/App), similar to flash AS3 NetConnection.connect(tcUrl). + * @remark When connected to server, user can retrieve informations from RTMP handler, + * for example, use srs_rtmp_get_server_id to get server ip/pid/cid. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_connect_app(srs_rtmp_t rtmp); + +/** + * Retrieve server ip from RTMP handler. + * @param ip A NULL terminated string specifies the server ip. + * @param pid An int specifies the PID of server. -1 is no PID information. + * @param cid An int specifies the CID of connection. -1 is no CID information. + * @remark For SRS, ip/pid/cid is the UUID of a client. For other server, these values maybe unknown. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_id(srs_rtmp_t rtmp, char** ip, int* pid, int* cid); + +/** + * Retrieve server signature from RTMP handler. + * @param sig A NULL terminated string specifies the server signature. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_sig(srs_rtmp_t rtmp, char** sig); + +/** + * Retrieve server version from RTMP handler, which in major.minor.revision.build format. + * @remark When connected to server by srs_rtmp_connect_app, the information is ready to be retrieved. + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_get_server_version(srs_rtmp_t rtmp, int* major, int* minor, int* revision, int* build); + +/** + * play a live/vod stream. + * category: play + * previous: connect-app + * next: destroy + * @return 0, success; otherwise, failed. + */ +extern int srs_rtmp_play_stream(srs_rtmp_t rtmp); + +/** + * publish a live stream. + * category: publish + * previous: connect-app + * next: destroy + * @return 0, success; otherwise, failed. + */ +extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp); + +/** + * do bandwidth check with srs server. + * + * bandwidth info: + * @param start_time, output the start time, in ms. + * @param end_time, output the end time, in ms. + * @param play_kbps, output the play/download kbps. + * @param publish_kbps, output the publish/upload kbps. + * @param play_bytes, output the play/download bytes. + * @param publish_bytes, output the publish/upload bytes. + * @param play_duration, output the play/download test duration, in ms. + * @param publish_duration, output the publish/upload test duration, in ms. + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, + int64_t* start_time, int64_t* end_time, + int* play_kbps, int* publish_kbps, + int* play_bytes, int* publish_bytes, + int* play_duration, int* publish_duration); + +/** + * E.4.1 FLV Tag, page 75 + */ +// 8 = audio #define SRS_RTMP_TYPE_AUDIO 8 - // 9 = video +// 9 = video #define SRS_RTMP_TYPE_VIDEO 9 - // 18 = script data +// 18 = script data #define SRS_RTMP_TYPE_SCRIPT 18 - /** - * read a audio/video/script-data packet from rtmp stream. - * @param type, output the packet type, macros: - * SRS_RTMP_TYPE_AUDIO, FlvTagAudio - * SRS_RTMP_TYPE_VIDEO, FlvTagVideo - * SRS_RTMP_TYPE_SCRIPT, FlvTagScript - * otherswise, invalid type. - * @param timestamp, in ms, overflow in 50days - * @param data, the packet data, according to type: - * FlvTagAudio, @see "E.4.2.1 AUDIODATA" - * FlvTagVideo, @see "E.4.3.1 VIDEODATA" - * FlvTagScript, @see "E.4.4.1 SCRIPTDATA" - * User can free the packet by srs_rtmp_free_packet. - * @param size, size of packet. - * @return the error code. 0 for success; otherwise, error. - * - * @remark: for read, user must free the data. - * @remark: for write, user should never free the data, even if error. - * @example /trunk/research/librtmp/srs_play.c - * @example /trunk/research/librtmp/srs_publish.c - * - * @return 0, success; otherswise, failed. - */ - extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size); - // @param data User should never free it anymore. - extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size); +/** + * read a audio/video/script-data packet from rtmp stream. + * @param type, output the packet type, macros: + * SRS_RTMP_TYPE_AUDIO, FlvTagAudio + * SRS_RTMP_TYPE_VIDEO, FlvTagVideo + * SRS_RTMP_TYPE_SCRIPT, FlvTagScript + * otherswise, invalid type. + * @param timestamp, in ms, overflow in 50days + * @param data, the packet data, according to type: + * FlvTagAudio, @see "E.4.2.1 AUDIODATA" + * FlvTagVideo, @see "E.4.3.1 VIDEODATA" + * FlvTagScript, @see "E.4.4.1 SCRIPTDATA" + * User can free the packet by srs_rtmp_free_packet. + * @param size, size of packet. + * @return the error code. 0 for success; otherwise, error. + * + * @remark: for read, user must free the data. + * @remark: for write, user should never free the data, even if error. + * @example /trunk/research/librtmp/srs_play.c + * @example /trunk/research/librtmp/srs_publish.c + * + * @return 0, success; otherswise, failed. + */ +extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, uint32_t* timestamp, char** data, int* size); +// @param data User should never free it anymore. +extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, uint32_t timestamp, char* data, int size); + +/** + * Free the packet allocated by srs_rtmp_read_packet. + */ +extern void srs_rtmp_free_packet(char* data); + +/** + * whether type is script data and the data is onMetaData. + */ +extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); + +/************************************************************* + ************************************************************** + * audio raw codec + ************************************************************** + *************************************************************/ +/** + * write an audio raw frame to srs. + * not similar to h.264 video, the audio never aggregated, always + * encoded one frame by one, so this api is used to write a frame. + * + * @param sound_format Format of SoundData. The following values are defined: + * 0 = Linear PCM, platform endian + * 1 = ADPCM + * 2 = MP3 + * 3 = Linear PCM, little endian + * 4 = Nellymoser 16 kHz mono + * 5 = Nellymoser 8 kHz mono + * 6 = Nellymoser + * 7 = G.711 A-law logarithmic PCM + * 8 = G.711 mu-law logarithmic PCM + * 9 = reserved + * 10 = AAC + * 11 = Speex + * 14 = MP3 8 kHz + * 15 = Device-specific sound + * Formats 7, 8, 14, and 15 are reserved. + * AAC is supported in Flash Player 9,0,115,0 and higher. + * Speex is supported in Flash Player 10 and higher. + * @param sound_rate Sampling rate. The following values are defined: + * 0 = 5.5 kHz + * 1 = 11 kHz + * 2 = 22 kHz + * 3 = 44 kHz + * @param sound_size Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 0 = 8-bit samples + * 1 = 16-bit samples + * @param sound_type Mono or stereo sound + * 0 = Mono sound + * 1 = Stereo sound + * @param timestamp The timestamp of audio. + * + * @example /trunk/research/librtmp/srs_aac_raw_publish.c + * @example /trunk/research/librtmp/srs_audio_raw_publish.c + * + * @remark for aac, the frame must be in ADTS format. + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS + * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 23, 1.5.1.1 Audio object type + * + * @see https://github.com/ossrs/srs/issues/212 + * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf + * + * @return 0, success; otherswise, failed. + */ +extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, + char sound_format, char sound_rate, char sound_size, char sound_type, + char* frame, int frame_size, uint32_t timestamp); + +/** + * whether aac raw data is in adts format, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + * @param aac_raw_data the input aac raw data, a encoded aac frame data. + * @param ac_raw_size the size of aac raw data. + * + * @reamrk used to check whether current frame is in adts format. + * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS + * @example /trunk/research/librtmp/srs_aac_raw_publish.c + * + * @return 0 false; otherwise, true. + */ +extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); + +/** + * parse the adts header to get the frame size, + * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. + * @param aac_raw_data the input aac raw data, a encoded aac frame data. + * @param ac_raw_size the size of aac raw data. + * + * @return failed when <=0 failed; otherwise, ok. + */ +extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); + +/************************************************************* + ************************************************************** + * h264 raw codec + ************************************************************** + *************************************************************/ +/** + * write h.264 raw frame over RTMP to rtmp server. + * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. + * frames can be one or more than one frame, + * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, + * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) + * about annexb, @see ISO_IEC_14496-10-AVC-2003.pdf, page 211. + * @param frames_size the size of h264 raw data. + * assert frames_size > 0, at least has 1 bytes header. + * @param dts the dts of h.264 raw data. + * @param pts the pts of h.264 raw data. + * + * @remark, user should free the frames. + * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. + * @remark, cts = pts - dts + * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * @see https://github.com/ossrs/srs/issues/66 + * + * @return 0, success; otherswise, failed. + * for dvbsp error, @see srs_h264_is_dvbsp_error(). + * for duplictated sps error, @see srs_h264_is_duplicated_sps_error(). + * for duplictated pps error, @see srs_h264_is_duplicated_pps_error(). + */ +/** + For the example file: + http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw + The data sequence is: + // SPS + 000000016742802995A014016E40 + // PPS + 0000000168CE3880 + // IFrame + 0000000165B8041014C038008B0D0D3A071..... + // PFrame + 0000000141E02041F8CDDC562BBDEFAD2F..... + User can send the SPS+PPS, then each frame: + // SPS+PPS + srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts) + // IFrame + srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) + User also can send one by one: + // SPS + srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts) + // PPS + srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts) + // IFrame + srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) + // PFrame + srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) + */ +extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts); +/** + * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. + * + * @see https://github.com/ossrs/srs/issues/203 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * @remark why drop video? + * some encoder, for example, ipcamera, will send sps/pps before each IFrame, + * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), + * this will cause SRS server to disable HLS. + */ +extern srs_bool srs_h264_is_dvbsp_error(int error_code); +/** + * whether error_code is duplicated sps error. + * + * @see https://github.com/ossrs/srs/issues/204 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + */ +extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); +/** + * whether error_code is duplicated pps error. + * + * @see https://github.com/ossrs/srs/issues/204 + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + */ +extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); +/** + * whether h264 raw data starts with the annexb, + * which bytes sequence matches N[00] 00 00 01, where N>=0. + * @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. + * @paam h264_raw_size the size of h264 raw data. + * @param pnb_start_code output the size of start code, must >=3. + * NULL to ignore. + * + * @reamrk used to check whether current frame is in annexb format. + * @example /trunk/research/librtmp/srs_h264_raw_publish.c + * + * @return 0 false; otherwise, true. + */ +extern srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code); + +/************************************************************* + ************************************************************* + * MP4 muxer and demuxer. + * @example /trunk/research/librtmp/srs_ingest_mp4.c + ************************************************************* + *************************************************************/ +typedef void* srs_mp4_t; +// The sample struct of mp4. +typedef struct { + // The handler type, it's SrsMp4HandlerType. + uint32_t handler_type; - /** - * Free the packet allocated by srs_rtmp_read_packet. - */ - extern void srs_rtmp_free_packet(char* data); + // The dts in milliseconds. + uint32_t dts; + // The codec id. + // video: SrsVideoCodecId. + // audio: SrsAudioCodecId. + uint16_t codec; + // The frame trait, some characteristic: + // video: SrsVideoAvcFrameTrait. + // audio: SrsAudioAacFrameTrait. + uint16_t frame_trait; - /** - * whether type is script data and the data is onMetaData. - */ - extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); + // The video pts in milliseconds. Ignore for audio. + uint32_t pts; + // The video frame type, it's SrsVideoAvcFrameType. + uint16_t frame_type; - /************************************************************* - ************************************************************** - * audio raw codec - ************************************************************** - *************************************************************/ - /** - * write an audio raw frame to srs. - * not similar to h.264 video, the audio never aggregated, always - * encoded one frame by one, so this api is used to write a frame. - * - * @param sound_format Format of SoundData. The following values are defined: - * 0 = Linear PCM, platform endian - * 1 = ADPCM - * 2 = MP3 - * 3 = Linear PCM, little endian - * 4 = Nellymoser 16 kHz mono - * 5 = Nellymoser 8 kHz mono - * 6 = Nellymoser - * 7 = G.711 A-law logarithmic PCM - * 8 = G.711 mu-law logarithmic PCM - * 9 = reserved - * 10 = AAC - * 11 = Speex - * 14 = MP3 8 kHz - * 15 = Device-specific sound - * Formats 7, 8, 14, and 15 are reserved. - * AAC is supported in Flash Player 9,0,115,0 and higher. - * Speex is supported in Flash Player 10 and higher. - * @param sound_rate Sampling rate. The following values are defined: - * 0 = 5.5 kHz - * 1 = 11 kHz - * 2 = 22 kHz - * 3 = 44 kHz - * @param sound_size Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 0 = 8-bit samples - * 1 = 16-bit samples - * @param sound_type Mono or stereo sound - * 0 = Mono sound - * 1 = Stereo sound - * @param timestamp The timestamp of audio. - * - * @example /trunk/research/librtmp/srs_aac_raw_publish.c - * @example /trunk/research/librtmp/srs_audio_raw_publish.c - * - * @remark for aac, the frame must be in ADTS format. - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS - * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 23, 1.5.1.1 Audio object type - * - * @see https://github.com/ossrs/srs/issues/212 - * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf - * - * @return 0, success; otherswise, failed. - */ - extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, - char sound_format, char sound_rate, char sound_size, char sound_type, - char* frame, int frame_size, uint32_t timestamp); - - /** - * whether aac raw data is in adts format, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - * @param aac_raw_data the input aac raw data, a encoded aac frame data. - * @param ac_raw_size the size of aac raw data. - * - * @reamrk used to check whether current frame is in adts format. - * @see ISO_IEC_14496-3-AAC-2001.pdf, page 75, 1.A.2.2 ADTS - * @example /trunk/research/librtmp/srs_aac_raw_publish.c - * - * @return 0 false; otherwise, true. - */ - extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size); - - /** - * parse the adts header to get the frame size, - * which bytes sequence matches '1111 1111 1111'B, that is 0xFFF. - * @param aac_raw_data the input aac raw data, a encoded aac frame data. - * @param ac_raw_size the size of aac raw data. - * - * @return failed when <=0 failed; otherwise, ok. - */ - extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); - - /************************************************************* - ************************************************************** - * h264 raw codec - ************************************************************** - *************************************************************/ - /** - * write h.264 raw frame over RTMP to rtmp server. - * @param frames the input h264 raw data, encoded h.264 I/P/B frames data. - * frames can be one or more than one frame, - * each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, - * for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40) - * about annexb, @see ISO_IEC_14496-10-AVC-2003.pdf, page 211. - * @param frames_size the size of h264 raw data. - * assert frames_size > 0, at least has 1 bytes header. - * @param dts the dts of h.264 raw data. - * @param pts the pts of h.264 raw data. - * - * @remark, user should free the frames. - * @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms. - * @remark, cts = pts - dts - * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * @see https://github.com/ossrs/srs/issues/66 - * - * @return 0, success; otherswise, failed. - * for dvbsp error, @see srs_h264_is_dvbsp_error(). - * for duplictated sps error, @see srs_h264_is_duplicated_sps_error(). - * for duplictated pps error, @see srs_h264_is_duplicated_pps_error(). - */ - /** - For the example file: - http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw - The data sequence is: - // SPS - 000000016742802995A014016E40 - // PPS - 0000000168CE3880 - // IFrame - 0000000165B8041014C038008B0D0D3A071..... - // PFrame - 0000000141E02041F8CDDC562BBDEFAD2F..... - User can send the SPS+PPS, then each frame: - // SPS+PPS - srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts) - // IFrame - srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) - // PFrame - srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) - User also can send one by one: - // SPS - srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts) - // PPS - srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts) - // IFrame - srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts) - // PFrame - srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) - */ - extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, char* frames, int frames_size, uint32_t dts, uint32_t pts); - /** - * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. - * - * @see https://github.com/ossrs/srs/issues/203 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * @remark why drop video? - * some encoder, for example, ipcamera, will send sps/pps before each IFrame, - * so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header), - * this will cause SRS server to disable HLS. - */ - extern srs_bool srs_h264_is_dvbsp_error(int error_code); - /** - * whether error_code is duplicated sps error. - * - * @see https://github.com/ossrs/srs/issues/204 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - */ - extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); - /** - * whether error_code is duplicated pps error. - * - * @see https://github.com/ossrs/srs/issues/204 - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - */ - extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); - /** - * whether h264 raw data starts with the annexb, - * which bytes sequence matches N[00] 00 00 01, where N>=0. - * @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data. - * @paam h264_raw_size the size of h264 raw data. - * @param pnb_start_code output the size of start code, must >=3. - * NULL to ignore. - * - * @reamrk used to check whether current frame is in annexb format. - * @example /trunk/research/librtmp/srs_h264_raw_publish.c - * - * @return 0 false; otherwise, true. - */ - extern srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code); - - /************************************************************* - ************************************************************* - * MP4 muxer and demuxer. - * @example /trunk/research/librtmp/srs_ingest_mp4.c - ************************************************************* - *************************************************************/ - typedef void* srs_mp4_t; - // The sample struct of mp4. - typedef struct { - // The handler type, it's SrsMp4HandlerType. - uint32_t handler_type; - - // The dts in milliseconds. - uint32_t dts; - // The codec id. - // video: SrsVideoCodecId. - // audio: SrsAudioCodecId. - uint16_t codec; - // The frame trait, some characteristic: - // video: SrsVideoAvcFrameTrait. - // audio: SrsAudioAacFrameTrait. - uint16_t frame_trait; - - // The video pts in milliseconds. Ignore for audio. - uint32_t pts; - // The video frame type, it's SrsVideoAvcFrameType. - uint16_t frame_type; - - // The audio sample rate, it's SrsAudioSampleRate. - uint8_t sample_rate; - // The audio sound bits, it's SrsAudioSampleBits. - uint8_t sound_bits; - // The audio sound type, it's SrsAudioChannels. - uint8_t channels; - - // The size of sample payload in bytes. - uint32_t nb_sample; - // The output sample data, user must free it by srs_mp4_free_sample. - uint8_t* sample; - } srs_mp4_sample_t; - /** - * Open mp4 file for muxer(write) or demuxer(read). - * @return A MP4 demuxer, NULL if failed. - */ - extern srs_mp4_t srs_mp4_open_read(const char* file); - /** - * Close the MP4 demuxer. - */ - extern void srs_mp4_close(srs_mp4_t mp4); - /** - * Initialize mp4 demuxer in non-seek mode. - * @remark Only support non-seek mode, that is fmp4 or moov before mdata. - * For the live streaming, we must feed stream frame by frame. - */ - extern int srs_mp4_init_demuxer(srs_mp4_t mp4); - /** - * Read a sample form mp4. - * @remark User can use srs_mp4_sample_to_flv_tag to convert mp4 sampel to flv tag. - * Use the srs_mp4_to_flv_tag_size to calc the flv tag data size to alloc. - */ - extern int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* sample); - /** - * Free the allocated mp4 sample. - */ - extern void srs_mp4_free_sample(srs_mp4_sample_t* sample); - /** - * Calc the size of flv tag, for the mp4 sample to convert to. - */ - extern int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* sample); - /** - * Covert mp4 sample to flv tag. - */ - extern int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* sample, char* type, uint32_t* time, char* data, int32_t size); - /* error code */ - /* whether the error code indicates EOF */ - extern srs_bool srs_mp4_is_eof(int error_code); - - /************************************************************* - ************************************************************** - * flv codec - * @example /trunk/research/librtmp/srs_flv_injecter.c - * @example /trunk/research/librtmp/srs_flv_parser.c - * @example /trunk/research/librtmp/srs_ingest_flv.c - * @example /trunk/research/librtmp/srs_ingest_rtmp.c - ************************************************************** - *************************************************************/ - typedef void* srs_flv_t; - /** - * Open FLV file in demux mode. - * @return A FLV demuxer, NULL if failed. - */ - extern srs_flv_t srs_flv_open_read(const char* file); - /** - * Open FlV file in mux mode. - * @return A FLV muxer, NULL if failed. - */ - extern srs_flv_t srs_flv_open_write(const char* file); - /** - * Close the FLV demuxer or muxer. - */ - extern void srs_flv_close(srs_flv_t flv); - /** - * read the flv header. 9bytes header. - * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. - * 3bytes, signature, "FLV", - * 1bytes, version, 0x01, - * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. - * 4bytes, dataoffset, 0x09, The length of this header in bytes - * - * @return 0, success; otherswise, failed. - * @remark, drop the 4bytes zero previous tag size. - */ - extern int srs_flv_read_header(srs_flv_t flv, char header[9]); - /** - * read the flv tag header, 1bytes tag, 3bytes data_size, - * 4bytes time, 3bytes stream id. - * @param ptype, output the type of tag, macros: - * SRS_RTMP_TYPE_AUDIO, FlvTagAudio - * SRS_RTMP_TYPE_VIDEO, FlvTagVideo - * SRS_RTMP_TYPE_SCRIPT, FlvTagScript - * @param pdata_size, output the size of tag data. - * @param ptime, output the time of tag, the dts in ms. - * - * @return 0, success; otherswise, failed. - * @remark, user must ensure the next is a tag, srs never check it. - */ - extern int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime); - /** - * read the tag data. drop the 4bytes previous tag size - * @param data, the data to read, user alloc and free it. - * @param size, the size of data to read, get by srs_flv_read_tag_header(). - * @remark, srs will ignore and drop the 4bytes previous tag size. - */ - extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); - /** - * write the flv header. 9bytes header. - * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. - * 3bytes, signature, "FLV", - * 1bytes, version, 0x01, - * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. - * 4bytes, dataoffset, 0x09, The length of this header in bytes - * - * @return 0, success; otherswise, failed. - * @remark, auto write the 4bytes zero previous tag size. - */ - extern int srs_flv_write_header(srs_flv_t flv, char header[9]); - /** - * write the flv tag to file. - * - * @return 0, success; otherswise, failed. - * @remark, auto write the 4bytes zero previous tag size. - */ - /* write flv tag to file, auto write the 4bytes previous tag size */ - extern int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); - /** - * get the tag size, for flv injecter to adjust offset, - * size = tag_header(11B) + data_size + previous_tag(4B) - * @return the size of tag. - */ - extern int srs_flv_size_tag(int data_size); - /* file stream */ - /* file stream tellg to get offset */ - extern int64_t srs_flv_tellg(srs_flv_t flv); - /* seek file stream, offset is form the start of file */ - extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); - /* error code */ - /* whether the error code indicates EOF */ - extern srs_bool srs_flv_is_eof(int error_code); - /* media codec */ - /** - * whether the video body is sequence header - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_data(). - */ - extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); - /** - * whether the video body is keyframe - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_data(). - */ - extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); - - /************************************************************* - ************************************************************** - * amf0 codec - * @example /trunk/research/librtmp/srs_ingest_flv.c - * @example /trunk/research/librtmp/srs_ingest_rtmp.c - ************************************************************** - *************************************************************/ - /* the output handler. */ - typedef double srs_amf0_number; - /** - * parse amf0 from data. - * @param nparsed, the parsed size, NULL to ignore. - * @return the parsed amf0 object. NULL for error. - * @remark user must free the parsed or created object by srs_amf0_free. - */ - extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); - extern srs_amf0_t srs_amf0_create_string(const char* value); - extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); - extern srs_amf0_t srs_amf0_create_ecma_array(); - extern srs_amf0_t srs_amf0_create_strict_array(); - extern srs_amf0_t srs_amf0_create_object(); - extern srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr); - extern void srs_amf0_free(srs_amf0_t amf0); - /* size and to bytes */ - extern int srs_amf0_size(srs_amf0_t amf0); - extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); - /* type detecter */ - extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); - extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); - /* value converter */ - extern const char* srs_amf0_to_string(srs_amf0_t amf0); - extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); - extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); - /* value setter */ - extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); - /* object value converter */ - extern int srs_amf0_object_property_count(srs_amf0_t amf0); - extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); - extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); - extern void srs_amf0_object_clear(srs_amf0_t amf0); - /* ecma array value converter */ - extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); - extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); - extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); - extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); - /* strict array value converter */ - extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); - extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); - extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); - - /************************************************************* - ************************************************************** - * utilities - ************************************************************** - *************************************************************/ - /** - * get the current system time in ms. - * use gettimeofday() to get system time. - */ - extern int64_t srs_utils_time_ms(); - - /** - * get the send bytes. - */ - extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp); - - /** - * get the recv bytes. - */ - extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp); - - /** - * parse the dts and pts by time in header and data in tag, - * or to parse the RTMP packet by srs_rtmp_read_packet(). - * - * @param time, the timestamp of tag, read by srs_flv_read_tag_header(). - * @param type, the type of tag, read by srs_flv_read_tag_header(). - * @param data, the data of tag, read by srs_flv_read_tag_data(). - * @param size, the size of tag, read by srs_flv_read_tag_header(). - * @param ppts, output the pts in ms, - * - * @return 0, success; otherswise, failed. - * @remark, the dts always equals to @param time. - * @remark, the pts=dts for audio or data. - * @remark, video only support h.264. - */ - extern int srs_utils_parse_timestamp(uint32_t time, char type, char* data, int size, uint32_t* ppts); - - /** - * whether the flv tag specified by param type is ok. - * @return true when tag is video/audio/script-data; otherwise, false. - */ - extern srs_bool srs_utils_flv_tag_is_ok(char type); - extern srs_bool srs_utils_flv_tag_is_audio(char type); - extern srs_bool srs_utils_flv_tag_is_video(char type); - extern srs_bool srs_utils_flv_tag_is_av(char type); - - /** - * get the CodecID of video tag. - * Codec Identifier. The following values are defined: - * 2 = Sorenson H.263 - * 3 = Screen video - * 4 = On2 VP6 - * 5 = On2 VP6 with alpha channel - * 6 = Screen video version 2 - * 7 = AVC - * @return the code id. 0 for error. - */ - extern char srs_utils_flv_video_codec_id(char* data, int size); - - /** - * get the AVCPacketType of video tag. - * The following values are defined: - * 0 = AVC sequence header - * 1 = AVC NALU - * 2 = AVC end of sequence (lower level NALU sequence ender is - * not required or supported) - * @return the avc packet type. -1(0xff) for error. - */ - extern char srs_utils_flv_video_avc_packet_type(char* data, int size); - - /** - * get the FrameType of video tag. - * Type of video frame. The following values are defined: - * 1 = key frame (for AVC, a seekable frame) - * 2 = inter frame (for AVC, a non-seekable frame) - * 3 = disposable inter frame (H.263 only) - * 4 = generated key frame (reserved for server use only) - * 5 = video info/command frame - * @return the frame type. 0 for error. - */ - extern char srs_utils_flv_video_frame_type(char* data, int size); - - /** - * get the SoundFormat of audio tag. - * Format of SoundData. The following values are defined: - * 0 = Linear PCM, platform endian - * 1 = ADPCM - * 2 = MP3 - * 3 = Linear PCM, little endian - * 4 = Nellymoser 16 kHz mono - * 5 = Nellymoser 8 kHz mono - * 6 = Nellymoser - * 7 = G.711 A-law logarithmic PCM - * 8 = G.711 mu-law logarithmic PCM - * 9 = reserved - * 10 = AAC - * 11 = Speex - * 14 = MP3 8 kHz - * 15 = Device-specific sound - * Formats 7, 8, 14, and 15 are reserved. - * AAC is supported in Flash Player 9,0,115,0 and higher. - * Speex is supported in Flash Player 10 and higher. - * @return the sound format. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_format(char* data, int size); - - /** - * get the SoundRate of audio tag. - * Sampling rate. The following values are defined: - * 0 = 5.5 kHz - * 1 = 11 kHz - * 2 = 22 kHz - * 3 = 44 kHz - * @return the sound rate. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_rate(char* data, int size); - - /** - * get the SoundSize of audio tag. - * Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 0 = 8-bit samples - * 1 = 16-bit samples - * @return the sound size. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_size(char* data, int size); - - /** - * get the SoundType of audio tag. - * Mono or stereo sound - * 0 = Mono sound - * 1 = Stereo sound - * @return the sound type. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_sound_type(char* data, int size); - - /** - * get the AACPacketType of audio tag. - * The following values are defined: - * 0 = AAC sequence header - * 1 = AAC raw - * @return the aac packet type. -1(0xff) for error. - */ - extern char srs_utils_flv_audio_aac_packet_type(char* data, int size); - - /************************************************************* - ************************************************************** - * human readable print. - ************************************************************** - *************************************************************/ - /** - * human readable print - * @param pdata, output the heap data, NULL to ignore. - * user must use srs_amf0_free_bytes to free it. - * @return return the *pdata for print. NULL to ignore. - */ - extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); - /** - * convert the flv tag type to string. - * SRS_RTMP_TYPE_AUDIO to "Audio" - * SRS_RTMP_TYPE_VIDEO to "Video" - * SRS_RTMP_TYPE_SCRIPT to "Data" - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_tag_type2string(char type); - - /** - * get the codec id string. - * H.263 = Sorenson H.263 - * Screen = Screen video - * VP6 = On2 VP6 - * VP6Alpha = On2 VP6 with alpha channel - * Screen2 = Screen video version 2 - * H.264 = AVC - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_codec_id2string(char codec_id); - - /** - * get the avc packet type string. - * SH = AVC sequence header - * Nalu = AVC NALU - * SpsPpsEnd = AVC end of sequence - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); - - /** - * get the frame type string. - * I = key frame (for AVC, a seekable frame) - * P/B = inter frame (for AVC, a non-seekable frame) - * DI = disposable inter frame (H.263 only) - * GI = generated key frame (reserved for server use only) - * VI = video info/command frame - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_video_frame_type2string(char frame_type); - - /** - * get the SoundFormat string. - * Format of SoundData. The following values are defined: - * LinearPCM = Linear PCM, platform endian - * ADPCM = ADPCM - * MP3 = MP3 - * LinearPCMLe = Linear PCM, little endian - * NellymoserKHz16 = Nellymoser 16 kHz mono - * NellymoserKHz8 = Nellymoser 8 kHz mono - * Nellymoser = Nellymoser - * G711APCM = G.711 A-law logarithmic PCM - * G711MuPCM = G.711 mu-law logarithmic PCM - * Reserved = reserved - * AAC = AAC - * Speex = Speex - * MP3KHz8 = MP3 8 kHz - * DeviceSpecific = Device-specific sound - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_format2string(char sound_format); - - /** - * get the SoundRate of audio tag. - * Sampling rate. The following values are defined: - * 5.5KHz = 5.5 kHz - * 11KHz = 11 kHz - * 22KHz = 22 kHz - * 44KHz = 44 kHz - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate); - - /** - * get the SoundSize of audio tag. - * Size of each audio sample. This parameter only pertains to - * uncompressed formats. Compressed formats always decode - * to 16 bits internally. - * 8bit = 8-bit samples - * 16bit = 16-bit samples - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_size2string(char sound_size); - - /** - * get the SoundType of audio tag. - * Mono or stereo sound - * Mono = Mono sound - * Stereo = Stereo sound - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_sound_type2string(char sound_type); - - /** - * get the AACPacketType of audio tag. - * The following values are defined: - * SH = AAC sequence header - * Raw = AAC raw - * otherwise, "Unknown" - * @remark user never free the return char*, - * it's static shared const string. - */ - extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type); - - /** - * Format the RTMP packet to human readable buffer. - * @return Whether parse RTMP packet ok. 0, success; otherwise, failed. - */ - extern int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size); - extern int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, - uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); - - /** - * Format current time to human readable string. - * @return A NULL terminated string. - */ - extern const char* srs_human_format_time(); + // The audio sample rate, it's SrsAudioSampleRate. + uint8_t sample_rate; + // The audio sound bits, it's SrsAudioSampleBits. + uint8_t sound_bits; + // The audio sound type, it's SrsAudioChannels. + uint8_t channels; + // The size of sample payload in bytes. + uint32_t nb_sample; + // The output sample data, user must free it by srs_mp4_free_sample. + uint8_t* sample; +} srs_mp4_sample_t; +/** + * Open mp4 file for muxer(write) or demuxer(read). + * @return A MP4 demuxer, NULL if failed. + */ +extern srs_mp4_t srs_mp4_open_read(const char* file); +/** + * Close the MP4 demuxer. + */ +extern void srs_mp4_close(srs_mp4_t mp4); +/** + * Initialize mp4 demuxer in non-seek mode. + * @remark Only support non-seek mode, that is fmp4 or moov before mdata. + * For the live streaming, we must feed stream frame by frame. + */ +extern int srs_mp4_init_demuxer(srs_mp4_t mp4); +/** + * Read a sample form mp4. + * @remark User can use srs_mp4_sample_to_flv_tag to convert mp4 sampel to flv tag. + * Use the srs_mp4_to_flv_tag_size to calc the flv tag data size to alloc. + */ +extern int srs_mp4_read_sample(srs_mp4_t mp4, srs_mp4_sample_t* sample); +/** + * Free the allocated mp4 sample. + */ +extern void srs_mp4_free_sample(srs_mp4_sample_t* sample); +/** + * Calc the size of flv tag, for the mp4 sample to convert to. + */ +extern int32_t srs_mp4_sizeof(srs_mp4_t mp4, srs_mp4_sample_t* sample); +/** + * Covert mp4 sample to flv tag. + */ +extern int srs_mp4_to_flv_tag(srs_mp4_t mp4, srs_mp4_sample_t* sample, char* type, uint32_t* time, char* data, int32_t size); +/* error code */ +/* whether the error code indicates EOF */ +extern srs_bool srs_mp4_is_eof(int error_code); + +/************************************************************* + ************************************************************** + * flv codec + * @example /trunk/research/librtmp/srs_flv_injecter.c + * @example /trunk/research/librtmp/srs_flv_parser.c + * @example /trunk/research/librtmp/srs_ingest_flv.c + * @example /trunk/research/librtmp/srs_ingest_rtmp.c + ************************************************************** + *************************************************************/ +typedef void* srs_flv_t; +/** + * Open FLV file in demux mode. + * @return A FLV demuxer, NULL if failed. + */ +extern srs_flv_t srs_flv_open_read(const char* file); +/** + * Open FlV file in mux mode. + * @return A FLV muxer, NULL if failed. + */ +extern srs_flv_t srs_flv_open_write(const char* file); +/** + * Close the FLV demuxer or muxer. + */ +extern void srs_flv_close(srs_flv_t flv); +/** + * read the flv header. 9bytes header. + * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. + * 3bytes, signature, "FLV", + * 1bytes, version, 0x01, + * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. + * 4bytes, dataoffset, 0x09, The length of this header in bytes + * + * @return 0, success; otherswise, failed. + * @remark, drop the 4bytes zero previous tag size. + */ +extern int srs_flv_read_header(srs_flv_t flv, char header[9]); +/** + * read the flv tag header, 1bytes tag, 3bytes data_size, + * 4bytes time, 3bytes stream id. + * @param ptype, output the type of tag, macros: + * SRS_RTMP_TYPE_AUDIO, FlvTagAudio + * SRS_RTMP_TYPE_VIDEO, FlvTagVideo + * SRS_RTMP_TYPE_SCRIPT, FlvTagScript + * @param pdata_size, output the size of tag data. + * @param ptime, output the time of tag, the dts in ms. + * + * @return 0, success; otherswise, failed. + * @remark, user must ensure the next is a tag, srs never check it. + */ +extern int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, uint32_t* ptime); +/** + * read the tag data. drop the 4bytes previous tag size + * @param data, the data to read, user alloc and free it. + * @param size, the size of data to read, get by srs_flv_read_tag_header(). + * @remark, srs will ignore and drop the 4bytes previous tag size. + */ +extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); +/** + * write the flv header. 9bytes header. + * @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc. + * 3bytes, signature, "FLV", + * 1bytes, version, 0x01, + * 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present. + * 4bytes, dataoffset, 0x09, The length of this header in bytes + * + * @return 0, success; otherswise, failed. + * @remark, auto write the 4bytes zero previous tag size. + */ +extern int srs_flv_write_header(srs_flv_t flv, char header[9]); +/** + * write the flv tag to file. + * + * @return 0, success; otherswise, failed. + * @remark, auto write the 4bytes zero previous tag size. + */ +/* write flv tag to file, auto write the 4bytes previous tag size */ +extern int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); +/** + * get the tag size, for flv injecter to adjust offset, + * size = tag_header(11B) + data_size + previous_tag(4B) + * @return the size of tag. + */ +extern int srs_flv_size_tag(int data_size); +/* file stream */ +/* file stream tellg to get offset */ +extern int64_t srs_flv_tellg(srs_flv_t flv); +/* seek file stream, offset is form the start of file */ +extern void srs_flv_lseek(srs_flv_t flv, int64_t offset); +/* error code */ +/* whether the error code indicates EOF */ +extern srs_bool srs_flv_is_eof(int error_code); +/* media codec */ +/** + * whether the video body is sequence header + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_data(). + */ +extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size); +/** + * whether the video body is keyframe + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_data(). + */ +extern srs_bool srs_flv_is_keyframe(char* data, int32_t size); + +/************************************************************* + ************************************************************** + * amf0 codec + * @example /trunk/research/librtmp/srs_ingest_flv.c + * @example /trunk/research/librtmp/srs_ingest_rtmp.c + ************************************************************** + *************************************************************/ +/* the output handler. */ +typedef double srs_amf0_number; +/** + * parse amf0 from data. + * @param nparsed, the parsed size, NULL to ignore. + * @return the parsed amf0 object. NULL for error. + * @remark user must free the parsed or created object by srs_amf0_free. + */ +extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed); +extern srs_amf0_t srs_amf0_create_string(const char* value); +extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value); +extern srs_amf0_t srs_amf0_create_ecma_array(); +extern srs_amf0_t srs_amf0_create_strict_array(); +extern srs_amf0_t srs_amf0_create_object(); +extern srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr); +extern void srs_amf0_free(srs_amf0_t amf0); +/* size and to bytes */ +extern int srs_amf0_size(srs_amf0_t amf0); +extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size); +/* type detecter */ +extern srs_bool srs_amf0_is_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_number(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_null(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_object(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0); +/* value converter */ +extern const char* srs_amf0_to_string(srs_amf0_t amf0); +extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0); +extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0); +/* value setter */ +extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value); +/* object value converter */ +extern int srs_amf0_object_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +extern void srs_amf0_object_clear(srs_amf0_t amf0); +/* ecma array value converter */ +extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); +extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); +extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); +extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +/* strict array value converter */ +extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0); +extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); +extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value); + +/************************************************************* + ************************************************************** + * utilities + ************************************************************** + *************************************************************/ +/** + * get the current system time in ms. + * use gettimeofday() to get system time. + */ +extern int64_t srs_utils_time_ms(); + +/** + * get the send bytes. + */ +extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp); + +/** + * get the recv bytes. + */ +extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp); + +/** + * parse the dts and pts by time in header and data in tag, + * or to parse the RTMP packet by srs_rtmp_read_packet(). + * + * @param time, the timestamp of tag, read by srs_flv_read_tag_header(). + * @param type, the type of tag, read by srs_flv_read_tag_header(). + * @param data, the data of tag, read by srs_flv_read_tag_data(). + * @param size, the size of tag, read by srs_flv_read_tag_header(). + * @param ppts, output the pts in ms, + * + * @return 0, success; otherswise, failed. + * @remark, the dts always equals to @param time. + * @remark, the pts=dts for audio or data. + * @remark, video only support h.264. + */ +extern int srs_utils_parse_timestamp(uint32_t time, char type, char* data, int size, uint32_t* ppts); + +/** + * whether the flv tag specified by param type is ok. + * @return true when tag is video/audio/script-data; otherwise, false. + */ +extern srs_bool srs_utils_flv_tag_is_ok(char type); +extern srs_bool srs_utils_flv_tag_is_audio(char type); +extern srs_bool srs_utils_flv_tag_is_video(char type); +extern srs_bool srs_utils_flv_tag_is_av(char type); + +/** + * get the CodecID of video tag. + * Codec Identifier. The following values are defined: + * 2 = Sorenson H.263 + * 3 = Screen video + * 4 = On2 VP6 + * 5 = On2 VP6 with alpha channel + * 6 = Screen video version 2 + * 7 = AVC + * @return the code id. 0 for error. + */ +extern char srs_utils_flv_video_codec_id(char* data, int size); + +/** + * get the AVCPacketType of video tag. + * The following values are defined: + * 0 = AVC sequence header + * 1 = AVC NALU + * 2 = AVC end of sequence (lower level NALU sequence ender is + * not required or supported) + * @return the avc packet type. -1(0xff) for error. + */ +extern char srs_utils_flv_video_avc_packet_type(char* data, int size); + +/** + * get the FrameType of video tag. + * Type of video frame. The following values are defined: + * 1 = key frame (for AVC, a seekable frame) + * 2 = inter frame (for AVC, a non-seekable frame) + * 3 = disposable inter frame (H.263 only) + * 4 = generated key frame (reserved for server use only) + * 5 = video info/command frame + * @return the frame type. 0 for error. + */ +extern char srs_utils_flv_video_frame_type(char* data, int size); + +/** + * get the SoundFormat of audio tag. + * Format of SoundData. The following values are defined: + * 0 = Linear PCM, platform endian + * 1 = ADPCM + * 2 = MP3 + * 3 = Linear PCM, little endian + * 4 = Nellymoser 16 kHz mono + * 5 = Nellymoser 8 kHz mono + * 6 = Nellymoser + * 7 = G.711 A-law logarithmic PCM + * 8 = G.711 mu-law logarithmic PCM + * 9 = reserved + * 10 = AAC + * 11 = Speex + * 14 = MP3 8 kHz + * 15 = Device-specific sound + * Formats 7, 8, 14, and 15 are reserved. + * AAC is supported in Flash Player 9,0,115,0 and higher. + * Speex is supported in Flash Player 10 and higher. + * @return the sound format. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_format(char* data, int size); + +/** + * get the SoundRate of audio tag. + * Sampling rate. The following values are defined: + * 0 = 5.5 kHz + * 1 = 11 kHz + * 2 = 22 kHz + * 3 = 44 kHz + * @return the sound rate. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_rate(char* data, int size); + +/** + * get the SoundSize of audio tag. + * Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 0 = 8-bit samples + * 1 = 16-bit samples + * @return the sound size. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_size(char* data, int size); + +/** + * get the SoundType of audio tag. + * Mono or stereo sound + * 0 = Mono sound + * 1 = Stereo sound + * @return the sound type. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_sound_type(char* data, int size); + +/** + * get the AACPacketType of audio tag. + * The following values are defined: + * 0 = AAC sequence header + * 1 = AAC raw + * @return the aac packet type. -1(0xff) for error. + */ +extern char srs_utils_flv_audio_aac_packet_type(char* data, int size); + +/************************************************************* + ************************************************************** + * human readable print. + ************************************************************** + *************************************************************/ +/** + * human readable print + * @param pdata, output the heap data, NULL to ignore. + * user must use srs_amf0_free_bytes to free it. + * @return return the *pdata for print. NULL to ignore. + */ +extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize); +/** + * convert the flv tag type to string. + * SRS_RTMP_TYPE_AUDIO to "Audio" + * SRS_RTMP_TYPE_VIDEO to "Video" + * SRS_RTMP_TYPE_SCRIPT to "Data" + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_tag_type2string(char type); + +/** + * get the codec id string. + * H.263 = Sorenson H.263 + * Screen = Screen video + * VP6 = On2 VP6 + * VP6Alpha = On2 VP6 with alpha channel + * Screen2 = Screen video version 2 + * H.264 = AVC + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_codec_id2string(char codec_id); + +/** + * get the avc packet type string. + * SH = AVC sequence header + * Nalu = AVC NALU + * SpsPpsEnd = AVC end of sequence + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type); + +/** + * get the frame type string. + * I = key frame (for AVC, a seekable frame) + * P/B = inter frame (for AVC, a non-seekable frame) + * DI = disposable inter frame (H.263 only) + * GI = generated key frame (reserved for server use only) + * VI = video info/command frame + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_video_frame_type2string(char frame_type); + +/** + * get the SoundFormat string. + * Format of SoundData. The following values are defined: + * LinearPCM = Linear PCM, platform endian + * ADPCM = ADPCM + * MP3 = MP3 + * LinearPCMLe = Linear PCM, little endian + * NellymoserKHz16 = Nellymoser 16 kHz mono + * NellymoserKHz8 = Nellymoser 8 kHz mono + * Nellymoser = Nellymoser + * G711APCM = G.711 A-law logarithmic PCM + * G711MuPCM = G.711 mu-law logarithmic PCM + * Reserved = reserved + * AAC = AAC + * Speex = Speex + * MP3KHz8 = MP3 8 kHz + * DeviceSpecific = Device-specific sound + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_format2string(char sound_format); + +/** + * get the SoundRate of audio tag. + * Sampling rate. The following values are defined: + * 5.5KHz = 5.5 kHz + * 11KHz = 11 kHz + * 22KHz = 22 kHz + * 44KHz = 44 kHz + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate); + +/** + * get the SoundSize of audio tag. + * Size of each audio sample. This parameter only pertains to + * uncompressed formats. Compressed formats always decode + * to 16 bits internally. + * 8bit = 8-bit samples + * 16bit = 16-bit samples + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_size2string(char sound_size); + +/** + * get the SoundType of audio tag. + * Mono or stereo sound + * Mono = Mono sound + * Stereo = Stereo sound + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_sound_type2string(char sound_type); + +/** + * get the AACPacketType of audio tag. + * The following values are defined: + * SH = AAC sequence header + * Raw = AAC raw + * otherwise, "Unknown" + * @remark user never free the return char*, + * it's static shared const string. + */ +extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type); + +/** + * Format the RTMP packet to human readable buffer. + * @return Whether parse RTMP packet ok. 0, success; otherwise, failed. + */ +extern int srs_human_format_rtmp_packet(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size); +extern int srs_human_format_rtmp_packet2(char* buffer, int nb_buffer, char type, uint32_t timestamp, char* data, int size, + uint32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); + +/** + * Format current time to human readable string. + * @return A NULL terminated string. + */ +extern const char* srs_human_format_time(); + #ifndef _WIN32 - // for getpid. +// for getpid. #include #endif - // The log function for librtmp. - // User can disable it by define macro SRS_DISABLE_LOG. - // Or user can directly use them, or define the alias by: - // #define trace(msg, ...) srs_human_trace(msg, ##__VA_ARGS__) - // #define warn(msg, ...) srs_human_warn(msg, ##__VA_ARGS__) - // #define error(msg, ...) srs_human_error(msg, ##__VA_ARGS__) +// The log function for librtmp. +// User can disable it by define macro SRS_DISABLE_LOG. +// Or user can directly use them, or define the alias by: +// #define trace(msg, ...) srs_human_trace(msg, ##__VA_ARGS__) +// #define warn(msg, ...) srs_human_warn(msg, ##__VA_ARGS__) +// #define error(msg, ...) srs_human_error(msg, ##__VA_ARGS__) #ifdef SRS_DISABLE_LOG #define srs_human_trace(msg, ...) (void)0 #define srs_human_warn(msg, ...) (void)0 @@ -1066,130 +1066,130 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define srs_human_verbose(msg, ...) (void)0 #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__) #endif - - /************************************************************* - ************************************************************** - * IO hijack, use your specified io functions. - ************************************************************** - *************************************************************/ - // the void* will convert to your handler for io hijack. - typedef void* srs_hijack_io_t; + +/************************************************************* + ************************************************************** + * IO hijack, use your specified io functions. + ************************************************************** + *************************************************************/ +// the void* will convert to your handler for io hijack. +typedef void* srs_hijack_io_t; #ifdef SRS_HIJACK_IO #ifndef _WIN32 - // for iovec. +// for iovec. #include #endif - /** - * get the hijack io object in rtmp protocol sdk. - * @remark, user should never provides this method, srs-librtmp provides it. - */ - extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp); +/** + * get the hijack io object in rtmp protocol sdk. + * @remark, user should never provides this method, srs-librtmp provides it. + */ +extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp); #endif - // define the following macro and functions in your module to hijack the io. - // the example @see https://github.com/ossrs/srs-bench - // which use librtmp but use its own io(use st also). +// define the following macro and functions in your module to hijack the io. +// the example @see https://github.com/ossrs/srs-bench +// which use librtmp but use its own io(use st also). #ifdef SRS_HIJACK_IO - /** - * create hijack. - * @return NULL for error; otherwise, ok. - */ - extern srs_hijack_io_t srs_hijack_io_create(); - /** - * destroy the context, user must close the socket. - */ - extern void srs_hijack_io_destroy(srs_hijack_io_t ctx); - /** - * create socket, not connect yet. - * @param owner, the rtmp context which create this socket. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx, srs_rtmp_t owner); - /** - * connect socket at server_ip:port. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port); - /** - * read from socket. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); - /** - * set the socket recv timeout in ms. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * get the socket recv timeout. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx); - /** - * get the socket recv bytes. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx); - /** - * set the socket send timeout in ms. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * get the socket send timeout. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx); - /** - * get the socket send bytes. - * @return 0, success; otherswise, failed. - */ - extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx); - /** - * writev of socket. - * @return 0, success; otherswise, failed. - * @remark We assume that the writev always write all data to peer, like what ST or block-socket done. - */ - extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite); - /** - * whether the timeout is never timeout in ms. - * @return 0, with timeout specified; otherwise, never timeout. - */ - extern int srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t tm); - /** - * read fully, fill the buf exactly size bytes. - * @return 0, success; otherswise, failed. - */ - extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); - /** - * write bytes to socket. - * @return 0, success; otherswise, failed. - * @remark We assume that the write always write all data to peer, like what ST or block-socket done. - */ - extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); +/** + * create hijack. + * @return NULL for error; otherwise, ok. + */ +extern srs_hijack_io_t srs_hijack_io_create(); +/** + * destroy the context, user must close the socket. + */ +extern void srs_hijack_io_destroy(srs_hijack_io_t ctx); +/** + * create socket, not connect yet. + * @param owner, the rtmp context which create this socket. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx, srs_rtmp_t owner); +/** + * connect socket at server_ip:port. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port); +/** + * read from socket. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); +/** + * set the socket recv timeout in ms. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * get the socket recv timeout. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx); +/** + * get the socket recv bytes. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx); +/** + * set the socket send timeout in ms. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * get the socket send timeout. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx); +/** + * get the socket send bytes. + * @return 0, success; otherswise, failed. + */ +extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx); +/** + * writev of socket. + * @return 0, success; otherswise, failed. + * @remark We assume that the writev always write all data to peer, like what ST or block-socket done. + */ +extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite); +/** + * whether the timeout is never timeout in ms. + * @return 0, with timeout specified; otherwise, never timeout. + */ +extern int srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t tm); +/** + * read fully, fill the buf exactly size bytes. + * @return 0, success; otherswise, failed. + */ +extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread); +/** + * write bytes to socket. + * @return 0, success; otherswise, failed. + * @remark We assume that the write always write all data to peer, like what ST or block-socket done. + */ +extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite); #endif - - /************************************************************* - ************************************************************** - * Windows SRS-LIBRTMP solution - ************************************************************** - *************************************************************/ - // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 + +/************************************************************* + ************************************************************** + * Windows SRS-LIBRTMP solution + ************************************************************** + *************************************************************/ +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 - // for time. +// for time. #define _CRT_SECURE_NO_WARNINGS #include - int gettimeofday(struct timeval* tv, struct timezone* tz); +int gettimeofday(struct timeval* tv, struct timezone* tz); #define PRId64 "lld" - - // for inet helpers. - typedef int socklen_t; - const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); - - // for mkdir(). + +// for inet helpers. +typedef int socklen_t; +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + +// for mkdir(). #include - - // for open(). - typedef int mode_t; + +// for open(). +typedef int mode_t; #define S_IRUSR 0 #define S_IWUSR 0 #define S_IXUSR 0 @@ -1198,8 +1198,8 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define S_IXGRP 0 #define S_IROTH 0 #define S_IXOTH 0 - - // for file seek. + +// for file seek. #include #include #define open _open @@ -1207,45 +1207,45 @@ fprintf(stderr, " (%s)\n", strerror(errno)) #define lseek _lseek #define write _write #define read _read - - // for socket. - ssize_t writev(int fd, const struct iovec *iov, int iovcnt); - typedef int64_t useconds_t; - int usleep(useconds_t usec); - int socket_setup(); - int socket_cleanup(); - - // others. + +// for socket. +ssize_t writev(int fd, const struct iovec *iov, int iovcnt); +typedef int64_t useconds_t; +int usleep(useconds_t usec); +int socket_setup(); +int socket_cleanup(); + +// others. #define snprintf _snprintf #endif - - /************************************************************* - ************************************************************* - * Deprecated APIs, maybe removed in future versions. - ************************************************************* - *************************************************************/ - /** - * Deprecated, for bandwidth test check only. - */ - extern srs_rtmp_t srs_rtmp_create2(const char* url); - - /** - * Deprecated, use seperate function to retrieve information from rtmp, - * for example, use srs_rtmp_get_server_ip to get server ip. - */ - extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, - char srs_server_ip[128], char srs_server[128], - char srs_primary[128], char srs_authors[128], - char srs_version[32], int* srs_id, int* srs_pid); - - /** - * Deprecated, use srs_human_format_rtmp_packet instead. - */ - extern int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size); - extern int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp); - extern int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now); - extern int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, - int64_t starttime, int64_t nb_packets); + +/************************************************************* + ************************************************************* + * Deprecated APIs, maybe removed in future versions. + ************************************************************* + *************************************************************/ +/** + * Deprecated, for bandwidth test check only. + */ +extern srs_rtmp_t srs_rtmp_create2(const char* url); + +/** + * Deprecated, use seperate function to retrieve information from rtmp, + * for example, use srs_rtmp_get_server_ip to get server ip. + */ +extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp, + char srs_server_ip[128], char srs_server[128], + char srs_primary[128], char srs_authors[128], + char srs_version[32], int* srs_id, int* srs_pid); + +/** + * Deprecated, use srs_human_format_rtmp_packet instead. + */ +extern int srs_human_print_rtmp_packet(char type, uint32_t timestamp, char* data, int size); +extern int srs_human_print_rtmp_packet2(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp); +extern int srs_human_print_rtmp_packet3(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now); +extern int srs_human_print_rtmp_packet4(char type, uint32_t timestamp, char* data, int size, uint32_t pre_timestamp, int64_t pre_now, + int64_t starttime, int64_t nb_packets); #ifdef __cplusplus }