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