diff --git a/trunk/configure b/trunk/configure index bea407f07..c7635ecc5 100755 --- a/trunk/configure +++ b/trunk/configure @@ -375,7 +375,8 @@ MODULE_ID="RTMP" MODULE_DEPENDS=("CORE" "KERNEL") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_rtmp_amf0" "srs_rtmp_io" "srs_rtmp_stack" "srs_rtmp_sdk" - "srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_rtmp_buffer") + "srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_rtmp_buffer" + "srs_raw_avc") RTMP_INCS="src/protocol"; MODULE_DIR=${RTMP_INCS} . auto/modules.sh RTMP_OBJS="${MODULE_OBJS[@]}" # diff --git a/trunk/ide/srs_upp/srs_upp.upp b/trunk/ide/srs_upp/srs_upp.upp index 1694cb6f6..138cd3081 100755 --- a/trunk/ide/srs_upp/srs_upp.upp +++ b/trunk/ide/srs_upp/srs_upp.upp @@ -45,6 +45,8 @@ file ../../src/kernel/srs_kernel_utility.hpp, ../../src/kernel/srs_kernel_utility.cpp, protocol readonly separator, + ../../src/protocol/srs_raw_avc.hpp, + ../../src/protocol/srs_raw_avc.cpp, ../../src/protocol/srs_rtmp_amf0.hpp, ../../src/protocol/srs_rtmp_amf0.cpp, ../../src/protocol/srs_rtmp_buffer.hpp, diff --git a/trunk/ide/srs_vs2010/srs.vcxproj b/trunk/ide/srs_vs2010/srs.vcxproj index 2a435734e..0c0afb301 100755 --- a/trunk/ide/srs_vs2010/srs.vcxproj +++ b/trunk/ide/srs_vs2010/srs.vcxproj @@ -36,7 +36,7 @@ - + $(ProjectDir)/../../src/core;$(ProjectDir)/../../src/kernel;$(ProjectDir)/../../src/protocol;$(ProjectDir)/../../src/app;$(ProjectDir)/../../src/libs;$(ProjectDir)/../../objs;$(IncludePath) @@ -116,6 +116,7 @@ + @@ -193,6 +194,7 @@ + diff --git a/trunk/ide/srs_vs2010/srs.vcxproj.filters b/trunk/ide/srs_vs2010/srs.vcxproj.filters index 6b03987fb..a592ec709 100755 --- a/trunk/ide/srs_vs2010/srs.vcxproj.filters +++ b/trunk/ide/srs_vs2010/srs.vcxproj.filters @@ -226,6 +226,9 @@ srs + + srs + @@ -414,6 +417,9 @@ srs + + srs + diff --git a/trunk/src/app/srs_app_edge.cpp b/trunk/src/app/srs_app_edge.cpp index d69a50238..2f65cd48f 100644 --- a/trunk/src/app/srs_app_edge.cpp +++ b/trunk/src/app/srs_app_edge.cpp @@ -447,7 +447,7 @@ int SrsEdgeForwarder::start() } if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { - srs_error("connect with server failed, stream=%s, stream_id=%d. ret=%d", + srs_error("publish failed, stream=%s, stream_id=%d. ret=%d", req->stream.c_str(), stream_id, ret); return ret; } diff --git a/trunk/src/app/srs_app_mpegts_udp.cpp b/trunk/src/app/srs_app_mpegts_udp.cpp index e510ea736..f13dc6d39 100644 --- a/trunk/src/app/srs_app_mpegts_udp.cpp +++ b/trunk/src/app/srs_app_mpegts_udp.cpp @@ -23,6 +23,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#ifdef SRS_AUTO_STREAM_CASTER + +#include #include #include #include @@ -39,8 +42,12 @@ using namespace std; #include #include #include - -#ifdef SRS_AUTO_STREAM_CASTER +#include +#include +#include +#include +#include +#include ISrsUdpHandler::ISrsUdpHandler() { @@ -56,13 +63,25 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) context = new SrsTsContext(); buffer = new SrsSimpleBuffer(); output = _srs_config->get_stream_caster_output(c); + req = NULL; + io = NULL; + client = NULL; + stfd = NULL; + stream_id = 0; + avc = new SrsRawH264Stream(); + h264_sps_changed = false; + h264_pps_changed = false; + h264_sps_pps_sent = false; } SrsMpegtsOverUdp::~SrsMpegtsOverUdp() { + close(); + srs_freep(buffer); srs_freep(stream); srs_freep(context); + srs_freep(avc); } int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) @@ -207,8 +226,311 @@ int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg) return ret; } + // check supported codec + if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamAudioAAC) { + ret = ERROR_STREAM_CASTER_TS_CODEC; + srs_error("mpegts: unsupported stream codec=%d. ret=%d", msg->channel->stream, ret); + return ret; + } + + // parse the stream. + SrsStream avs; + if ((ret = avs.initialize(msg->payload->bytes(), msg->payload->length())) != ERROR_SUCCESS) { + srs_error("mpegts: initialize av stream failed. ret=%d", ret); + return ret; + } + + // publish audio or video. + if (msg->channel->stream == SrsTsStreamVideoH264) { + return on_ts_video(msg, &avs); + } + // TODO: FIXME: implements it. return ret; } +int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs) +{ + int ret = ERROR_SUCCESS; + + // ensure rtmp connected. + if ((ret = connect()) != ERROR_SUCCESS) { + return ret; + } + + // ts tbn to flv tbn. + u_int32_t dts = msg->dts / 90; + u_int32_t pts = msg->dts / 90; + + // send each frame. + while (!avs->empty()) { + char* frame = NULL; + int frame_size = 0; + if ((ret = avc->annexb_demux(avs, &frame, &frame_size)) != ERROR_SUCCESS) { + return ret; + } + + // ignore invalid frame, + // * atleast 1bytes for SPS to decode the type + // * ignore the auth bytes '09f0' + if (frame_size <= 2) { + continue; + } + + // it may be return error, but we must process all packets. + if ((ret = write_h264_raw_frame(frame, frame_size, dts, pts)) != ERROR_SUCCESS) { + if (ret = ERROR_H264_DROP_BEFORE_SPS_PPS) { + continue; + } + return ret; + } + } + + return ret; +} + +int SrsMpegtsOverUdp::write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) +{ + int ret = ERROR_SUCCESS; + + // for sps + if (avc->is_sps(frame, frame_size)) { + std::string sps; + if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { + return ret; + } + + if (h264_sps == sps) { + return ret; + } + h264_sps_changed = true; + h264_sps = sps; + + return write_h264_sps_pps(dts, pts); + } + + // for pps + if (avc->is_pps(frame, frame_size)) { + std::string pps; + if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { + return ret; + } + + if (h264_pps == pps) { + return ret; + } + h264_pps_changed = true; + h264_pps = pps; + + return write_h264_sps_pps(dts, pts); + } + + // ibp frame. + return write_h264_ipb_frame(frame, frame_size, dts, pts); +} + +int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts) +{ + int ret = ERROR_SUCCESS; + + // only send when both sps and pps changed. + if (!h264_sps_changed || !h264_pps_changed) { + return ret; + } + + // h264 raw to h264 packet. + std::string sh; + if ((ret = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != ERROR_SUCCESS) { + return ret; + } + + // h264 packet to flv packet. + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; + char* flv = NULL; + int nb_flv = 0; + if ((ret = avc->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { + return ret; + } + + // reset sps and pps. + h264_sps_changed = false; + h264_pps_changed = false; + h264_sps_pps_sent = true; + + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; + return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); +} + +int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) +{ + int ret = ERROR_SUCCESS; + + // when sps or pps not sent, ignore the packet. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 + if (!h264_sps_pps_sent) { + return ERROR_H264_DROP_BEFORE_SPS_PPS; + } + + std::string ibp; + int8_t frame_type; + if ((ret = avc->mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) { + return ret; + } + + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; + char* flv = NULL; + int nb_flv = 0; + if ((ret = avc->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { + return ret; + } + + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; + return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); +} + +int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size) +{ + int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = NULL; + + if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, stream_id, &msg)) != ERROR_SUCCESS) { + return ret; + } + + srs_assert(msg); + + // send out encoded msg. + if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsMpegtsOverUdp::connect() +{ + int ret = ERROR_SUCCESS; + + // when ok, ignore. + if (io || client) { + return ret; + } + + // parse uri + if (!req) { + req = new SrsRequest(); + + size_t pos = string::npos; + string uri = req->tcUrl = output; + + // tcUrl, stream + if ((pos = uri.rfind("/")) != string::npos) { + req->stream = uri.substr(pos + 1); + req->tcUrl = uri = uri.substr(0, pos); + } + + srs_discovery_tc_url(req->tcUrl, + req->schema, req->host, req->vhost, req->app, req->port, + req->param); + } + + // connect host. + if ((ret = srs_socket_connect(req->host, ::atoi(req->port.c_str()), ST_UTIME_NO_TIMEOUT, &stfd)) != ERROR_SUCCESS) { + srs_error("mpegts: connect server %s:%s failed. ret=%d", req->host.c_str(), req->port.c_str(), ret); + return ret; + } + io = new SrsStSocket(stfd); + client = new SrsRtmpClient(io); + + client->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); + client->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); + + // connect to vhost/app + if ((ret = client->handshake()) != ERROR_SUCCESS) { + srs_error("mpegts: handshake with server failed. ret=%d", ret); + return ret; + } + if ((ret = connect_app(req->host, req->port)) != ERROR_SUCCESS) { + srs_error("mpegts: connect with server failed. ret=%d", ret); + return ret; + } + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { + srs_error("mpegts: connect with server failed, stream_id=%d. ret=%d", stream_id, ret); + return ret; + } + + // publish. + if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { + srs_error("mpegts: publish failed, stream=%s, stream_id=%d. ret=%d", + req->stream.c_str(), stream_id, ret); + return ret; + } + + + return ret; +} + +// TODO: FIXME: refine the connect_app. +int SrsMpegtsOverUdp::connect_app(string ep_server, string ep_port) +{ + int ret = ERROR_SUCCESS; + + // args of request takes the srs info. + if (req->args == NULL) { + req->args = SrsAmf0Any::object(); + } + + // notify server the edge identity, + // @see https://github.com/winlinvip/simple-rtmp-server/issues/147 + SrsAmf0Object* data = req->args; + data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); + data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); + data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); + data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); + // for edge to directly get the id of client. + data->set("srs_pid", SrsAmf0Any::number(getpid())); + data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); + + // local ip of edge + std::vector ips = srs_get_local_ipv4_ips(); + assert(_srs_config->get_stats_network() < (int)ips.size()); + std::string local_ip = ips[_srs_config->get_stats_network()]; + data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str())); + + // generate the tcUrl + std::string param = ""; + std::string tc_url = srs_generate_tc_url(ep_server, req->vhost, req->app, ep_port, param); + + // upnode server identity will show in the connect_app of client. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/160 + // the debug_srs_upnode is config in vhost and default to true. + bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost); + if ((ret = client->connect_app(req->app, tc_url, req, debug_srs_upnode)) != ERROR_SUCCESS) { + srs_error("mpegts: connect with server failed, tcUrl=%s, dsu=%d. ret=%d", + tc_url.c_str(), debug_srs_upnode, ret); + return ret; + } + + return ret; +} + +void SrsMpegtsOverUdp::close() +{ + srs_freep(client); + srs_freep(io); + srs_freep(req); + srs_close_stfd(stfd); +} + #endif diff --git a/trunk/src/app/srs_app_mpegts_udp.hpp b/trunk/src/app/srs_app_mpegts_udp.hpp index efca95c75..f51860e86 100644 --- a/trunk/src/app/srs_app_mpegts_udp.hpp +++ b/trunk/src/app/srs_app_mpegts_udp.hpp @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#ifdef SRS_AUTO_STREAM_CASTER + class sockaddr_in; #include @@ -37,9 +39,12 @@ class SrsStream; class SrsTsContext; class SrsConfDirective; class SrsSimpleBuffer; +class SrsRtmpClient; +class SrsStSocket; +class SrsRequest; +class SrsRawH264Stream; -#ifdef SRS_AUTO_STREAM_CASTER - +#include #include /** @@ -74,6 +79,19 @@ private: SrsTsContext* context; SrsSimpleBuffer* buffer; std::string output; +private: + SrsRequest* req; + st_netfd_t stfd; + SrsStSocket* io; + SrsRtmpClient* client; + int stream_id; +private: + SrsRawH264Stream* avc; + std::string h264_sps; + bool h264_sps_changed; + std::string h264_pps; + bool h264_pps_changed; + bool h264_sps_pps_sent; public: SrsMpegtsOverUdp(SrsConfDirective* c); virtual ~SrsMpegtsOverUdp(); @@ -83,6 +101,19 @@ public: // interface ISrsTsHandler public: virtual int on_ts_message(SrsTsMessage* msg); +private: + virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs); + virtual int write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); + virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); + virtual int write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); + virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size); +private: + // connect to rtmp output url. + // @remark ignore when not connected, reconnect when disconnected. + virtual int connect(); + virtual int connect_app(std::string ep_server, std::string ep_port); + // close the connected io and rtmp to ready to be re-connect. + virtual void close(); }; #endif diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 740f71905..a2ce31ebc 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 109 +#define VERSION_REVISION 110 // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index 3733da045..8ee98f3e5 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -53,7 +53,7 @@ enum SrsCodecAudioType // 5 = video info/command frame enum SrsCodecVideoAVCFrame { - // set to the max value to reserved, for array map. + // set to the zero to reserved, for array map. SrsCodecVideoAVCFrameReserved = 0, SrsCodecVideoAVCFrameReserved1 = 6, @@ -91,7 +91,7 @@ enum SrsCodecVideoAVCType // 7 = AVC enum SrsCodecVideo { - // set to the max value to reserved, for array map. + // set to the zero to reserved, for array map. SrsCodecVideoReserved = 0, SrsCodecVideoReserved1 = 1, SrsCodecVideoReserved2 = 8, @@ -163,6 +163,22 @@ enum SrsCodecAudioSampleRate SrsCodecAudioSampleRate44100 = 3, }; +/** +* E.4.1 FLV Tag, page 75 +*/ +enum SrsCodecFlvTag +{ + // set to the zero to reserved, for array map. + SrsCodecFlvTagReserved = 0, + + // 8 = audio + SrsCodecFlvTagAudio = 8, + // 9 = video + SrsCodecFlvTagVideo = 9, + // 18 = script data + SrsCodecFlvTagScript = 18, +}; + /** * Annex E. The FLV File Format * @see SrsAvcAacCodec for the media stream codec. diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index f97443b60..32b9202ff 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -228,6 +228,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_STREAM_CASTER_TS_PMT 4018 #define ERROR_STREAM_CASTER_TS_PSE 4019 #define ERROR_STREAM_CASTER_TS_ES 4020 +#define ERROR_STREAM_CASTER_TS_CODEC 4021 +#define ERROR_STREAM_CASTER_AVC_SPS 4022 +#define ERROR_STREAM_CASTER_AVC_PPS 4023 +#define ERROR_STREAM_CASTER_FLV_TAG 4024 /** * whether the error code is an system control error. diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 76eaf56e3..90523e7dc 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -47,6 +47,7 @@ using namespace std; #include #include #include +#include // kernel module. ISrsLog* _srs_log = new ISrsLog(); @@ -80,6 +81,9 @@ struct Context SimpleSocketStream* skt; int stream_id; + // for h264 raw stream, + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 + SrsRawH264Stream avc_raw; // for h264 raw stream, // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 SrsStream h264_raw_stream; @@ -1021,44 +1025,16 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* Context* context = (Context*)rtmp; SrsSharedPtrMessage* msg = NULL; - - if (type == SRS_RTMP_TYPE_AUDIO) { - SrsMessageHeader header; - header.initialize_audio(size, timestamp, context->stream_id); - - msg = new SrsSharedPtrMessage(); - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { - srs_freep(data); - return ret; - } - } else if (type == SRS_RTMP_TYPE_VIDEO) { - SrsMessageHeader header; - header.initialize_video(size, timestamp, context->stream_id); - - msg = new SrsSharedPtrMessage(); - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { - srs_freep(data); - return ret; - } - } else if (type == SRS_RTMP_TYPE_SCRIPT) { - SrsMessageHeader header; - header.initialize_amf0_script(size, context->stream_id); - - msg = new SrsSharedPtrMessage(); - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { - srs_freep(data); - return ret; - } + + if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != ERROR_SUCCESS) { + return ret; } - - if (msg) { - // send out encoded msg. - if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) { - return ret; - } - } else { - // directly free data if not sent out. - srs_freep(data); + + srs_assert(msg); + + // send out encoded msg. + if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) { + return ret; } return ret; @@ -1403,52 +1379,37 @@ int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) return size; } - + /** -* write h264 packet, with rtmp header. -* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. -* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. -* @param h264_raw_data the h.264 raw data, user must free it. +* write h264 IPB-frame. */ -int __srs_write_h264_packet(Context* context, - int8_t frame_type, int8_t avc_packet_type, - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts +int __srs_write_h264_ipb_frame(Context* context, + char* frame, int frame_size, u_int32_t dts, u_int32_t pts ) { + int ret = ERROR_SUCCESS; + + // when sps or pps not sent, ignore the packet. + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 + if (!context->h264_sps_pps_sent) { + return ERROR_H264_DROP_BEFORE_SPS_PPS; + } + + std::string ibp; + int8_t frame_type; + if ((ret = context->avc_raw.mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) { + return ret; + } + + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; + char* flv = NULL; + int nb_flv = 0; + if ((ret = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { + return ret; + } + // the timestamp in rtmp message header is dts. u_int32_t timestamp = dts; - - // for h264 in RTMP video payload, there is 5bytes header: - // 1bytes, FrameType | CodecID - // 1bytes, AVCPacketType - // 3bytes, CompositionTime, the cts. - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - int size = h264_raw_size + 5; - char* data = new char[size]; - char* p = data; - - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 - // Frame Type, Type of video frame. - // CodecID, Codec Identifier. - // set the rtmp header - *p++ = (frame_type << 4) | SrsCodecVideoAVC; - - // AVCPacketType - *p++ = avc_packet_type; - - // CompositionTime - // pts = dts + cts, or - // cts = pts - dts. - // where cts is the header in rtmp video packet payload header. - u_int32_t cts = pts - dts; - char* pp = (char*)&cts; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; - - // h.264 raw data. - memcpy(p, h264_raw_data, h264_raw_size); - - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); } /** @@ -1463,78 +1424,19 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) return ret; } - // 5bytes sps/pps header: - // configurationVersion, AVCProfileIndication, profile_compatibility, - // AVCLevelIndication, lengthSizeMinusOne - // 3bytes size of sps: - // numOfSequenceParameterSets, sequenceParameterSetLength(2B) - // Nbytes of sps. - // sequenceParameterSetNALUnit - // 3bytes size of pps: - // numOfPictureParameterSets, pictureParameterSetLength - // Nbytes of pps: - // pictureParameterSetNALUnit - int nb_packet = 5 - + 3 + (int)context->h264_sps.length() - + 3 + (int)context->h264_pps.length(); - char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); - - // use stream to generate the h264 packet. - SrsStream stream; - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { + // h264 raw to h264 packet. + std::string sh; + if ((ret = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != ERROR_SUCCESS) { return ret; } - // decode the SPS: - // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 - if (true) { - srs_assert((int)context->h264_sps.length() >= 4); - char* frame = (char*)context->h264_sps.data(); - - // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 - // Baseline profile profile_idc is 66(0x42). - // Main profile profile_idc is 77(0x4d). - // Extended profile profile_idc is 88(0x58). - u_int8_t profile_idc = frame[1]; - //u_int8_t constraint_set = frame[2]; - u_int8_t level_idc = frame[3]; - - // generate the sps/pps header - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // configurationVersion - stream.write_1bytes(0x01); - // AVCProfileIndication - stream.write_1bytes(profile_idc); - // profile_compatibility - stream.write_1bytes(0x00); - // AVCLevelIndication - stream.write_1bytes(level_idc); - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, - // so we always set it to 0x03. - stream.write_1bytes(0x03); - } - - // sps - if (true) { - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // numOfSequenceParameterSets, always 1 - stream.write_1bytes(0x01); - // sequenceParameterSetLength - stream.write_2bytes(context->h264_sps.length()); - // sequenceParameterSetNALUnit - stream.write_string(context->h264_sps); - } - - // pps - if (true) { - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // numOfPictureParameterSets, always 1 - stream.write_1bytes(0x01); - // pictureParameterSetLength - stream.write_2bytes(context->h264_pps.length()); - // pictureParameterSetNALUnit - stream.write_string(context->h264_pps); + // h264 packet to flv packet. + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; + char* flv = NULL; + int nb_flv = 0; + if ((ret = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { + return ret; } // reset sps and pps. @@ -1542,75 +1444,9 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) context->h264_pps_changed = false; context->h264_sps_pps_sent = true; - // TODO: FIXME: for more profile. - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 - - // send out h264 packet. - int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; - int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; - return __srs_write_h264_packet( - context, frame_type, avc_packet_type, - packet, nb_packet, dts, pts - ); -} - -/** -* write h264 IPB-frame. -*/ -int __srs_write_h264_ipb_frame(Context* context, - char* data, int size, u_int32_t dts, u_int32_t pts -) { - int ret = ERROR_SUCCESS; - - // when sps or pps not sent, ignore the packet. - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 - if (!context->h264_sps_pps_sent) { - return ERROR_H264_DROP_BEFORE_SPS_PPS; - } - - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - u_int8_t nal_unit_type = (char)data[0] & 0x1f; - - // 4bytes size of nalu: - // NALUnitLength - // Nbytes of nalu. - // NALUnit - int nb_packet = 4 + size; - char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); - - // use stream to generate the h264 packet. - SrsStream stream; - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { - return ret; - } - - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size - u_int32_t NAL_unit_length = size; - - // mux the avc NALU in "ISO Base Media File Format" - // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 - // NALUnitLength - stream.write_4bytes(NAL_unit_length); - // NALUnit - stream.write_bytes(data, size); - - // send out h264 packet. - int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; - if (nal_unit_type != 1) { - frame_type = SrsCodecVideoAVCFrameKeyFrame; - } - int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; - return __srs_write_h264_packet( - context, frame_type, avc_packet_type, - packet, nb_packet, dts, pts - ); - - return ret; + // the timestamp in rtmp message header is dts. + u_int32_t timestamp = dts; + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); } /** @@ -1620,27 +1456,14 @@ int __srs_write_h264_raw_frame(Context* context, char* frame, int frame_size, u_int32_t dts, u_int32_t pts ) { int ret = ERROR_SUCCESS; - - // ignore invalid frame, - // atleast 1bytes for SPS to decode the type - if (frame_size < 1) { - return ret; - } - - // 5bits, 7.3.1 NAL unit syntax, - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame - u_int8_t nal_unit_type = (char)frame[0] & 0x1f; - - if (nal_unit_type == 7) { - // atleast 1bytes for SPS to decode the type, profile, constrain and level. - if (frame_size < 4) { + + // for sps + if (context->avc_raw.is_sps(frame, frame_size)) { + std::string sps; + if ((ret = context->avc_raw.sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { return ret; } - std::string sps; - sps.append(frame, frame_size); - if (context->h264_sps == sps) { return ERROR_H264_DUPLICATED_SPS; } @@ -1648,10 +1471,14 @@ int __srs_write_h264_raw_frame(Context* context, context->h264_sps = sps; return __srs_write_h264_sps_pps(context, dts, pts); - } else if (nal_unit_type == 8) { - + } + + // for pps + if (context->avc_raw.is_pps(frame, frame_size)) { std::string pps; - pps.append(frame, frame_size); + if ((ret = context->avc_raw.pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { + return ret; + } if (context->h264_pps == pps) { return ERROR_H264_DUPLICATED_PPS; @@ -1660,11 +1487,10 @@ int __srs_write_h264_raw_frame(Context* context, context->h264_pps = pps; return __srs_write_h264_sps_pps(context, dts, pts); - } else { - return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); } - - return ret; + + // ibp frame. + return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); } /** @@ -1692,29 +1518,21 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, // send each frame. while (!context->h264_raw_stream.empty()) { - // each frame must prefixed by annexb format. - // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. - int pnb_start_code = 0; - if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { - return ERROR_H264_API_NO_PREFIXED; + char* frame = NULL; + int frame_size = 0; + bool got_sps_pps = false; + if ((ret = context->avc_raw.annexb_demux(&context->h264_raw_stream, &frame, &frame_size)) != ERROR_SUCCESS) { + return ret; } - int start = context->h264_raw_stream.pos() + pnb_start_code; - - // find the last frame prefixed by annexb format. - context->h264_raw_stream.skip(pnb_start_code); - while (!context->h264_raw_stream.empty()) { - if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { - break; - } - context->h264_raw_stream.skip(1); + + // ignore invalid frame, + // atleast 1bytes for SPS to decode the type + if (frame_size <= 0) { + continue; } - int size = context->h264_raw_stream.pos() - start; - - // send out the frame. - char* frame = context->h264_raw_stream.data() + start; // it may be return error, but we must process all packets. - if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { + 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. diff --git a/trunk/src/protocol/srs_raw_avc.cpp b/trunk/src/protocol/srs_raw_avc.cpp new file mode 100644 index 000000000..084a638b5 --- /dev/null +++ b/trunk/src/protocol/srs_raw_avc.cpp @@ -0,0 +1,314 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +using namespace std; + +#include +#include +#include +#include +#include + +SrsRawH264Stream::SrsRawH264Stream() +{ +} + +SrsRawH264Stream::~SrsRawH264Stream() +{ +} + +int SrsRawH264Stream::annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame) +{ + int ret = ERROR_SUCCESS; + + *pframe = NULL; + *pnb_frame = 0; + + while (!stream->empty()) { + // each frame must prefixed by annexb format. + // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. + int pnb_start_code = 0; + if (!srs_avc_startswith_annexb(stream, &pnb_start_code)) { + return ERROR_H264_API_NO_PREFIXED; + } + int start = stream->pos() + pnb_start_code; + + // find the last frame prefixed by annexb format. + stream->skip(pnb_start_code); + while (!stream->empty()) { + if (srs_avc_startswith_annexb(stream, NULL)) { + break; + } + stream->skip(1); + } + + // demux the frame. + *pnb_frame = stream->pos() - start; + *pframe = stream->data() + start; + break; + } + + return ret; +} + +bool SrsRawH264Stream::is_sps(char* frame, int nb_frame) +{ + srs_assert(nb_frame > 0); + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; + + return nal_unit_type == 7; +} + +bool SrsRawH264Stream::is_pps(char* frame, int nb_frame) +{ + srs_assert(nb_frame > 0); + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; + + return nal_unit_type == 8; +} + +int SrsRawH264Stream::sps_demux(char* frame, int nb_frame, string& sps) +{ + int ret = ERROR_SUCCESS; + + // atleast 1bytes for SPS to decode the type, profile, constrain and level. + if (nb_frame < 4) { + return ret; + } + + sps = ""; + if (nb_frame > 0) { + sps.append(frame, nb_frame); + } + + // should never be empty. + if (sps.empty()) { + return ERROR_STREAM_CASTER_AVC_SPS; + } + + return ret; +} + +int SrsRawH264Stream::pps_demux(char* frame, int nb_frame, string& pps) +{ + int ret = ERROR_SUCCESS; + + pps = ""; + if (nb_frame > 0) { + pps.append(frame, nb_frame); + } + + // should never be empty. + if (pps.empty()) { + return ERROR_STREAM_CASTER_AVC_PPS; + } + + return ret; +} + +int SrsRawH264Stream::mux_sequence_header(string sps, string pps, u_int32_t dts, u_int32_t pts, string& sh) +{ + int ret = ERROR_SUCCESS; + + // 5bytes sps/pps header: + // configurationVersion, AVCProfileIndication, profile_compatibility, + // AVCLevelIndication, lengthSizeMinusOne + // 3bytes size of sps: + // numOfSequenceParameterSets, sequenceParameterSetLength(2B) + // Nbytes of sps. + // sequenceParameterSetNALUnit + // 3bytes size of pps: + // numOfPictureParameterSets, pictureParameterSetLength + // Nbytes of pps: + // pictureParameterSetNALUnit + int nb_packet = 5 + + 3 + (int)sps.length() + + 3 + (int)pps.length(); + char* packet = new char[nb_packet]; + SrsAutoFree(char, packet); + + // use stream to generate the h264 packet. + SrsStream stream; + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { + return ret; + } + + // decode the SPS: + // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 + if (true) { + srs_assert((int)sps.length() >= 4); + char* frame = (char*)sps.data(); + + // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 + // Baseline profile profile_idc is 66(0x42). + // Main profile profile_idc is 77(0x4d). + // Extended profile profile_idc is 88(0x58). + u_int8_t profile_idc = frame[1]; + //u_int8_t constraint_set = frame[2]; + u_int8_t level_idc = frame[3]; + + // generate the sps/pps header + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // configurationVersion + stream.write_1bytes(0x01); + // AVCProfileIndication + stream.write_1bytes(profile_idc); + // profile_compatibility + stream.write_1bytes(0x00); + // AVCLevelIndication + stream.write_1bytes(level_idc); + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, + // so we always set it to 0x03. + stream.write_1bytes(0x03); + } + + // sps + if (true) { + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // numOfSequenceParameterSets, always 1 + stream.write_1bytes(0x01); + // sequenceParameterSetLength + stream.write_2bytes(sps.length()); + // sequenceParameterSetNALUnit + stream.write_string(sps); + } + + // pps + if (true) { + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // numOfPictureParameterSets, always 1 + stream.write_1bytes(0x01); + // pictureParameterSetLength + stream.write_2bytes(pps.length()); + // pictureParameterSetNALUnit + stream.write_string(pps); + } + + // TODO: FIXME: for more profile. + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 + + sh = ""; + sh.append(packet, nb_packet); + + return ret; +} + +int SrsRawH264Stream::mux_ipb_frame(char* frame, int nb_frame, u_int32_t dts, u_int32_t pts, string& ibp, int8_t& frame_type) +{ + int ret = ERROR_SUCCESS; + + // 5bits, 7.3.1 NAL unit syntax, + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; + + // 4bytes size of nalu: + // NALUnitLength + // Nbytes of nalu. + // NALUnit + int nb_packet = 4 + nb_frame; + char* packet = new char[nb_packet]; + SrsAutoFree(char, packet); + + // use stream to generate the h264 packet. + SrsStream stream; + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { + return ret; + } + + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size + u_int32_t NAL_unit_length = nb_frame; + + // mux the avc NALU in "ISO Base Media File Format" + // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 + // NALUnitLength + stream.write_4bytes(NAL_unit_length); + // NALUnit + stream.write_bytes(frame, nb_frame); + + // send out h264 packet. + frame_type = SrsCodecVideoAVCFrameInterFrame; + if (nal_unit_type != 1) { + frame_type = SrsCodecVideoAVCFrameKeyFrame; + } + + ibp = ""; + ibp.append(packet, nb_packet); + + return ret; +} + +int SrsRawH264Stream::mux_avc2flv(string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv) +{ + int ret = ERROR_SUCCESS; + + // for h264 in RTMP video payload, there is 5bytes header: + // 1bytes, FrameType | CodecID + // 1bytes, AVCPacketType + // 3bytes, CompositionTime, the cts. + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + int size = video.length() + 5; + char* data = new char[size]; + char* p = data; + + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 + // Frame Type, Type of video frame. + // CodecID, Codec Identifier. + // set the rtmp header + *p++ = (frame_type << 4) | SrsCodecVideoAVC; + + // AVCPacketType + *p++ = avc_packet_type; + + // CompositionTime + // pts = dts + cts, or + // cts = pts - dts. + // where cts is the header in rtmp video packet payload header. + u_int32_t cts = pts - dts; + char* pp = (char*)&cts; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; + + // h.264 raw data. + memcpy(p, video.data(), video.length()); + + *flv = data; + *nb_flv = size; + + return ret; +} + diff --git a/trunk/src/protocol/srs_raw_avc.hpp b/trunk/src/protocol/srs_raw_avc.hpp new file mode 100644 index 000000000..a175e540f --- /dev/null +++ b/trunk/src/protocol/srs_raw_avc.hpp @@ -0,0 +1,89 @@ +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_PROTOCOL_RAW_AVC_HPP +#define SRS_PROTOCOL_RAW_AVC_HPP + +/* +#include +*/ + +#include + +#include + +class SrsStream; + +/** +* the raw h.264 stream, in annexb. +*/ +class SrsRawH264Stream +{ +public: + SrsRawH264Stream(); + virtual ~SrsRawH264Stream(); +public: + /** + * demux the stream in annexb format. + * @param stream the input stream bytes. + * @param pframe the output h.264 frame in stream. user should never free it. + * @param pnb_frame the output h.264 frame size. + */ + virtual int annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame); + /** + * whether the frame is sps or pps. + */ + virtual bool is_sps(char* frame, int nb_frame); + virtual bool is_pps(char* frame, int nb_frame); + /** + * demux the sps or pps to string. + * @param sps/pps output the sps/pps. + */ + virtual int sps_demux(char* frame, int nb_frame, std::string& sps); + virtual int pps_demux(char* frame, int nb_frame, std::string& pps); +public: + /** + * h264 raw data to h264 packet, without flv payload header. + * mux the sps/pps to flv sequence header packet. + * @param sh output the sequence header. + */ + virtual int mux_sequence_header(std::string sps, std::string pps, u_int32_t dts, u_int32_t pts, std::string& sh); + /** + * h264 raw data to h264 packet, without flv payload header. + * mux the ibp to flv ibp packet. + * @param ibp output the packet. + * @param frame_type output the frame type. + */ + virtual int mux_ipb_frame(char* frame, int nb_frame, u_int32_t dts, u_int32_t pts, std::string& ibp, int8_t& frame_type); + /** + * mux the avc video packet to flv video packet. + * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. + * @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. + * @param video the h.264 raw data. + * @param flv output the muxed flv packet. + * @param nb_flv output the muxed flv size. + */ + virtual int mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv); +}; + +#endif diff --git a/trunk/src/protocol/srs_rtmp_utility.cpp b/trunk/src/protocol/srs_rtmp_utility.cpp index e3d23a21b..e56ad7468 100644 --- a/trunk/src/protocol/srs_rtmp_utility.cpp +++ b/trunk/src/protocol/srs_rtmp_utility.cpp @@ -30,6 +30,7 @@ using namespace std; #include #include #include +#include void srs_discovery_tc_url( string tcUrl, @@ -287,3 +288,61 @@ int srs_chunk_header_c3( return p - cache; } +int __srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) +{ + int ret = ERROR_SUCCESS; + + *ppmsg = NULL; + SrsSharedPtrMessage* msg = NULL; + + if (type == SrsCodecFlvTagAudio) { + SrsMessageHeader header; + header.initialize_audio(size, timestamp, stream_id); + + msg = new SrsSharedPtrMessage(); + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { + srs_freep(msg); + return ret; + } + } else if (type == SrsCodecFlvTagVideo) { + SrsMessageHeader header; + header.initialize_video(size, timestamp, stream_id); + + msg = new SrsSharedPtrMessage(); + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { + srs_freep(msg); + return ret; + } + } else if (type == SrsCodecFlvTagScript) { + SrsMessageHeader header; + header.initialize_amf0_script(size, stream_id); + + msg = new SrsSharedPtrMessage(); + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { + srs_freep(msg); + return ret; + } + } else { + ret = ERROR_STREAM_CASTER_FLV_TAG; + srs_error("rtmp unknown tag type=%#x. ret=%d", type, ret); + return ret; + } + + *ppmsg = msg; + + return ret; +} + +int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) +{ + int ret = ERROR_SUCCESS; + + // only when failed, we must free the data. + if ((ret = __srs_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != ERROR_SUCCESS) { + srs_freep(data); + return ret; + } + + return ret; +} + diff --git a/trunk/src/protocol/srs_rtmp_utility.hpp b/trunk/src/protocol/srs_rtmp_utility.hpp index 3200bdcf8..8fb74976f 100644 --- a/trunk/src/protocol/srs_rtmp_utility.hpp +++ b/trunk/src/protocol/srs_rtmp_utility.hpp @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class SrsMessageHeader; +class SrsSharedPtrMessage; /** * parse the tcUrl, output the schema, host, vhost, app and port. @@ -110,5 +111,12 @@ extern int srs_chunk_header_c3( char* cache, int nb_cache ); +/** +* create shared ptr message from bytes. +* @param data the packet bytes. user should never free it. +* @param ppmsg output the shared ptr message. user should free it. +*/ +extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg); + #endif