mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 19:31:53 +00:00
for #250, support h264 video for push mpegts over udp. 2.0.110
This commit is contained in:
parent
66fccdbbd1
commit
70c8fe13c4
15 changed files with 942 additions and 270 deletions
3
trunk/configure
vendored
3
trunk/configure
vendored
|
@ -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,
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>$(ProjectDir)/../../src/core;$(ProjectDir)/../../src/kernel;$(ProjectDir)/../../src/protocol;$(ProjectDir)/../../src/app;$(ProjectDir)/../../src/libs;$(ProjectDir)/../../objs;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
@ -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.
|
||||
|
|
314
trunk/src/protocol/srs_raw_avc.cpp
Normal file
314
trunk/src/protocol/srs_raw_avc.cpp
Normal file
|
@ -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 <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;
|
||||
}
|
||||
|
89
trunk/src/protocol/srs_raw_avc.hpp
Normal file
89
trunk/src/protocol/srs_raw_avc.hpp
Normal file
|
@ -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 <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
|
||||
|
||||
|
|
Loading…
Reference in a new issue