winlin

for #250, support h264 video for push mpegts over udp. 2.0.110

... ... @@ -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[@]}"
#
... ...
... ... @@ -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,
... ...
... ... @@ -116,6 +116,7 @@
<ClInclude Include="..\..\src\libs\srs_librtmp.hpp" />
<ClInclude Include="..\..\src\libs\srs_lib_bandwidth.hpp" />
<ClInclude Include="..\..\src\libs\srs_lib_simple_socket.hpp" />
<ClInclude Include="..\..\src\protocol\srs_raw_avc.hpp" />
<ClInclude Include="..\..\src\protocol\srs_rtmp_amf0.hpp" />
<ClInclude Include="..\..\src\protocol\srs_rtmp_buffer.hpp" />
<ClInclude Include="..\..\src\protocol\srs_rtmp_handshake.hpp" />
... ... @@ -193,6 +194,7 @@
<ClCompile Include="..\..\src\libs\srs_lib_bandwidth.cpp" />
<ClCompile Include="..\..\src\libs\srs_lib_simple_socket.cpp" />
<ClCompile Include="..\..\src\main\srs_main_server.cpp" />
<ClCompile Include="..\..\src\protocol\srs_raw_avc.cpp" />
<ClCompile Include="..\..\src\protocol\srs_rtmp_amf0.cpp" />
<ClCompile Include="..\..\src\protocol\srs_rtmp_buffer.cpp" />
<ClCompile Include="..\..\src\protocol\srs_rtmp_handshake.cpp" />
... ...
... ... @@ -226,6 +226,9 @@
<ClCompile Include="..\..\src\app\srs_app_mpegts_udp.cpp">
<Filter>srs</Filter>
</ClCompile>
<ClCompile Include="..\..\src\protocol\srs_raw_avc.cpp">
<Filter>srs</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\app\srs_app_bandwidth.hpp">
... ... @@ -414,6 +417,9 @@
<ClInclude Include="..\..\src\app\srs_app_mpegts_udp.hpp">
<Filter>srs</Filter>
</ClInclude>
<ClInclude Include="..\..\src\protocol\srs_raw_avc.hpp">
<Filter>srs</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="research">
... ...
... ... @@ -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;
}
... ...
... ... @@ -23,6 +23,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_app_mpegts_udp.hpp>
#ifdef SRS_AUTO_STREAM_CASTER
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
... ... @@ -39,8 +42,12 @@ using namespace std;
#include <srs_kernel_file.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_utility.hpp>
#ifdef SRS_AUTO_STREAM_CASTER
#include <srs_rtmp_sdk.hpp>
#include <srs_app_st_socket.hpp>
#include <srs_rtmp_utility.hpp>
#include <srs_app_utility.hpp>
#include <srs_rtmp_amf0.hpp>
#include <srs_raw_avc.hpp>
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<std::string> 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
... ...
... ... @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_core.hpp>
#ifdef SRS_AUTO_STREAM_CASTER
class sockaddr_in;
#include <string>
... ... @@ -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 <srs_app_st.hpp>
#include <srs_kernel_ts.hpp>
/**
... ... @@ -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
... ...
... ... @@ -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"
... ...
... ... @@ -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,
... ... @@ -164,6 +164,22 @@ enum SrsCodecAudioSampleRate
};
/**
* 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.
*/
... ...
... ... @@ -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.
... ...
... ... @@ -47,6 +47,7 @@ using namespace std;
#include <srs_kernel_codec.hpp>
#include <srs_kernel_file.hpp>
#include <srs_lib_bandwidth.hpp>
#include <srs_raw_avc.hpp>
// kernel module.
ISrsLog* _srs_log = new ISrsLog();
... ... @@ -82,6 +83,9 @@ struct Context
// 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;
// about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
std::string h264_sps;
... ... @@ -1022,44 +1026,16 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char*
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);
if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != ERROR_SUCCESS) {
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;
}
}
srs_assert(msg);
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);
}
return ret;
}
... ... @@ -1405,50 +1381,35 @@ int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_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
) {
// 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;
int ret = ERROR_SUCCESS;
// AVCPacketType
*p++ = avc_packet_type;
// 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;
}
// 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];
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;
}
// h.264 raw data.
memcpy(p, h264_raw_data, h264_raw_size);
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;
}
return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size);
// 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);
}
/**
... ... @@ -1463,154 +1424,29 @@ 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) {
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 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;
}
// reset sps and pps.
context->h264_sps_changed = false;
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.
// h264 packet to flv 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) {
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;
}
// 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
);
// reset sps and pps.
context->h264_sps_changed = false;
context->h264_pps_changed = false;
context->h264_sps_pps_sent = true;
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);
}
/**
... ... @@ -1621,26 +1457,13 @@ int __srs_write_h264_raw_frame(Context* context,
) {
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.
... ...
/*
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 <srs_raw_avc.hpp>
#include <string.h>
using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_codec.hpp>
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;
}
... ...
/*
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 <srs_raw_avc.hpp>
*/
#include <srs_core.hpp>
#include <string>
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
... ...
... ... @@ -30,6 +30,7 @@ using namespace std;
#include <srs_kernel_utility.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_codec.hpp>
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;
}
... ...
... ... @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <srs_kernel_consts.hpp>
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
... ...