for #250, support h264 video for push mpegts over udp. 2.0.110
正在显示
15 个修改的文件
包含
940 行增加
和
268 行删除
| @@ -375,7 +375,8 @@ MODULE_ID="RTMP" | @@ -375,7 +375,8 @@ MODULE_ID="RTMP" | ||
| 375 | MODULE_DEPENDS=("CORE" "KERNEL") | 375 | MODULE_DEPENDS=("CORE" "KERNEL") |
| 376 | ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) | 376 | ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) |
| 377 | MODULE_FILES=("srs_rtmp_amf0" "srs_rtmp_io" "srs_rtmp_stack" "srs_rtmp_sdk" | 377 | MODULE_FILES=("srs_rtmp_amf0" "srs_rtmp_io" "srs_rtmp_stack" "srs_rtmp_sdk" |
| 378 | - "srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_rtmp_buffer") | 378 | + "srs_rtmp_handshake" "srs_rtmp_utility" "srs_rtmp_msg_array" "srs_rtmp_buffer" |
| 379 | + "srs_raw_avc") | ||
| 379 | RTMP_INCS="src/protocol"; MODULE_DIR=${RTMP_INCS} . auto/modules.sh | 380 | RTMP_INCS="src/protocol"; MODULE_DIR=${RTMP_INCS} . auto/modules.sh |
| 380 | RTMP_OBJS="${MODULE_OBJS[@]}" | 381 | RTMP_OBJS="${MODULE_OBJS[@]}" |
| 381 | # | 382 | # |
| @@ -45,6 +45,8 @@ file | @@ -45,6 +45,8 @@ file | ||
| 45 | ../../src/kernel/srs_kernel_utility.hpp, | 45 | ../../src/kernel/srs_kernel_utility.hpp, |
| 46 | ../../src/kernel/srs_kernel_utility.cpp, | 46 | ../../src/kernel/srs_kernel_utility.cpp, |
| 47 | protocol readonly separator, | 47 | protocol readonly separator, |
| 48 | + ../../src/protocol/srs_raw_avc.hpp, | ||
| 49 | + ../../src/protocol/srs_raw_avc.cpp, | ||
| 48 | ../../src/protocol/srs_rtmp_amf0.hpp, | 50 | ../../src/protocol/srs_rtmp_amf0.hpp, |
| 49 | ../../src/protocol/srs_rtmp_amf0.cpp, | 51 | ../../src/protocol/srs_rtmp_amf0.cpp, |
| 50 | ../../src/protocol/srs_rtmp_buffer.hpp, | 52 | ../../src/protocol/srs_rtmp_buffer.hpp, |
| @@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
| 36 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | 36 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
| 37 | </ImportGroup> | 37 | </ImportGroup> |
| 38 | <PropertyGroup Label="UserMacros" /> | 38 | <PropertyGroup Label="UserMacros" /> |
| 39 | - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | 39 | + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
| 40 | <IncludePath>$(ProjectDir)/../../src/core;$(ProjectDir)/../../src/kernel;$(ProjectDir)/../../src/protocol;$(ProjectDir)/../../src/app;$(ProjectDir)/../../src/libs;$(ProjectDir)/../../objs;$(IncludePath)</IncludePath> | 40 | <IncludePath>$(ProjectDir)/../../src/core;$(ProjectDir)/../../src/kernel;$(ProjectDir)/../../src/protocol;$(ProjectDir)/../../src/app;$(ProjectDir)/../../src/libs;$(ProjectDir)/../../objs;$(IncludePath)</IncludePath> |
| 41 | </PropertyGroup> | 41 | </PropertyGroup> |
| 42 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | 42 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
| @@ -116,6 +116,7 @@ | @@ -116,6 +116,7 @@ | ||
| 116 | <ClInclude Include="..\..\src\libs\srs_librtmp.hpp" /> | 116 | <ClInclude Include="..\..\src\libs\srs_librtmp.hpp" /> |
| 117 | <ClInclude Include="..\..\src\libs\srs_lib_bandwidth.hpp" /> | 117 | <ClInclude Include="..\..\src\libs\srs_lib_bandwidth.hpp" /> |
| 118 | <ClInclude Include="..\..\src\libs\srs_lib_simple_socket.hpp" /> | 118 | <ClInclude Include="..\..\src\libs\srs_lib_simple_socket.hpp" /> |
| 119 | + <ClInclude Include="..\..\src\protocol\srs_raw_avc.hpp" /> | ||
| 119 | <ClInclude Include="..\..\src\protocol\srs_rtmp_amf0.hpp" /> | 120 | <ClInclude Include="..\..\src\protocol\srs_rtmp_amf0.hpp" /> |
| 120 | <ClInclude Include="..\..\src\protocol\srs_rtmp_buffer.hpp" /> | 121 | <ClInclude Include="..\..\src\protocol\srs_rtmp_buffer.hpp" /> |
| 121 | <ClInclude Include="..\..\src\protocol\srs_rtmp_handshake.hpp" /> | 122 | <ClInclude Include="..\..\src\protocol\srs_rtmp_handshake.hpp" /> |
| @@ -193,6 +194,7 @@ | @@ -193,6 +194,7 @@ | ||
| 193 | <ClCompile Include="..\..\src\libs\srs_lib_bandwidth.cpp" /> | 194 | <ClCompile Include="..\..\src\libs\srs_lib_bandwidth.cpp" /> |
| 194 | <ClCompile Include="..\..\src\libs\srs_lib_simple_socket.cpp" /> | 195 | <ClCompile Include="..\..\src\libs\srs_lib_simple_socket.cpp" /> |
| 195 | <ClCompile Include="..\..\src\main\srs_main_server.cpp" /> | 196 | <ClCompile Include="..\..\src\main\srs_main_server.cpp" /> |
| 197 | + <ClCompile Include="..\..\src\protocol\srs_raw_avc.cpp" /> | ||
| 196 | <ClCompile Include="..\..\src\protocol\srs_rtmp_amf0.cpp" /> | 198 | <ClCompile Include="..\..\src\protocol\srs_rtmp_amf0.cpp" /> |
| 197 | <ClCompile Include="..\..\src\protocol\srs_rtmp_buffer.cpp" /> | 199 | <ClCompile Include="..\..\src\protocol\srs_rtmp_buffer.cpp" /> |
| 198 | <ClCompile Include="..\..\src\protocol\srs_rtmp_handshake.cpp" /> | 200 | <ClCompile Include="..\..\src\protocol\srs_rtmp_handshake.cpp" /> |
| @@ -226,6 +226,9 @@ | @@ -226,6 +226,9 @@ | ||
| 226 | <ClCompile Include="..\..\src\app\srs_app_mpegts_udp.cpp"> | 226 | <ClCompile Include="..\..\src\app\srs_app_mpegts_udp.cpp"> |
| 227 | <Filter>srs</Filter> | 227 | <Filter>srs</Filter> |
| 228 | </ClCompile> | 228 | </ClCompile> |
| 229 | + <ClCompile Include="..\..\src\protocol\srs_raw_avc.cpp"> | ||
| 230 | + <Filter>srs</Filter> | ||
| 231 | + </ClCompile> | ||
| 229 | </ItemGroup> | 232 | </ItemGroup> |
| 230 | <ItemGroup> | 233 | <ItemGroup> |
| 231 | <ClInclude Include="..\..\src\app\srs_app_bandwidth.hpp"> | 234 | <ClInclude Include="..\..\src\app\srs_app_bandwidth.hpp"> |
| @@ -414,6 +417,9 @@ | @@ -414,6 +417,9 @@ | ||
| 414 | <ClInclude Include="..\..\src\app\srs_app_mpegts_udp.hpp"> | 417 | <ClInclude Include="..\..\src\app\srs_app_mpegts_udp.hpp"> |
| 415 | <Filter>srs</Filter> | 418 | <Filter>srs</Filter> |
| 416 | </ClInclude> | 419 | </ClInclude> |
| 420 | + <ClInclude Include="..\..\src\protocol\srs_raw_avc.hpp"> | ||
| 421 | + <Filter>srs</Filter> | ||
| 422 | + </ClInclude> | ||
| 417 | </ItemGroup> | 423 | </ItemGroup> |
| 418 | <ItemGroup> | 424 | <ItemGroup> |
| 419 | <Filter Include="research"> | 425 | <Filter Include="research"> |
| @@ -447,7 +447,7 @@ int SrsEdgeForwarder::start() | @@ -447,7 +447,7 @@ int SrsEdgeForwarder::start() | ||
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { | 449 | if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { |
| 450 | - srs_error("connect with server failed, stream=%s, stream_id=%d. ret=%d", | 450 | + srs_error("publish failed, stream=%s, stream_id=%d. ret=%d", |
| 451 | req->stream.c_str(), stream_id, ret); | 451 | req->stream.c_str(), stream_id, ret); |
| 452 | return ret; | 452 | return ret; |
| 453 | } | 453 | } |
| @@ -23,6 +23,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -23,6 +23,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 23 | 23 | ||
| 24 | #include <srs_app_mpegts_udp.hpp> | 24 | #include <srs_app_mpegts_udp.hpp> |
| 25 | 25 | ||
| 26 | +#ifdef SRS_AUTO_STREAM_CASTER | ||
| 27 | + | ||
| 28 | +#include <stdlib.h> | ||
| 26 | #include <sys/socket.h> | 29 | #include <sys/socket.h> |
| 27 | #include <netinet/in.h> | 30 | #include <netinet/in.h> |
| 28 | #include <arpa/inet.h> | 31 | #include <arpa/inet.h> |
| @@ -39,8 +42,12 @@ using namespace std; | @@ -39,8 +42,12 @@ using namespace std; | ||
| 39 | #include <srs_kernel_file.hpp> | 42 | #include <srs_kernel_file.hpp> |
| 40 | #include <srs_core_autofree.hpp> | 43 | #include <srs_core_autofree.hpp> |
| 41 | #include <srs_kernel_utility.hpp> | 44 | #include <srs_kernel_utility.hpp> |
| 42 | - | ||
| 43 | -#ifdef SRS_AUTO_STREAM_CASTER | 45 | +#include <srs_rtmp_sdk.hpp> |
| 46 | +#include <srs_app_st_socket.hpp> | ||
| 47 | +#include <srs_rtmp_utility.hpp> | ||
| 48 | +#include <srs_app_utility.hpp> | ||
| 49 | +#include <srs_rtmp_amf0.hpp> | ||
| 50 | +#include <srs_raw_avc.hpp> | ||
| 44 | 51 | ||
| 45 | ISrsUdpHandler::ISrsUdpHandler() | 52 | ISrsUdpHandler::ISrsUdpHandler() |
| 46 | { | 53 | { |
| @@ -56,13 +63,25 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) | @@ -56,13 +63,25 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp(SrsConfDirective* c) | ||
| 56 | context = new SrsTsContext(); | 63 | context = new SrsTsContext(); |
| 57 | buffer = new SrsSimpleBuffer(); | 64 | buffer = new SrsSimpleBuffer(); |
| 58 | output = _srs_config->get_stream_caster_output(c); | 65 | output = _srs_config->get_stream_caster_output(c); |
| 66 | + req = NULL; | ||
| 67 | + io = NULL; | ||
| 68 | + client = NULL; | ||
| 69 | + stfd = NULL; | ||
| 70 | + stream_id = 0; | ||
| 71 | + avc = new SrsRawH264Stream(); | ||
| 72 | + h264_sps_changed = false; | ||
| 73 | + h264_pps_changed = false; | ||
| 74 | + h264_sps_pps_sent = false; | ||
| 59 | } | 75 | } |
| 60 | 76 | ||
| 61 | SrsMpegtsOverUdp::~SrsMpegtsOverUdp() | 77 | SrsMpegtsOverUdp::~SrsMpegtsOverUdp() |
| 62 | { | 78 | { |
| 79 | + close(); | ||
| 80 | + | ||
| 63 | srs_freep(buffer); | 81 | srs_freep(buffer); |
| 64 | srs_freep(stream); | 82 | srs_freep(stream); |
| 65 | srs_freep(context); | 83 | srs_freep(context); |
| 84 | + srs_freep(avc); | ||
| 66 | } | 85 | } |
| 67 | 86 | ||
| 68 | int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | 87 | int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) |
| @@ -207,8 +226,311 @@ int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg) | @@ -207,8 +226,311 @@ int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg) | ||
| 207 | return ret; | 226 | return ret; |
| 208 | } | 227 | } |
| 209 | 228 | ||
| 229 | + // check supported codec | ||
| 230 | + if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamAudioAAC) { | ||
| 231 | + ret = ERROR_STREAM_CASTER_TS_CODEC; | ||
| 232 | + srs_error("mpegts: unsupported stream codec=%d. ret=%d", msg->channel->stream, ret); | ||
| 233 | + return ret; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + // parse the stream. | ||
| 237 | + SrsStream avs; | ||
| 238 | + if ((ret = avs.initialize(msg->payload->bytes(), msg->payload->length())) != ERROR_SUCCESS) { | ||
| 239 | + srs_error("mpegts: initialize av stream failed. ret=%d", ret); | ||
| 240 | + return ret; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + // publish audio or video. | ||
| 244 | + if (msg->channel->stream == SrsTsStreamVideoH264) { | ||
| 245 | + return on_ts_video(msg, &avs); | ||
| 246 | + } | ||
| 247 | + | ||
| 210 | // TODO: FIXME: implements it. | 248 | // TODO: FIXME: implements it. |
| 211 | return ret; | 249 | return ret; |
| 212 | } | 250 | } |
| 213 | 251 | ||
| 252 | +int SrsMpegtsOverUdp::on_ts_video(SrsTsMessage* msg, SrsStream* avs) | ||
| 253 | +{ | ||
| 254 | + int ret = ERROR_SUCCESS; | ||
| 255 | + | ||
| 256 | + // ensure rtmp connected. | ||
| 257 | + if ((ret = connect()) != ERROR_SUCCESS) { | ||
| 258 | + return ret; | ||
| 259 | + } | ||
| 260 | + | ||
| 261 | + // ts tbn to flv tbn. | ||
| 262 | + u_int32_t dts = msg->dts / 90; | ||
| 263 | + u_int32_t pts = msg->dts / 90; | ||
| 264 | + | ||
| 265 | + // send each frame. | ||
| 266 | + while (!avs->empty()) { | ||
| 267 | + char* frame = NULL; | ||
| 268 | + int frame_size = 0; | ||
| 269 | + if ((ret = avc->annexb_demux(avs, &frame, &frame_size)) != ERROR_SUCCESS) { | ||
| 270 | + return ret; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + // ignore invalid frame, | ||
| 274 | + // * atleast 1bytes for SPS to decode the type | ||
| 275 | + // * ignore the auth bytes '09f0' | ||
| 276 | + if (frame_size <= 2) { | ||
| 277 | + continue; | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + // it may be return error, but we must process all packets. | ||
| 281 | + if ((ret = write_h264_raw_frame(frame, frame_size, dts, pts)) != ERROR_SUCCESS) { | ||
| 282 | + if (ret = ERROR_H264_DROP_BEFORE_SPS_PPS) { | ||
| 283 | + continue; | ||
| 284 | + } | ||
| 285 | + return ret; | ||
| 286 | + } | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + return ret; | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +int SrsMpegtsOverUdp::write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) | ||
| 293 | +{ | ||
| 294 | + int ret = ERROR_SUCCESS; | ||
| 295 | + | ||
| 296 | + // for sps | ||
| 297 | + if (avc->is_sps(frame, frame_size)) { | ||
| 298 | + std::string sps; | ||
| 299 | + if ((ret = avc->sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { | ||
| 300 | + return ret; | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + if (h264_sps == sps) { | ||
| 304 | + return ret; | ||
| 305 | + } | ||
| 306 | + h264_sps_changed = true; | ||
| 307 | + h264_sps = sps; | ||
| 308 | + | ||
| 309 | + return write_h264_sps_pps(dts, pts); | ||
| 310 | + } | ||
| 311 | + | ||
| 312 | + // for pps | ||
| 313 | + if (avc->is_pps(frame, frame_size)) { | ||
| 314 | + std::string pps; | ||
| 315 | + if ((ret = avc->pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { | ||
| 316 | + return ret; | ||
| 317 | + } | ||
| 318 | + | ||
| 319 | + if (h264_pps == pps) { | ||
| 320 | + return ret; | ||
| 321 | + } | ||
| 322 | + h264_pps_changed = true; | ||
| 323 | + h264_pps = pps; | ||
| 324 | + | ||
| 325 | + return write_h264_sps_pps(dts, pts); | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + // ibp frame. | ||
| 329 | + return write_h264_ipb_frame(frame, frame_size, dts, pts); | ||
| 330 | +} | ||
| 331 | + | ||
| 332 | +int SrsMpegtsOverUdp::write_h264_sps_pps(u_int32_t dts, u_int32_t pts) | ||
| 333 | +{ | ||
| 334 | + int ret = ERROR_SUCCESS; | ||
| 335 | + | ||
| 336 | + // only send when both sps and pps changed. | ||
| 337 | + if (!h264_sps_changed || !h264_pps_changed) { | ||
| 338 | + return ret; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + // h264 raw to h264 packet. | ||
| 342 | + std::string sh; | ||
| 343 | + if ((ret = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != ERROR_SUCCESS) { | ||
| 344 | + return ret; | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + // h264 packet to flv packet. | ||
| 348 | + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 349 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 350 | + char* flv = NULL; | ||
| 351 | + int nb_flv = 0; | ||
| 352 | + if ((ret = avc->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 353 | + return ret; | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + // reset sps and pps. | ||
| 357 | + h264_sps_changed = false; | ||
| 358 | + h264_pps_changed = false; | ||
| 359 | + h264_sps_pps_sent = true; | ||
| 360 | + | ||
| 361 | + // the timestamp in rtmp message header is dts. | ||
| 362 | + u_int32_t timestamp = dts; | ||
| 363 | + return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); | ||
| 364 | +} | ||
| 365 | + | ||
| 366 | +int SrsMpegtsOverUdp::write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) | ||
| 367 | +{ | ||
| 368 | + int ret = ERROR_SUCCESS; | ||
| 369 | + | ||
| 370 | + // when sps or pps not sent, ignore the packet. | ||
| 371 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
| 372 | + if (!h264_sps_pps_sent) { | ||
| 373 | + return ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + std::string ibp; | ||
| 377 | + int8_t frame_type; | ||
| 378 | + if ((ret = avc->mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) { | ||
| 379 | + return ret; | ||
| 380 | + } | ||
| 381 | + | ||
| 382 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
| 383 | + char* flv = NULL; | ||
| 384 | + int nb_flv = 0; | ||
| 385 | + if ((ret = avc->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 386 | + return ret; | ||
| 387 | + } | ||
| 388 | + | ||
| 389 | + // the timestamp in rtmp message header is dts. | ||
| 390 | + u_int32_t timestamp = dts; | ||
| 391 | + return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); | ||
| 392 | +} | ||
| 393 | + | ||
| 394 | +int SrsMpegtsOverUdp::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size) | ||
| 395 | +{ | ||
| 396 | + int ret = ERROR_SUCCESS; | ||
| 397 | + | ||
| 398 | + SrsSharedPtrMessage* msg = NULL; | ||
| 399 | + | ||
| 400 | + if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, stream_id, &msg)) != ERROR_SUCCESS) { | ||
| 401 | + return ret; | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + srs_assert(msg); | ||
| 405 | + | ||
| 406 | + // send out encoded msg. | ||
| 407 | + if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { | ||
| 408 | + return ret; | ||
| 409 | + } | ||
| 410 | + | ||
| 411 | + return ret; | ||
| 412 | +} | ||
| 413 | + | ||
| 414 | +int SrsMpegtsOverUdp::connect() | ||
| 415 | +{ | ||
| 416 | + int ret = ERROR_SUCCESS; | ||
| 417 | + | ||
| 418 | + // when ok, ignore. | ||
| 419 | + if (io || client) { | ||
| 420 | + return ret; | ||
| 421 | + } | ||
| 422 | + | ||
| 423 | + // parse uri | ||
| 424 | + if (!req) { | ||
| 425 | + req = new SrsRequest(); | ||
| 426 | + | ||
| 427 | + size_t pos = string::npos; | ||
| 428 | + string uri = req->tcUrl = output; | ||
| 429 | + | ||
| 430 | + // tcUrl, stream | ||
| 431 | + if ((pos = uri.rfind("/")) != string::npos) { | ||
| 432 | + req->stream = uri.substr(pos + 1); | ||
| 433 | + req->tcUrl = uri = uri.substr(0, pos); | ||
| 434 | + } | ||
| 435 | + | ||
| 436 | + srs_discovery_tc_url(req->tcUrl, | ||
| 437 | + req->schema, req->host, req->vhost, req->app, req->port, | ||
| 438 | + req->param); | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + // connect host. | ||
| 442 | + if ((ret = srs_socket_connect(req->host, ::atoi(req->port.c_str()), ST_UTIME_NO_TIMEOUT, &stfd)) != ERROR_SUCCESS) { | ||
| 443 | + srs_error("mpegts: connect server %s:%s failed. ret=%d", req->host.c_str(), req->port.c_str(), ret); | ||
| 444 | + return ret; | ||
| 445 | + } | ||
| 446 | + io = new SrsStSocket(stfd); | ||
| 447 | + client = new SrsRtmpClient(io); | ||
| 448 | + | ||
| 449 | + client->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); | ||
| 450 | + client->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); | ||
| 451 | + | ||
| 452 | + // connect to vhost/app | ||
| 453 | + if ((ret = client->handshake()) != ERROR_SUCCESS) { | ||
| 454 | + srs_error("mpegts: handshake with server failed. ret=%d", ret); | ||
| 455 | + return ret; | ||
| 456 | + } | ||
| 457 | + if ((ret = connect_app(req->host, req->port)) != ERROR_SUCCESS) { | ||
| 458 | + srs_error("mpegts: connect with server failed. ret=%d", ret); | ||
| 459 | + return ret; | ||
| 460 | + } | ||
| 461 | + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { | ||
| 462 | + srs_error("mpegts: connect with server failed, stream_id=%d. ret=%d", stream_id, ret); | ||
| 463 | + return ret; | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + // publish. | ||
| 467 | + if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { | ||
| 468 | + srs_error("mpegts: publish failed, stream=%s, stream_id=%d. ret=%d", | ||
| 469 | + req->stream.c_str(), stream_id, ret); | ||
| 470 | + return ret; | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + | ||
| 474 | + return ret; | ||
| 475 | +} | ||
| 476 | + | ||
| 477 | +// TODO: FIXME: refine the connect_app. | ||
| 478 | +int SrsMpegtsOverUdp::connect_app(string ep_server, string ep_port) | ||
| 479 | +{ | ||
| 480 | + int ret = ERROR_SUCCESS; | ||
| 481 | + | ||
| 482 | + // args of request takes the srs info. | ||
| 483 | + if (req->args == NULL) { | ||
| 484 | + req->args = SrsAmf0Any::object(); | ||
| 485 | + } | ||
| 486 | + | ||
| 487 | + // notify server the edge identity, | ||
| 488 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/147 | ||
| 489 | + SrsAmf0Object* data = req->args; | ||
| 490 | + data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); | ||
| 491 | + data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); | ||
| 492 | + data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); | ||
| 493 | + data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); | ||
| 494 | + data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); | ||
| 495 | + data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); | ||
| 496 | + data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); | ||
| 497 | + data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); | ||
| 498 | + data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); | ||
| 499 | + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); | ||
| 500 | + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); | ||
| 501 | + // for edge to directly get the id of client. | ||
| 502 | + data->set("srs_pid", SrsAmf0Any::number(getpid())); | ||
| 503 | + data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); | ||
| 504 | + | ||
| 505 | + // local ip of edge | ||
| 506 | + std::vector<std::string> ips = srs_get_local_ipv4_ips(); | ||
| 507 | + assert(_srs_config->get_stats_network() < (int)ips.size()); | ||
| 508 | + std::string local_ip = ips[_srs_config->get_stats_network()]; | ||
| 509 | + data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str())); | ||
| 510 | + | ||
| 511 | + // generate the tcUrl | ||
| 512 | + std::string param = ""; | ||
| 513 | + std::string tc_url = srs_generate_tc_url(ep_server, req->vhost, req->app, ep_port, param); | ||
| 514 | + | ||
| 515 | + // upnode server identity will show in the connect_app of client. | ||
| 516 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/160 | ||
| 517 | + // the debug_srs_upnode is config in vhost and default to true. | ||
| 518 | + bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost); | ||
| 519 | + if ((ret = client->connect_app(req->app, tc_url, req, debug_srs_upnode)) != ERROR_SUCCESS) { | ||
| 520 | + srs_error("mpegts: connect with server failed, tcUrl=%s, dsu=%d. ret=%d", | ||
| 521 | + tc_url.c_str(), debug_srs_upnode, ret); | ||
| 522 | + return ret; | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + return ret; | ||
| 526 | +} | ||
| 527 | + | ||
| 528 | +void SrsMpegtsOverUdp::close() | ||
| 529 | +{ | ||
| 530 | + srs_freep(client); | ||
| 531 | + srs_freep(io); | ||
| 532 | + srs_freep(req); | ||
| 533 | + srs_close_stfd(stfd); | ||
| 534 | +} | ||
| 535 | + | ||
| 214 | #endif | 536 | #endif |
| @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -30,6 +30,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 30 | 30 | ||
| 31 | #include <srs_core.hpp> | 31 | #include <srs_core.hpp> |
| 32 | 32 | ||
| 33 | +#ifdef SRS_AUTO_STREAM_CASTER | ||
| 34 | + | ||
| 33 | class sockaddr_in; | 35 | class sockaddr_in; |
| 34 | #include <string> | 36 | #include <string> |
| 35 | 37 | ||
| @@ -37,9 +39,12 @@ class SrsStream; | @@ -37,9 +39,12 @@ class SrsStream; | ||
| 37 | class SrsTsContext; | 39 | class SrsTsContext; |
| 38 | class SrsConfDirective; | 40 | class SrsConfDirective; |
| 39 | class SrsSimpleBuffer; | 41 | class SrsSimpleBuffer; |
| 42 | +class SrsRtmpClient; | ||
| 43 | +class SrsStSocket; | ||
| 44 | +class SrsRequest; | ||
| 45 | +class SrsRawH264Stream; | ||
| 40 | 46 | ||
| 41 | -#ifdef SRS_AUTO_STREAM_CASTER | ||
| 42 | - | 47 | +#include <srs_app_st.hpp> |
| 43 | #include <srs_kernel_ts.hpp> | 48 | #include <srs_kernel_ts.hpp> |
| 44 | 49 | ||
| 45 | /** | 50 | /** |
| @@ -74,6 +79,19 @@ private: | @@ -74,6 +79,19 @@ private: | ||
| 74 | SrsTsContext* context; | 79 | SrsTsContext* context; |
| 75 | SrsSimpleBuffer* buffer; | 80 | SrsSimpleBuffer* buffer; |
| 76 | std::string output; | 81 | std::string output; |
| 82 | +private: | ||
| 83 | + SrsRequest* req; | ||
| 84 | + st_netfd_t stfd; | ||
| 85 | + SrsStSocket* io; | ||
| 86 | + SrsRtmpClient* client; | ||
| 87 | + int stream_id; | ||
| 88 | +private: | ||
| 89 | + SrsRawH264Stream* avc; | ||
| 90 | + std::string h264_sps; | ||
| 91 | + bool h264_sps_changed; | ||
| 92 | + std::string h264_pps; | ||
| 93 | + bool h264_pps_changed; | ||
| 94 | + bool h264_sps_pps_sent; | ||
| 77 | public: | 95 | public: |
| 78 | SrsMpegtsOverUdp(SrsConfDirective* c); | 96 | SrsMpegtsOverUdp(SrsConfDirective* c); |
| 79 | virtual ~SrsMpegtsOverUdp(); | 97 | virtual ~SrsMpegtsOverUdp(); |
| @@ -83,6 +101,19 @@ public: | @@ -83,6 +101,19 @@ public: | ||
| 83 | // interface ISrsTsHandler | 101 | // interface ISrsTsHandler |
| 84 | public: | 102 | public: |
| 85 | virtual int on_ts_message(SrsTsMessage* msg); | 103 | virtual int on_ts_message(SrsTsMessage* msg); |
| 104 | +private: | ||
| 105 | + virtual int on_ts_video(SrsTsMessage* msg, SrsStream* avs); | ||
| 106 | + virtual int write_h264_raw_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); | ||
| 107 | + virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); | ||
| 108 | + virtual int write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); | ||
| 109 | + virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size); | ||
| 110 | +private: | ||
| 111 | + // connect to rtmp output url. | ||
| 112 | + // @remark ignore when not connected, reconnect when disconnected. | ||
| 113 | + virtual int connect(); | ||
| 114 | + virtual int connect_app(std::string ep_server, std::string ep_port); | ||
| 115 | + // close the connected io and rtmp to ready to be re-connect. | ||
| 116 | + virtual void close(); | ||
| 86 | }; | 117 | }; |
| 87 | 118 | ||
| 88 | #endif | 119 | #endif |
| @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 31 | // current release version | 31 | // current release version |
| 32 | #define VERSION_MAJOR 2 | 32 | #define VERSION_MAJOR 2 |
| 33 | #define VERSION_MINOR 0 | 33 | #define VERSION_MINOR 0 |
| 34 | -#define VERSION_REVISION 109 | 34 | +#define VERSION_REVISION 110 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
| @@ -53,7 +53,7 @@ enum SrsCodecAudioType | @@ -53,7 +53,7 @@ enum SrsCodecAudioType | ||
| 53 | // 5 = video info/command frame | 53 | // 5 = video info/command frame |
| 54 | enum SrsCodecVideoAVCFrame | 54 | enum SrsCodecVideoAVCFrame |
| 55 | { | 55 | { |
| 56 | - // set to the max value to reserved, for array map. | 56 | + // set to the zero to reserved, for array map. |
| 57 | SrsCodecVideoAVCFrameReserved = 0, | 57 | SrsCodecVideoAVCFrameReserved = 0, |
| 58 | SrsCodecVideoAVCFrameReserved1 = 6, | 58 | SrsCodecVideoAVCFrameReserved1 = 6, |
| 59 | 59 | ||
| @@ -91,7 +91,7 @@ enum SrsCodecVideoAVCType | @@ -91,7 +91,7 @@ enum SrsCodecVideoAVCType | ||
| 91 | // 7 = AVC | 91 | // 7 = AVC |
| 92 | enum SrsCodecVideo | 92 | enum SrsCodecVideo |
| 93 | { | 93 | { |
| 94 | - // set to the max value to reserved, for array map. | 94 | + // set to the zero to reserved, for array map. |
| 95 | SrsCodecVideoReserved = 0, | 95 | SrsCodecVideoReserved = 0, |
| 96 | SrsCodecVideoReserved1 = 1, | 96 | SrsCodecVideoReserved1 = 1, |
| 97 | SrsCodecVideoReserved2 = 8, | 97 | SrsCodecVideoReserved2 = 8, |
| @@ -164,6 +164,22 @@ enum SrsCodecAudioSampleRate | @@ -164,6 +164,22 @@ enum SrsCodecAudioSampleRate | ||
| 164 | }; | 164 | }; |
| 165 | 165 | ||
| 166 | /** | 166 | /** |
| 167 | +* E.4.1 FLV Tag, page 75 | ||
| 168 | +*/ | ||
| 169 | +enum SrsCodecFlvTag | ||
| 170 | +{ | ||
| 171 | + // set to the zero to reserved, for array map. | ||
| 172 | + SrsCodecFlvTagReserved = 0, | ||
| 173 | + | ||
| 174 | + // 8 = audio | ||
| 175 | + SrsCodecFlvTagAudio = 8, | ||
| 176 | + // 9 = video | ||
| 177 | + SrsCodecFlvTagVideo = 9, | ||
| 178 | + // 18 = script data | ||
| 179 | + SrsCodecFlvTagScript = 18, | ||
| 180 | +}; | ||
| 181 | + | ||
| 182 | +/** | ||
| 167 | * Annex E. The FLV File Format | 183 | * Annex E. The FLV File Format |
| 168 | * @see SrsAvcAacCodec for the media stream codec. | 184 | * @see SrsAvcAacCodec for the media stream codec. |
| 169 | */ | 185 | */ |
| @@ -228,6 +228,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -228,6 +228,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 228 | #define ERROR_STREAM_CASTER_TS_PMT 4018 | 228 | #define ERROR_STREAM_CASTER_TS_PMT 4018 |
| 229 | #define ERROR_STREAM_CASTER_TS_PSE 4019 | 229 | #define ERROR_STREAM_CASTER_TS_PSE 4019 |
| 230 | #define ERROR_STREAM_CASTER_TS_ES 4020 | 230 | #define ERROR_STREAM_CASTER_TS_ES 4020 |
| 231 | +#define ERROR_STREAM_CASTER_TS_CODEC 4021 | ||
| 232 | +#define ERROR_STREAM_CASTER_AVC_SPS 4022 | ||
| 233 | +#define ERROR_STREAM_CASTER_AVC_PPS 4023 | ||
| 234 | +#define ERROR_STREAM_CASTER_FLV_TAG 4024 | ||
| 231 | 235 | ||
| 232 | /** | 236 | /** |
| 233 | * whether the error code is an system control error. | 237 | * whether the error code is an system control error. |
| @@ -47,6 +47,7 @@ using namespace std; | @@ -47,6 +47,7 @@ using namespace std; | ||
| 47 | #include <srs_kernel_codec.hpp> | 47 | #include <srs_kernel_codec.hpp> |
| 48 | #include <srs_kernel_file.hpp> | 48 | #include <srs_kernel_file.hpp> |
| 49 | #include <srs_lib_bandwidth.hpp> | 49 | #include <srs_lib_bandwidth.hpp> |
| 50 | +#include <srs_raw_avc.hpp> | ||
| 50 | 51 | ||
| 51 | // kernel module. | 52 | // kernel module. |
| 52 | ISrsLog* _srs_log = new ISrsLog(); | 53 | ISrsLog* _srs_log = new ISrsLog(); |
| @@ -82,6 +83,9 @@ struct Context | @@ -82,6 +83,9 @@ struct Context | ||
| 82 | 83 | ||
| 83 | // for h264 raw stream, | 84 | // for h264 raw stream, |
| 84 | // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 | 85 | // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 |
| 86 | + SrsRawH264Stream avc_raw; | ||
| 87 | + // for h264 raw stream, | ||
| 88 | + // @see: https://github.com/winlinvip/simple-rtmp-server/issues/66#issuecomment-62240521 | ||
| 85 | SrsStream h264_raw_stream; | 89 | SrsStream h264_raw_stream; |
| 86 | // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | 90 | // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 |
| 87 | std::string h264_sps; | 91 | std::string h264_sps; |
| @@ -1021,44 +1025,16 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* | @@ -1021,44 +1025,16 @@ int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* | ||
| 1021 | Context* context = (Context*)rtmp; | 1025 | Context* context = (Context*)rtmp; |
| 1022 | 1026 | ||
| 1023 | SrsSharedPtrMessage* msg = NULL; | 1027 | SrsSharedPtrMessage* msg = NULL; |
| 1024 | - | ||
| 1025 | - if (type == SRS_RTMP_TYPE_AUDIO) { | ||
| 1026 | - SrsMessageHeader header; | ||
| 1027 | - header.initialize_audio(size, timestamp, context->stream_id); | ||
| 1028 | - | ||
| 1029 | - msg = new SrsSharedPtrMessage(); | ||
| 1030 | - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 1031 | - srs_freep(data); | ||
| 1032 | - return ret; | ||
| 1033 | - } | ||
| 1034 | - } else if (type == SRS_RTMP_TYPE_VIDEO) { | ||
| 1035 | - SrsMessageHeader header; | ||
| 1036 | - header.initialize_video(size, timestamp, context->stream_id); | ||
| 1037 | - | ||
| 1038 | - msg = new SrsSharedPtrMessage(); | ||
| 1039 | - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 1040 | - srs_freep(data); | ||
| 1041 | - return ret; | ||
| 1042 | - } | ||
| 1043 | - } else if (type == SRS_RTMP_TYPE_SCRIPT) { | ||
| 1044 | - SrsMessageHeader header; | ||
| 1045 | - header.initialize_amf0_script(size, context->stream_id); | ||
| 1046 | - | ||
| 1047 | - msg = new SrsSharedPtrMessage(); | ||
| 1048 | - if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 1049 | - srs_freep(data); | ||
| 1050 | - return ret; | ||
| 1051 | - } | 1028 | + |
| 1029 | + if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != ERROR_SUCCESS) { | ||
| 1030 | + return ret; | ||
| 1052 | } | 1031 | } |
| 1053 | - | ||
| 1054 | - if (msg) { | ||
| 1055 | - // send out encoded msg. | ||
| 1056 | - if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) { | ||
| 1057 | - return ret; | ||
| 1058 | - } | ||
| 1059 | - } else { | ||
| 1060 | - // directly free data if not sent out. | ||
| 1061 | - srs_freep(data); | 1032 | + |
| 1033 | + srs_assert(msg); | ||
| 1034 | + | ||
| 1035 | + // send out encoded msg. | ||
| 1036 | + if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) { | ||
| 1037 | + return ret; | ||
| 1062 | } | 1038 | } |
| 1063 | 1039 | ||
| 1064 | return ret; | 1040 | return ret; |
| @@ -1403,52 +1379,37 @@ int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) | @@ -1403,52 +1379,37 @@ int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size) | ||
| 1403 | 1379 | ||
| 1404 | return size; | 1380 | return size; |
| 1405 | } | 1381 | } |
| 1406 | - | 1382 | + |
| 1407 | /** | 1383 | /** |
| 1408 | -* write h264 packet, with rtmp header. | ||
| 1409 | -* @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. | ||
| 1410 | -* @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. | ||
| 1411 | -* @param h264_raw_data the h.264 raw data, user must free it. | 1384 | +* write h264 IPB-frame. |
| 1412 | */ | 1385 | */ |
| 1413 | -int __srs_write_h264_packet(Context* context, | ||
| 1414 | - int8_t frame_type, int8_t avc_packet_type, | ||
| 1415 | - char* h264_raw_data, int h264_raw_size, u_int32_t dts, u_int32_t pts | 1386 | +int __srs_write_h264_ipb_frame(Context* context, |
| 1387 | + char* frame, int frame_size, u_int32_t dts, u_int32_t pts | ||
| 1416 | ) { | 1388 | ) { |
| 1417 | - // the timestamp in rtmp message header is dts. | ||
| 1418 | - u_int32_t timestamp = dts; | ||
| 1419 | - | ||
| 1420 | - // for h264 in RTMP video payload, there is 5bytes header: | ||
| 1421 | - // 1bytes, FrameType | CodecID | ||
| 1422 | - // 1bytes, AVCPacketType | ||
| 1423 | - // 3bytes, CompositionTime, the cts. | ||
| 1424 | - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
| 1425 | - int size = h264_raw_size + 5; | ||
| 1426 | - char* data = new char[size]; | ||
| 1427 | - char* p = data; | ||
| 1428 | - | ||
| 1429 | - // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
| 1430 | - // Frame Type, Type of video frame. | ||
| 1431 | - // CodecID, Codec Identifier. | ||
| 1432 | - // set the rtmp header | ||
| 1433 | - *p++ = (frame_type << 4) | SrsCodecVideoAVC; | 1389 | + int ret = ERROR_SUCCESS; |
| 1434 | 1390 | ||
| 1435 | - // AVCPacketType | ||
| 1436 | - *p++ = avc_packet_type; | 1391 | + // when sps or pps not sent, ignore the packet. |
| 1392 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
| 1393 | + if (!context->h264_sps_pps_sent) { | ||
| 1394 | + return ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
| 1395 | + } | ||
| 1437 | 1396 | ||
| 1438 | - // CompositionTime | ||
| 1439 | - // pts = dts + cts, or | ||
| 1440 | - // cts = pts - dts. | ||
| 1441 | - // where cts is the header in rtmp video packet payload header. | ||
| 1442 | - u_int32_t cts = pts - dts; | ||
| 1443 | - char* pp = (char*)&cts; | ||
| 1444 | - *p++ = pp[2]; | ||
| 1445 | - *p++ = pp[1]; | ||
| 1446 | - *p++ = pp[0]; | 1397 | + std::string ibp; |
| 1398 | + int8_t frame_type; | ||
| 1399 | + if ((ret = context->avc_raw.mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) { | ||
| 1400 | + return ret; | ||
| 1401 | + } | ||
| 1447 | 1402 | ||
| 1448 | - // h.264 raw data. | ||
| 1449 | - memcpy(p, h264_raw_data, h264_raw_size); | 1403 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; |
| 1404 | + char* flv = NULL; | ||
| 1405 | + int nb_flv = 0; | ||
| 1406 | + if ((ret = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 1407 | + return ret; | ||
| 1408 | + } | ||
| 1450 | 1409 | ||
| 1451 | - return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, data, size); | 1410 | + // the timestamp in rtmp message header is dts. |
| 1411 | + u_int32_t timestamp = dts; | ||
| 1412 | + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); | ||
| 1452 | } | 1413 | } |
| 1453 | 1414 | ||
| 1454 | /** | 1415 | /** |
| @@ -1463,78 +1424,19 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | @@ -1463,78 +1424,19 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | ||
| 1463 | return ret; | 1424 | return ret; |
| 1464 | } | 1425 | } |
| 1465 | 1426 | ||
| 1466 | - // 5bytes sps/pps header: | ||
| 1467 | - // configurationVersion, AVCProfileIndication, profile_compatibility, | ||
| 1468 | - // AVCLevelIndication, lengthSizeMinusOne | ||
| 1469 | - // 3bytes size of sps: | ||
| 1470 | - // numOfSequenceParameterSets, sequenceParameterSetLength(2B) | ||
| 1471 | - // Nbytes of sps. | ||
| 1472 | - // sequenceParameterSetNALUnit | ||
| 1473 | - // 3bytes size of pps: | ||
| 1474 | - // numOfPictureParameterSets, pictureParameterSetLength | ||
| 1475 | - // Nbytes of pps: | ||
| 1476 | - // pictureParameterSetNALUnit | ||
| 1477 | - int nb_packet = 5 | ||
| 1478 | - + 3 + (int)context->h264_sps.length() | ||
| 1479 | - + 3 + (int)context->h264_pps.length(); | ||
| 1480 | - char* packet = new char[nb_packet]; | ||
| 1481 | - SrsAutoFree(char, packet); | ||
| 1482 | - | ||
| 1483 | - // use stream to generate the h264 packet. | ||
| 1484 | - SrsStream stream; | ||
| 1485 | - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | 1427 | + // h264 raw to h264 packet. |
| 1428 | + std::string sh; | ||
| 1429 | + if ((ret = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != ERROR_SUCCESS) { | ||
| 1486 | return ret; | 1430 | return ret; |
| 1487 | } | 1431 | } |
| 1488 | 1432 | ||
| 1489 | - // decode the SPS: | ||
| 1490 | - // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
| 1491 | - if (true) { | ||
| 1492 | - srs_assert((int)context->h264_sps.length() >= 4); | ||
| 1493 | - char* frame = (char*)context->h264_sps.data(); | ||
| 1494 | - | ||
| 1495 | - // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 | ||
| 1496 | - // Baseline profile profile_idc is 66(0x42). | ||
| 1497 | - // Main profile profile_idc is 77(0x4d). | ||
| 1498 | - // Extended profile profile_idc is 88(0x58). | ||
| 1499 | - u_int8_t profile_idc = frame[1]; | ||
| 1500 | - //u_int8_t constraint_set = frame[2]; | ||
| 1501 | - u_int8_t level_idc = frame[3]; | ||
| 1502 | - | ||
| 1503 | - // generate the sps/pps header | ||
| 1504 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1505 | - // configurationVersion | ||
| 1506 | - stream.write_1bytes(0x01); | ||
| 1507 | - // AVCProfileIndication | ||
| 1508 | - stream.write_1bytes(profile_idc); | ||
| 1509 | - // profile_compatibility | ||
| 1510 | - stream.write_1bytes(0x00); | ||
| 1511 | - // AVCLevelIndication | ||
| 1512 | - stream.write_1bytes(level_idc); | ||
| 1513 | - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, | ||
| 1514 | - // so we always set it to 0x03. | ||
| 1515 | - stream.write_1bytes(0x03); | ||
| 1516 | - } | ||
| 1517 | - | ||
| 1518 | - // sps | ||
| 1519 | - if (true) { | ||
| 1520 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1521 | - // numOfSequenceParameterSets, always 1 | ||
| 1522 | - stream.write_1bytes(0x01); | ||
| 1523 | - // sequenceParameterSetLength | ||
| 1524 | - stream.write_2bytes(context->h264_sps.length()); | ||
| 1525 | - // sequenceParameterSetNALUnit | ||
| 1526 | - stream.write_string(context->h264_sps); | ||
| 1527 | - } | ||
| 1528 | - | ||
| 1529 | - // pps | ||
| 1530 | - if (true) { | ||
| 1531 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1532 | - // numOfPictureParameterSets, always 1 | ||
| 1533 | - stream.write_1bytes(0x01); | ||
| 1534 | - // pictureParameterSetLength | ||
| 1535 | - stream.write_2bytes(context->h264_pps.length()); | ||
| 1536 | - // pictureParameterSetNALUnit | ||
| 1537 | - stream.write_string(context->h264_pps); | 1433 | + // h264 packet to flv packet. |
| 1434 | + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1435 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 1436 | + char* flv = NULL; | ||
| 1437 | + int nb_flv = 0; | ||
| 1438 | + if ((ret = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 1439 | + return ret; | ||
| 1538 | } | 1440 | } |
| 1539 | 1441 | ||
| 1540 | // reset sps and pps. | 1442 | // reset sps and pps. |
| @@ -1542,75 +1444,9 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | @@ -1542,75 +1444,9 @@ int __srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) | ||
| 1542 | context->h264_pps_changed = false; | 1444 | context->h264_pps_changed = false; |
| 1543 | context->h264_sps_pps_sent = true; | 1445 | context->h264_sps_pps_sent = true; |
| 1544 | 1446 | ||
| 1545 | - // TODO: FIXME: for more profile. | ||
| 1546 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1547 | - // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 | ||
| 1548 | - | ||
| 1549 | - // send out h264 packet. | ||
| 1550 | - int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1551 | - int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 1552 | - return __srs_write_h264_packet( | ||
| 1553 | - context, frame_type, avc_packet_type, | ||
| 1554 | - packet, nb_packet, dts, pts | ||
| 1555 | - ); | ||
| 1556 | -} | ||
| 1557 | - | ||
| 1558 | -/** | ||
| 1559 | -* write h264 IPB-frame. | ||
| 1560 | -*/ | ||
| 1561 | -int __srs_write_h264_ipb_frame(Context* context, | ||
| 1562 | - char* data, int size, u_int32_t dts, u_int32_t pts | ||
| 1563 | -) { | ||
| 1564 | - int ret = ERROR_SUCCESS; | ||
| 1565 | - | ||
| 1566 | - // when sps or pps not sent, ignore the packet. | ||
| 1567 | - // @see https://github.com/winlinvip/simple-rtmp-server/issues/203 | ||
| 1568 | - if (!context->h264_sps_pps_sent) { | ||
| 1569 | - return ERROR_H264_DROP_BEFORE_SPS_PPS; | ||
| 1570 | - } | ||
| 1571 | - | ||
| 1572 | - // 5bits, 7.3.1 NAL unit syntax, | ||
| 1573 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 1574 | - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 1575 | - u_int8_t nal_unit_type = (char)data[0] & 0x1f; | ||
| 1576 | - | ||
| 1577 | - // 4bytes size of nalu: | ||
| 1578 | - // NALUnitLength | ||
| 1579 | - // Nbytes of nalu. | ||
| 1580 | - // NALUnit | ||
| 1581 | - int nb_packet = 4 + size; | ||
| 1582 | - char* packet = new char[nb_packet]; | ||
| 1583 | - SrsAutoFree(char, packet); | ||
| 1584 | - | ||
| 1585 | - // use stream to generate the h264 packet. | ||
| 1586 | - SrsStream stream; | ||
| 1587 | - if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
| 1588 | - return ret; | ||
| 1589 | - } | ||
| 1590 | - | ||
| 1591 | - // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 1592 | - // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size | ||
| 1593 | - u_int32_t NAL_unit_length = size; | ||
| 1594 | - | ||
| 1595 | - // mux the avc NALU in "ISO Base Media File Format" | ||
| 1596 | - // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 | ||
| 1597 | - // NALUnitLength | ||
| 1598 | - stream.write_4bytes(NAL_unit_length); | ||
| 1599 | - // NALUnit | ||
| 1600 | - stream.write_bytes(data, size); | ||
| 1601 | - | ||
| 1602 | - // send out h264 packet. | ||
| 1603 | - int8_t frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
| 1604 | - if (nal_unit_type != 1) { | ||
| 1605 | - frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 1606 | - } | ||
| 1607 | - int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
| 1608 | - return __srs_write_h264_packet( | ||
| 1609 | - context, frame_type, avc_packet_type, | ||
| 1610 | - packet, nb_packet, dts, pts | ||
| 1611 | - ); | ||
| 1612 | - | ||
| 1613 | - return ret; | 1447 | + // the timestamp in rtmp message header is dts. |
| 1448 | + u_int32_t timestamp = dts; | ||
| 1449 | + return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv); | ||
| 1614 | } | 1450 | } |
| 1615 | 1451 | ||
| 1616 | /** | 1452 | /** |
| @@ -1620,27 +1456,14 @@ int __srs_write_h264_raw_frame(Context* context, | @@ -1620,27 +1456,14 @@ int __srs_write_h264_raw_frame(Context* context, | ||
| 1620 | char* frame, int frame_size, u_int32_t dts, u_int32_t pts | 1456 | char* frame, int frame_size, u_int32_t dts, u_int32_t pts |
| 1621 | ) { | 1457 | ) { |
| 1622 | int ret = ERROR_SUCCESS; | 1458 | int ret = ERROR_SUCCESS; |
| 1623 | - | ||
| 1624 | - // ignore invalid frame, | ||
| 1625 | - // atleast 1bytes for SPS to decode the type | ||
| 1626 | - if (frame_size < 1) { | ||
| 1627 | - return ret; | ||
| 1628 | - } | ||
| 1629 | - | ||
| 1630 | - // 5bits, 7.3.1 NAL unit syntax, | ||
| 1631 | - // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 1632 | - // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 1633 | - u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 1634 | - | ||
| 1635 | - if (nal_unit_type == 7) { | ||
| 1636 | - // atleast 1bytes for SPS to decode the type, profile, constrain and level. | ||
| 1637 | - if (frame_size < 4) { | 1459 | + |
| 1460 | + // for sps | ||
| 1461 | + if (context->avc_raw.is_sps(frame, frame_size)) { | ||
| 1462 | + std::string sps; | ||
| 1463 | + if ((ret = context->avc_raw.sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) { | ||
| 1638 | return ret; | 1464 | return ret; |
| 1639 | } | 1465 | } |
| 1640 | 1466 | ||
| 1641 | - std::string sps; | ||
| 1642 | - sps.append(frame, frame_size); | ||
| 1643 | - | ||
| 1644 | if (context->h264_sps == sps) { | 1467 | if (context->h264_sps == sps) { |
| 1645 | return ERROR_H264_DUPLICATED_SPS; | 1468 | return ERROR_H264_DUPLICATED_SPS; |
| 1646 | } | 1469 | } |
| @@ -1648,10 +1471,14 @@ int __srs_write_h264_raw_frame(Context* context, | @@ -1648,10 +1471,14 @@ int __srs_write_h264_raw_frame(Context* context, | ||
| 1648 | context->h264_sps = sps; | 1471 | context->h264_sps = sps; |
| 1649 | 1472 | ||
| 1650 | return __srs_write_h264_sps_pps(context, dts, pts); | 1473 | return __srs_write_h264_sps_pps(context, dts, pts); |
| 1651 | - } else if (nal_unit_type == 8) { | ||
| 1652 | - | 1474 | + } |
| 1475 | + | ||
| 1476 | + // for pps | ||
| 1477 | + if (context->avc_raw.is_pps(frame, frame_size)) { | ||
| 1653 | std::string pps; | 1478 | std::string pps; |
| 1654 | - pps.append(frame, frame_size); | 1479 | + if ((ret = context->avc_raw.pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) { |
| 1480 | + return ret; | ||
| 1481 | + } | ||
| 1655 | 1482 | ||
| 1656 | if (context->h264_pps == pps) { | 1483 | if (context->h264_pps == pps) { |
| 1657 | return ERROR_H264_DUPLICATED_PPS; | 1484 | return ERROR_H264_DUPLICATED_PPS; |
| @@ -1660,11 +1487,10 @@ int __srs_write_h264_raw_frame(Context* context, | @@ -1660,11 +1487,10 @@ int __srs_write_h264_raw_frame(Context* context, | ||
| 1660 | context->h264_pps = pps; | 1487 | context->h264_pps = pps; |
| 1661 | 1488 | ||
| 1662 | return __srs_write_h264_sps_pps(context, dts, pts); | 1489 | return __srs_write_h264_sps_pps(context, dts, pts); |
| 1663 | - } else { | ||
| 1664 | - return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); | ||
| 1665 | } | 1490 | } |
| 1666 | - | ||
| 1667 | - return ret; | 1491 | + |
| 1492 | + // ibp frame. | ||
| 1493 | + return __srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts); | ||
| 1668 | } | 1494 | } |
| 1669 | 1495 | ||
| 1670 | /** | 1496 | /** |
| @@ -1692,29 +1518,21 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, | @@ -1692,29 +1518,21 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, | ||
| 1692 | 1518 | ||
| 1693 | // send each frame. | 1519 | // send each frame. |
| 1694 | while (!context->h264_raw_stream.empty()) { | 1520 | while (!context->h264_raw_stream.empty()) { |
| 1695 | - // each frame must prefixed by annexb format. | ||
| 1696 | - // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. | ||
| 1697 | - int pnb_start_code = 0; | ||
| 1698 | - if (!srs_avc_startswith_annexb(&context->h264_raw_stream, &pnb_start_code)) { | ||
| 1699 | - return ERROR_H264_API_NO_PREFIXED; | 1521 | + char* frame = NULL; |
| 1522 | + int frame_size = 0; | ||
| 1523 | + bool got_sps_pps = false; | ||
| 1524 | + if ((ret = context->avc_raw.annexb_demux(&context->h264_raw_stream, &frame, &frame_size)) != ERROR_SUCCESS) { | ||
| 1525 | + return ret; | ||
| 1700 | } | 1526 | } |
| 1701 | - int start = context->h264_raw_stream.pos() + pnb_start_code; | ||
| 1702 | - | ||
| 1703 | - // find the last frame prefixed by annexb format. | ||
| 1704 | - context->h264_raw_stream.skip(pnb_start_code); | ||
| 1705 | - while (!context->h264_raw_stream.empty()) { | ||
| 1706 | - if (srs_avc_startswith_annexb(&context->h264_raw_stream, NULL)) { | ||
| 1707 | - break; | ||
| 1708 | - } | ||
| 1709 | - context->h264_raw_stream.skip(1); | 1527 | + |
| 1528 | + // ignore invalid frame, | ||
| 1529 | + // atleast 1bytes for SPS to decode the type | ||
| 1530 | + if (frame_size <= 0) { | ||
| 1531 | + continue; | ||
| 1710 | } | 1532 | } |
| 1711 | - int size = context->h264_raw_stream.pos() - start; | ||
| 1712 | - | ||
| 1713 | - // send out the frame. | ||
| 1714 | - char* frame = context->h264_raw_stream.data() + start; | ||
| 1715 | 1533 | ||
| 1716 | // it may be return error, but we must process all packets. | 1534 | // it may be return error, but we must process all packets. |
| 1717 | - if ((ret = __srs_write_h264_raw_frame(context, frame, size, dts, pts)) != ERROR_SUCCESS) { | 1535 | + if ((ret = __srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) { |
| 1718 | error_code_return = ret; | 1536 | error_code_return = ret; |
| 1719 | 1537 | ||
| 1720 | // ignore known error, process all packets. | 1538 | // ignore known error, process all packets. |
trunk/src/protocol/srs_raw_avc.cpp
0 → 100644
| 1 | +/* | ||
| 2 | +The MIT License (MIT) | ||
| 3 | + | ||
| 4 | +Copyright (c) 2013-2015 winlin | ||
| 5 | + | ||
| 6 | +Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 7 | +this software and associated documentation files (the "Software"), to deal in | ||
| 8 | +the Software without restriction, including without limitation the rights to | ||
| 9 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| 10 | +the Software, and to permit persons to whom the Software is furnished to do so, | ||
| 11 | +subject to the following conditions: | ||
| 12 | + | ||
| 13 | +The above copyright notice and this permission notice shall be included in all | ||
| 14 | +copies or substantial portions of the Software. | ||
| 15 | + | ||
| 16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 17 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
| 18 | +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
| 19 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 20 | +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 21 | +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 22 | +*/ | ||
| 23 | + | ||
| 24 | +#include <srs_raw_avc.hpp> | ||
| 25 | + | ||
| 26 | +#include <string.h> | ||
| 27 | +using namespace std; | ||
| 28 | + | ||
| 29 | +#include <srs_kernel_error.hpp> | ||
| 30 | +#include <srs_kernel_stream.hpp> | ||
| 31 | +#include <srs_kernel_utility.hpp> | ||
| 32 | +#include <srs_core_autofree.hpp> | ||
| 33 | +#include <srs_kernel_codec.hpp> | ||
| 34 | + | ||
| 35 | +SrsRawH264Stream::SrsRawH264Stream() | ||
| 36 | +{ | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +SrsRawH264Stream::~SrsRawH264Stream() | ||
| 40 | +{ | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +int SrsRawH264Stream::annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame) | ||
| 44 | +{ | ||
| 45 | + int ret = ERROR_SUCCESS; | ||
| 46 | + | ||
| 47 | + *pframe = NULL; | ||
| 48 | + *pnb_frame = 0; | ||
| 49 | + | ||
| 50 | + while (!stream->empty()) { | ||
| 51 | + // each frame must prefixed by annexb format. | ||
| 52 | + // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211. | ||
| 53 | + int pnb_start_code = 0; | ||
| 54 | + if (!srs_avc_startswith_annexb(stream, &pnb_start_code)) { | ||
| 55 | + return ERROR_H264_API_NO_PREFIXED; | ||
| 56 | + } | ||
| 57 | + int start = stream->pos() + pnb_start_code; | ||
| 58 | + | ||
| 59 | + // find the last frame prefixed by annexb format. | ||
| 60 | + stream->skip(pnb_start_code); | ||
| 61 | + while (!stream->empty()) { | ||
| 62 | + if (srs_avc_startswith_annexb(stream, NULL)) { | ||
| 63 | + break; | ||
| 64 | + } | ||
| 65 | + stream->skip(1); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + // demux the frame. | ||
| 69 | + *pnb_frame = stream->pos() - start; | ||
| 70 | + *pframe = stream->data() + start; | ||
| 71 | + break; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + return ret; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +bool SrsRawH264Stream::is_sps(char* frame, int nb_frame) | ||
| 78 | +{ | ||
| 79 | + srs_assert(nb_frame > 0); | ||
| 80 | + | ||
| 81 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 82 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 83 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 84 | + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 85 | + | ||
| 86 | + return nal_unit_type == 7; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +bool SrsRawH264Stream::is_pps(char* frame, int nb_frame) | ||
| 90 | +{ | ||
| 91 | + srs_assert(nb_frame > 0); | ||
| 92 | + | ||
| 93 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 94 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 95 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 96 | + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 97 | + | ||
| 98 | + return nal_unit_type == 8; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +int SrsRawH264Stream::sps_demux(char* frame, int nb_frame, string& sps) | ||
| 102 | +{ | ||
| 103 | + int ret = ERROR_SUCCESS; | ||
| 104 | + | ||
| 105 | + // atleast 1bytes for SPS to decode the type, profile, constrain and level. | ||
| 106 | + if (nb_frame < 4) { | ||
| 107 | + return ret; | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + sps = ""; | ||
| 111 | + if (nb_frame > 0) { | ||
| 112 | + sps.append(frame, nb_frame); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + // should never be empty. | ||
| 116 | + if (sps.empty()) { | ||
| 117 | + return ERROR_STREAM_CASTER_AVC_SPS; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + return ret; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +int SrsRawH264Stream::pps_demux(char* frame, int nb_frame, string& pps) | ||
| 124 | +{ | ||
| 125 | + int ret = ERROR_SUCCESS; | ||
| 126 | + | ||
| 127 | + pps = ""; | ||
| 128 | + if (nb_frame > 0) { | ||
| 129 | + pps.append(frame, nb_frame); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + // should never be empty. | ||
| 133 | + if (pps.empty()) { | ||
| 134 | + return ERROR_STREAM_CASTER_AVC_PPS; | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + return ret; | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +int SrsRawH264Stream::mux_sequence_header(string sps, string pps, u_int32_t dts, u_int32_t pts, string& sh) | ||
| 141 | +{ | ||
| 142 | + int ret = ERROR_SUCCESS; | ||
| 143 | + | ||
| 144 | + // 5bytes sps/pps header: | ||
| 145 | + // configurationVersion, AVCProfileIndication, profile_compatibility, | ||
| 146 | + // AVCLevelIndication, lengthSizeMinusOne | ||
| 147 | + // 3bytes size of sps: | ||
| 148 | + // numOfSequenceParameterSets, sequenceParameterSetLength(2B) | ||
| 149 | + // Nbytes of sps. | ||
| 150 | + // sequenceParameterSetNALUnit | ||
| 151 | + // 3bytes size of pps: | ||
| 152 | + // numOfPictureParameterSets, pictureParameterSetLength | ||
| 153 | + // Nbytes of pps: | ||
| 154 | + // pictureParameterSetNALUnit | ||
| 155 | + int nb_packet = 5 | ||
| 156 | + + 3 + (int)sps.length() | ||
| 157 | + + 3 + (int)pps.length(); | ||
| 158 | + char* packet = new char[nb_packet]; | ||
| 159 | + SrsAutoFree(char, packet); | ||
| 160 | + | ||
| 161 | + // use stream to generate the h264 packet. | ||
| 162 | + SrsStream stream; | ||
| 163 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
| 164 | + return ret; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + // decode the SPS: | ||
| 168 | + // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 | ||
| 169 | + if (true) { | ||
| 170 | + srs_assert((int)sps.length() >= 4); | ||
| 171 | + char* frame = (char*)sps.data(); | ||
| 172 | + | ||
| 173 | + // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205 | ||
| 174 | + // Baseline profile profile_idc is 66(0x42). | ||
| 175 | + // Main profile profile_idc is 77(0x4d). | ||
| 176 | + // Extended profile profile_idc is 88(0x58). | ||
| 177 | + u_int8_t profile_idc = frame[1]; | ||
| 178 | + //u_int8_t constraint_set = frame[2]; | ||
| 179 | + u_int8_t level_idc = frame[3]; | ||
| 180 | + | ||
| 181 | + // generate the sps/pps header | ||
| 182 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 183 | + // configurationVersion | ||
| 184 | + stream.write_1bytes(0x01); | ||
| 185 | + // AVCProfileIndication | ||
| 186 | + stream.write_1bytes(profile_idc); | ||
| 187 | + // profile_compatibility | ||
| 188 | + stream.write_1bytes(0x00); | ||
| 189 | + // AVCLevelIndication | ||
| 190 | + stream.write_1bytes(level_idc); | ||
| 191 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size, | ||
| 192 | + // so we always set it to 0x03. | ||
| 193 | + stream.write_1bytes(0x03); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + // sps | ||
| 197 | + if (true) { | ||
| 198 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 199 | + // numOfSequenceParameterSets, always 1 | ||
| 200 | + stream.write_1bytes(0x01); | ||
| 201 | + // sequenceParameterSetLength | ||
| 202 | + stream.write_2bytes(sps.length()); | ||
| 203 | + // sequenceParameterSetNALUnit | ||
| 204 | + stream.write_string(sps); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + // pps | ||
| 208 | + if (true) { | ||
| 209 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 210 | + // numOfPictureParameterSets, always 1 | ||
| 211 | + stream.write_1bytes(0x01); | ||
| 212 | + // pictureParameterSetLength | ||
| 213 | + stream.write_2bytes(pps.length()); | ||
| 214 | + // pictureParameterSetNALUnit | ||
| 215 | + stream.write_string(pps); | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + // TODO: FIXME: for more profile. | ||
| 219 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 220 | + // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144 | ||
| 221 | + | ||
| 222 | + sh = ""; | ||
| 223 | + sh.append(packet, nb_packet); | ||
| 224 | + | ||
| 225 | + return ret; | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +int SrsRawH264Stream::mux_ipb_frame(char* frame, int nb_frame, u_int32_t dts, u_int32_t pts, string& ibp, int8_t& frame_type) | ||
| 229 | +{ | ||
| 230 | + int ret = ERROR_SUCCESS; | ||
| 231 | + | ||
| 232 | + // 5bits, 7.3.1 NAL unit syntax, | ||
| 233 | + // H.264-AVC-ISO_IEC_14496-10.pdf, page 44. | ||
| 234 | + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame | ||
| 235 | + u_int8_t nal_unit_type = (char)frame[0] & 0x1f; | ||
| 236 | + | ||
| 237 | + // 4bytes size of nalu: | ||
| 238 | + // NALUnitLength | ||
| 239 | + // Nbytes of nalu. | ||
| 240 | + // NALUnit | ||
| 241 | + int nb_packet = 4 + nb_frame; | ||
| 242 | + char* packet = new char[nb_packet]; | ||
| 243 | + SrsAutoFree(char, packet); | ||
| 244 | + | ||
| 245 | + // use stream to generate the h264 packet. | ||
| 246 | + SrsStream stream; | ||
| 247 | + if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) { | ||
| 248 | + return ret; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 | ||
| 252 | + // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size | ||
| 253 | + u_int32_t NAL_unit_length = nb_frame; | ||
| 254 | + | ||
| 255 | + // mux the avc NALU in "ISO Base Media File Format" | ||
| 256 | + // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20 | ||
| 257 | + // NALUnitLength | ||
| 258 | + stream.write_4bytes(NAL_unit_length); | ||
| 259 | + // NALUnit | ||
| 260 | + stream.write_bytes(frame, nb_frame); | ||
| 261 | + | ||
| 262 | + // send out h264 packet. | ||
| 263 | + frame_type = SrsCodecVideoAVCFrameInterFrame; | ||
| 264 | + if (nal_unit_type != 1) { | ||
| 265 | + frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + ibp = ""; | ||
| 269 | + ibp.append(packet, nb_packet); | ||
| 270 | + | ||
| 271 | + return ret; | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | +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) | ||
| 275 | +{ | ||
| 276 | + int ret = ERROR_SUCCESS; | ||
| 277 | + | ||
| 278 | + // for h264 in RTMP video payload, there is 5bytes header: | ||
| 279 | + // 1bytes, FrameType | CodecID | ||
| 280 | + // 1bytes, AVCPacketType | ||
| 281 | + // 3bytes, CompositionTime, the cts. | ||
| 282 | + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
| 283 | + int size = video.length() + 5; | ||
| 284 | + char* data = new char[size]; | ||
| 285 | + char* p = data; | ||
| 286 | + | ||
| 287 | + // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78 | ||
| 288 | + // Frame Type, Type of video frame. | ||
| 289 | + // CodecID, Codec Identifier. | ||
| 290 | + // set the rtmp header | ||
| 291 | + *p++ = (frame_type << 4) | SrsCodecVideoAVC; | ||
| 292 | + | ||
| 293 | + // AVCPacketType | ||
| 294 | + *p++ = avc_packet_type; | ||
| 295 | + | ||
| 296 | + // CompositionTime | ||
| 297 | + // pts = dts + cts, or | ||
| 298 | + // cts = pts - dts. | ||
| 299 | + // where cts is the header in rtmp video packet payload header. | ||
| 300 | + u_int32_t cts = pts - dts; | ||
| 301 | + char* pp = (char*)&cts; | ||
| 302 | + *p++ = pp[2]; | ||
| 303 | + *p++ = pp[1]; | ||
| 304 | + *p++ = pp[0]; | ||
| 305 | + | ||
| 306 | + // h.264 raw data. | ||
| 307 | + memcpy(p, video.data(), video.length()); | ||
| 308 | + | ||
| 309 | + *flv = data; | ||
| 310 | + *nb_flv = size; | ||
| 311 | + | ||
| 312 | + return ret; | ||
| 313 | +} | ||
| 314 | + |
trunk/src/protocol/srs_raw_avc.hpp
0 → 100644
| 1 | +/* | ||
| 2 | +The MIT License (MIT) | ||
| 3 | + | ||
| 4 | +Copyright (c) 2013-2015 winlin | ||
| 5 | + | ||
| 6 | +Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 7 | +this software and associated documentation files (the "Software"), to deal in | ||
| 8 | +the Software without restriction, including without limitation the rights to | ||
| 9 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| 10 | +the Software, and to permit persons to whom the Software is furnished to do so, | ||
| 11 | +subject to the following conditions: | ||
| 12 | + | ||
| 13 | +The above copyright notice and this permission notice shall be included in all | ||
| 14 | +copies or substantial portions of the Software. | ||
| 15 | + | ||
| 16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 17 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
| 18 | +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
| 19 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 20 | +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 21 | +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 22 | +*/ | ||
| 23 | + | ||
| 24 | +#ifndef SRS_PROTOCOL_RAW_AVC_HPP | ||
| 25 | +#define SRS_PROTOCOL_RAW_AVC_HPP | ||
| 26 | + | ||
| 27 | +/* | ||
| 28 | +#include <srs_raw_avc.hpp> | ||
| 29 | +*/ | ||
| 30 | + | ||
| 31 | +#include <srs_core.hpp> | ||
| 32 | + | ||
| 33 | +#include <string> | ||
| 34 | + | ||
| 35 | +class SrsStream; | ||
| 36 | + | ||
| 37 | +/** | ||
| 38 | +* the raw h.264 stream, in annexb. | ||
| 39 | +*/ | ||
| 40 | +class SrsRawH264Stream | ||
| 41 | +{ | ||
| 42 | +public: | ||
| 43 | + SrsRawH264Stream(); | ||
| 44 | + virtual ~SrsRawH264Stream(); | ||
| 45 | +public: | ||
| 46 | + /** | ||
| 47 | + * demux the stream in annexb format. | ||
| 48 | + * @param stream the input stream bytes. | ||
| 49 | + * @param pframe the output h.264 frame in stream. user should never free it. | ||
| 50 | + * @param pnb_frame the output h.264 frame size. | ||
| 51 | + */ | ||
| 52 | + virtual int annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame); | ||
| 53 | + /** | ||
| 54 | + * whether the frame is sps or pps. | ||
| 55 | + */ | ||
| 56 | + virtual bool is_sps(char* frame, int nb_frame); | ||
| 57 | + virtual bool is_pps(char* frame, int nb_frame); | ||
| 58 | + /** | ||
| 59 | + * demux the sps or pps to string. | ||
| 60 | + * @param sps/pps output the sps/pps. | ||
| 61 | + */ | ||
| 62 | + virtual int sps_demux(char* frame, int nb_frame, std::string& sps); | ||
| 63 | + virtual int pps_demux(char* frame, int nb_frame, std::string& pps); | ||
| 64 | +public: | ||
| 65 | + /** | ||
| 66 | + * h264 raw data to h264 packet, without flv payload header. | ||
| 67 | + * mux the sps/pps to flv sequence header packet. | ||
| 68 | + * @param sh output the sequence header. | ||
| 69 | + */ | ||
| 70 | + virtual int mux_sequence_header(std::string sps, std::string pps, u_int32_t dts, u_int32_t pts, std::string& sh); | ||
| 71 | + /** | ||
| 72 | + * h264 raw data to h264 packet, without flv payload header. | ||
| 73 | + * mux the ibp to flv ibp packet. | ||
| 74 | + * @param ibp output the packet. | ||
| 75 | + * @param frame_type output the frame type. | ||
| 76 | + */ | ||
| 77 | + 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); | ||
| 78 | + /** | ||
| 79 | + * mux the avc video packet to flv video packet. | ||
| 80 | + * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame. | ||
| 81 | + * @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU. | ||
| 82 | + * @param video the h.264 raw data. | ||
| 83 | + * @param flv output the muxed flv packet. | ||
| 84 | + * @param nb_flv output the muxed flv size. | ||
| 85 | + */ | ||
| 86 | + 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); | ||
| 87 | +}; | ||
| 88 | + | ||
| 89 | +#endif |
| @@ -30,6 +30,7 @@ using namespace std; | @@ -30,6 +30,7 @@ using namespace std; | ||
| 30 | #include <srs_kernel_utility.hpp> | 30 | #include <srs_kernel_utility.hpp> |
| 31 | #include <srs_kernel_stream.hpp> | 31 | #include <srs_kernel_stream.hpp> |
| 32 | #include <srs_rtmp_stack.hpp> | 32 | #include <srs_rtmp_stack.hpp> |
| 33 | +#include <srs_kernel_codec.hpp> | ||
| 33 | 34 | ||
| 34 | void srs_discovery_tc_url( | 35 | void srs_discovery_tc_url( |
| 35 | string tcUrl, | 36 | string tcUrl, |
| @@ -287,3 +288,61 @@ int srs_chunk_header_c3( | @@ -287,3 +288,61 @@ int srs_chunk_header_c3( | ||
| 287 | return p - cache; | 288 | return p - cache; |
| 288 | } | 289 | } |
| 289 | 290 | ||
| 291 | +int __srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) | ||
| 292 | +{ | ||
| 293 | + int ret = ERROR_SUCCESS; | ||
| 294 | + | ||
| 295 | + *ppmsg = NULL; | ||
| 296 | + SrsSharedPtrMessage* msg = NULL; | ||
| 297 | + | ||
| 298 | + if (type == SrsCodecFlvTagAudio) { | ||
| 299 | + SrsMessageHeader header; | ||
| 300 | + header.initialize_audio(size, timestamp, stream_id); | ||
| 301 | + | ||
| 302 | + msg = new SrsSharedPtrMessage(); | ||
| 303 | + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 304 | + srs_freep(msg); | ||
| 305 | + return ret; | ||
| 306 | + } | ||
| 307 | + } else if (type == SrsCodecFlvTagVideo) { | ||
| 308 | + SrsMessageHeader header; | ||
| 309 | + header.initialize_video(size, timestamp, stream_id); | ||
| 310 | + | ||
| 311 | + msg = new SrsSharedPtrMessage(); | ||
| 312 | + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 313 | + srs_freep(msg); | ||
| 314 | + return ret; | ||
| 315 | + } | ||
| 316 | + } else if (type == SrsCodecFlvTagScript) { | ||
| 317 | + SrsMessageHeader header; | ||
| 318 | + header.initialize_amf0_script(size, stream_id); | ||
| 319 | + | ||
| 320 | + msg = new SrsSharedPtrMessage(); | ||
| 321 | + if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) { | ||
| 322 | + srs_freep(msg); | ||
| 323 | + return ret; | ||
| 324 | + } | ||
| 325 | + } else { | ||
| 326 | + ret = ERROR_STREAM_CASTER_FLV_TAG; | ||
| 327 | + srs_error("rtmp unknown tag type=%#x. ret=%d", type, ret); | ||
| 328 | + return ret; | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + *ppmsg = msg; | ||
| 332 | + | ||
| 333 | + return ret; | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg) | ||
| 337 | +{ | ||
| 338 | + int ret = ERROR_SUCCESS; | ||
| 339 | + | ||
| 340 | + // only when failed, we must free the data. | ||
| 341 | + if ((ret = __srs_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != ERROR_SUCCESS) { | ||
| 342 | + srs_freep(data); | ||
| 343 | + return ret; | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + return ret; | ||
| 347 | +} | ||
| 348 | + |
| @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 34 | #include <srs_kernel_consts.hpp> | 34 | #include <srs_kernel_consts.hpp> |
| 35 | 35 | ||
| 36 | class SrsMessageHeader; | 36 | class SrsMessageHeader; |
| 37 | +class SrsSharedPtrMessage; | ||
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * parse the tcUrl, output the schema, host, vhost, app and port. | 40 | * parse the tcUrl, output the schema, host, vhost, app and port. |
| @@ -110,5 +111,12 @@ extern int srs_chunk_header_c3( | @@ -110,5 +111,12 @@ extern int srs_chunk_header_c3( | ||
| 110 | char* cache, int nb_cache | 111 | char* cache, int nb_cache |
| 111 | ); | 112 | ); |
| 112 | 113 | ||
| 114 | +/** | ||
| 115 | +* create shared ptr message from bytes. | ||
| 116 | +* @param data the packet bytes. user should never free it. | ||
| 117 | +* @param ppmsg output the shared ptr message. user should free it. | ||
| 118 | +*/ | ||
| 119 | +extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg); | ||
| 120 | + | ||
| 113 | #endif | 121 | #endif |
| 114 | 122 |
-
请 注册 或 登录 后发表评论