正在显示
11 个修改的文件
包含
670 行增加
和
80 行删除
| 1 | #Simple-RTMP-Server | 1 | #Simple-RTMP-Server |
| 2 | 2 | ||
| 3 | -<<<<<<< HEAD | ||
| 4 | SRS/2.0,开发代号:[ZhouGuowen](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#release20) | 3 | SRS/2.0,开发代号:[ZhouGuowen](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#release20) |
| 5 | -======= | ||
| 6 | -SRS/1.0,开发代号:[HuKaiqun](https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Product#release10) | ||
| 7 | ->>>>>>> 1.0release | ||
| 8 | 4 | ||
| 9 | SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。 | 5 | SRS定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码。 |
| 10 | 6 | ||
| @@ -493,6 +489,8 @@ Supported operating systems and hardware: | @@ -493,6 +489,8 @@ Supported operating systems and hardware: | ||
| 493 | [#250](https://github.com/winlinvip/simple-rtmp-server/issues/250). | 489 | [#250](https://github.com/winlinvip/simple-rtmp-server/issues/250). |
| 494 | 1. Rewrite HLS(h.264+aac/mp3) streaming, read | 490 | 1. Rewrite HLS(h.264+aac/mp3) streaming, read |
| 495 | [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304). | 491 | [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304). |
| 492 | +1. Support push RTSP to SRS, read | ||
| 493 | +[#133](https://github.com/winlinvip/simple-rtmp-server/issues/133). | ||
| 496 | 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). | 494 | 1. [no-plan] Support <500ms latency, FRSC(Fast RTMP-compatible Stream Channel tech). |
| 497 | 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). | 495 | 1. [no-plan] Support RTMP 302 redirect [#92](https://github.com/winlinvip/simple-rtmp-server/issues/92). |
| 498 | 1. [no-plan] Support multiple processes, for both origin and edge | 496 | 1. [no-plan] Support multiple processes, for both origin and edge |
| @@ -531,6 +529,7 @@ Supported operating systems and hardware: | @@ -531,6 +529,7 @@ Supported operating systems and hardware: | ||
| 531 | 529 | ||
| 532 | ### SRS 2.0 history | 530 | ### SRS 2.0 history |
| 533 | 531 | ||
| 532 | +* v2.0, 2015-02-18, fix [#133](https://github.com/winlinvip/simple-rtmp-server/issues/133), support push rtsp to srs. 2.0.120. | ||
| 534 | * v2.0, 2015-02-17, the join maybe failed, should use a variable to ensure thread terminated. 2.0.119. | 533 | * v2.0, 2015-02-17, the join maybe failed, should use a variable to ensure thread terminated. 2.0.119. |
| 535 | * v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), support config default acodec/vcodec. 2.0.118. | 534 | * v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), support config default acodec/vcodec. 2.0.118. |
| 536 | * v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite hls/ts code, support h.264+mp3 for hls. 2.0.117. | 535 | * v2.0, 2015-02-15, for [#304](https://github.com/winlinvip/simple-rtmp-server/issues/304), rewrite hls/ts code, support h.264+mp3 for hls. 2.0.117. |
| @@ -35,6 +35,12 @@ using namespace std; | @@ -35,6 +35,12 @@ using namespace std; | ||
| 35 | #include <srs_core_autofree.hpp> | 35 | #include <srs_core_autofree.hpp> |
| 36 | #include <srs_kernel_stream.hpp> | 36 | #include <srs_kernel_stream.hpp> |
| 37 | #include <srs_kernel_buffer.hpp> | 37 | #include <srs_kernel_buffer.hpp> |
| 38 | +#include <srs_rtmp_sdk.hpp> | ||
| 39 | +#include <srs_rtmp_amf0.hpp> | ||
| 40 | +#include <srs_rtmp_utility.hpp> | ||
| 41 | +#include <srs_kernel_utility.hpp> | ||
| 42 | +#include <srs_raw_avc.hpp> | ||
| 43 | +#include <srs_kernel_codec.hpp> | ||
| 38 | 44 | ||
| 39 | #ifdef SRS_AUTO_STREAM_CASTER | 45 | #ifdef SRS_AUTO_STREAM_CASTER |
| 40 | 46 | ||
| @@ -100,15 +106,15 @@ int SrsRtpConn::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | @@ -100,15 +106,15 @@ int SrsRtpConn::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | ||
| 100 | } | 106 | } |
| 101 | } | 107 | } |
| 102 | 108 | ||
| 103 | - srs_trace("rtsp: rtp %dB, vt=%d/%u, sts=%u/%#x/%#x, paylod=%dB, chunked=%d", | ||
| 104 | - nb_buf, cache->version, cache->payload_type, cache->sequence_number, cache->timestamp, cache->ssrc, | 109 | + srs_trace("rtsp: rtp #%d %dB, vt=%d/%u, sts=%u/%u/%#x, paylod=%dB, chunked=%d", |
| 110 | + stream_id, nb_buf, cache->version, cache->payload_type, cache->sequence_number, cache->timestamp, cache->ssrc, | ||
| 105 | cache->payload->length(), cache->chunked | 111 | cache->payload->length(), cache->chunked |
| 106 | ); | 112 | ); |
| 107 | 113 | ||
| 108 | // always free it. | 114 | // always free it. |
| 109 | SrsAutoFree(SrsRtpPacket, cache); | 115 | SrsAutoFree(SrsRtpPacket, cache); |
| 110 | 116 | ||
| 111 | - if ((ret = rtsp->on_rtp_packet(cache)) != ERROR_SUCCESS) { | 117 | + if ((ret = rtsp->on_rtp_packet(cache, stream_id)) != ERROR_SUCCESS) { |
| 112 | srs_error("rtsp: process rtp packet failed. ret=%d", ret); | 118 | srs_error("rtsp: process rtp packet failed. ret=%d", ret); |
| 113 | return ret; | 119 | return ret; |
| 114 | } | 120 | } |
| @@ -116,9 +122,59 @@ int SrsRtpConn::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | @@ -116,9 +122,59 @@ int SrsRtpConn::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) | ||
| 116 | return ret; | 122 | return ret; |
| 117 | } | 123 | } |
| 118 | 124 | ||
| 125 | +SrsRtspAudioCache::SrsRtspAudioCache() | ||
| 126 | +{ | ||
| 127 | + dts = NULL; | ||
| 128 | + audio_samples = NULL; | ||
| 129 | + payload = NULL; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +SrsRtspAudioCache::~SrsRtspAudioCache() | ||
| 133 | +{ | ||
| 134 | + srs_freep(audio_samples); | ||
| 135 | + srs_freep(payload); | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +SrsRtspJitter::SrsRtspJitter() | ||
| 139 | +{ | ||
| 140 | + delta = 0; | ||
| 141 | + previous_timestamp = 0; | ||
| 142 | + pts = 0; | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +SrsRtspJitter::~SrsRtspJitter() | ||
| 146 | +{ | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +int64_t SrsRtspJitter::timestamp() | ||
| 150 | +{ | ||
| 151 | + return pts; | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +int SrsRtspJitter::correct(int64_t& ts) | ||
| 155 | +{ | ||
| 156 | + int ret = ERROR_SUCCESS; | ||
| 157 | + | ||
| 158 | + if (previous_timestamp == 0) { | ||
| 159 | + previous_timestamp = ts; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + delta = srs_max(0, ts - previous_timestamp); | ||
| 163 | + if (delta > 90000) { | ||
| 164 | + delta = 0; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + previous_timestamp = ts; | ||
| 168 | + | ||
| 169 | + ts = pts + delta; | ||
| 170 | + pts = ts; | ||
| 171 | + | ||
| 172 | + return ret; | ||
| 173 | +} | ||
| 174 | + | ||
| 119 | SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) | 175 | SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) |
| 120 | { | 176 | { |
| 121 | - output = o; | 177 | + output_template = o; |
| 122 | 178 | ||
| 123 | session = ""; | 179 | session = ""; |
| 124 | video_rtp = NULL; | 180 | video_rtp = NULL; |
| @@ -129,6 +185,18 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) | @@ -129,6 +185,18 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) | ||
| 129 | skt = new SrsStSocket(fd); | 185 | skt = new SrsStSocket(fd); |
| 130 | rtsp = new SrsRtspStack(skt); | 186 | rtsp = new SrsRtspStack(skt); |
| 131 | trd = new SrsThread("rtsp", this, 0, false); | 187 | trd = new SrsThread("rtsp", this, 0, false); |
| 188 | + | ||
| 189 | + req = NULL; | ||
| 190 | + io = NULL; | ||
| 191 | + client = NULL; | ||
| 192 | + stream_id = 0; | ||
| 193 | + vjitter = new SrsRtspJitter(); | ||
| 194 | + ajitter = new SrsRtspJitter(); | ||
| 195 | + | ||
| 196 | + avc = new SrsRawH264Stream(); | ||
| 197 | + aac = new SrsRawAacStream(); | ||
| 198 | + acodec = new SrsRawAacStreamCodec(); | ||
| 199 | + acache = new SrsRtspAudioCache(); | ||
| 132 | } | 200 | } |
| 133 | 201 | ||
| 134 | SrsRtspConn::~SrsRtspConn() | 202 | SrsRtspConn::~SrsRtspConn() |
| @@ -142,6 +210,13 @@ SrsRtspConn::~SrsRtspConn() | @@ -142,6 +210,13 @@ SrsRtspConn::~SrsRtspConn() | ||
| 142 | srs_freep(trd); | 210 | srs_freep(trd); |
| 143 | srs_freep(skt); | 211 | srs_freep(skt); |
| 144 | srs_freep(rtsp); | 212 | srs_freep(rtsp); |
| 213 | + | ||
| 214 | + close(); | ||
| 215 | + | ||
| 216 | + srs_freep(vjitter); | ||
| 217 | + srs_freep(ajitter); | ||
| 218 | + srs_freep(acodec); | ||
| 219 | + srs_freep(acache); | ||
| 145 | } | 220 | } |
| 146 | 221 | ||
| 147 | int SrsRtspConn::serve() | 222 | int SrsRtspConn::serve() |
| @@ -179,6 +254,18 @@ int SrsRtspConn::do_cycle() | @@ -179,6 +254,18 @@ int SrsRtspConn::do_cycle() | ||
| 179 | return ret; | 254 | return ret; |
| 180 | } | 255 | } |
| 181 | } else if (req->is_announce()) { | 256 | } else if (req->is_announce()) { |
| 257 | + if (rtsp_tcUrl.empty()) { | ||
| 258 | + rtsp_tcUrl = req->uri; | ||
| 259 | + } | ||
| 260 | + size_t pos = string::npos; | ||
| 261 | + if ((pos = rtsp_tcUrl.rfind(".sdp")) != string::npos) { | ||
| 262 | + rtsp_tcUrl = rtsp_tcUrl.substr(0, pos); | ||
| 263 | + } | ||
| 264 | + if ((pos = rtsp_tcUrl.rfind("/")) != string::npos) { | ||
| 265 | + rtsp_stream = rtsp_tcUrl.substr(pos + 1); | ||
| 266 | + rtsp_tcUrl = rtsp_tcUrl.substr(0, pos); | ||
| 267 | + } | ||
| 268 | + | ||
| 182 | srs_assert(req->sdp); | 269 | srs_assert(req->sdp); |
| 183 | video_id = ::atoi(req->sdp->video_stream_id.c_str()); | 270 | video_id = ::atoi(req->sdp->video_stream_id.c_str()); |
| 184 | audio_id = ::atoi(req->sdp->audio_stream_id.c_str()); | 271 | audio_id = ::atoi(req->sdp->audio_stream_id.c_str()); |
| @@ -186,13 +273,13 @@ int SrsRtspConn::do_cycle() | @@ -186,13 +273,13 @@ int SrsRtspConn::do_cycle() | ||
| 186 | audio_codec = req->sdp->audio_codec; | 273 | audio_codec = req->sdp->audio_codec; |
| 187 | audio_sample_rate = ::atoi(req->sdp->audio_sample_rate.c_str()); | 274 | audio_sample_rate = ::atoi(req->sdp->audio_sample_rate.c_str()); |
| 188 | audio_channel = ::atoi(req->sdp->audio_channel.c_str()); | 275 | audio_channel = ::atoi(req->sdp->audio_channel.c_str()); |
| 189 | - sps = req->sdp->video_sps; | ||
| 190 | - pps = req->sdp->video_pps; | ||
| 191 | - asc = req->sdp->audio_sh; | ||
| 192 | - srs_trace("rtsp: video(#%d, %s, %s/%s), audio(#%d, %s, %s/%s, %dHZ %dchannels)", | 276 | + h264_sps = req->sdp->video_sps; |
| 277 | + h264_pps = req->sdp->video_pps; | ||
| 278 | + aac_specific_config = req->sdp->audio_sh; | ||
| 279 | + srs_trace("rtsp: video(#%d, %s, %s/%s), audio(#%d, %s, %s/%s, %dHZ %dchannels), %s/%s", | ||
| 193 | video_id, video_codec.c_str(), req->sdp->video_protocol.c_str(), req->sdp->video_transport_format.c_str(), | 280 | video_id, video_codec.c_str(), req->sdp->video_protocol.c_str(), req->sdp->video_transport_format.c_str(), |
| 194 | audio_id, audio_codec.c_str(), req->sdp->audio_protocol.c_str(), req->sdp->audio_transport_format.c_str(), | 281 | audio_id, audio_codec.c_str(), req->sdp->audio_protocol.c_str(), req->sdp->audio_transport_format.c_str(), |
| 195 | - audio_sample_rate, audio_channel | 282 | + audio_sample_rate, audio_channel, rtsp_tcUrl.c_str(), rtsp_stream.c_str() |
| 196 | ); | 283 | ); |
| 197 | 284 | ||
| 198 | SrsRtspResponse* res = new SrsRtspResponse(req->seq); | 285 | SrsRtspResponse* res = new SrsRtspResponse(req->seq); |
| @@ -262,9 +349,38 @@ int SrsRtspConn::do_cycle() | @@ -262,9 +349,38 @@ int SrsRtspConn::do_cycle() | ||
| 262 | return ret; | 349 | return ret; |
| 263 | } | 350 | } |
| 264 | 351 | ||
| 265 | -int SrsRtspConn::on_rtp_packet(SrsRtpPacket* pkt) | 352 | +int SrsRtspConn::on_rtp_packet(SrsRtpPacket* pkt, int stream_id) |
| 266 | { | 353 | { |
| 267 | int ret = ERROR_SUCCESS; | 354 | int ret = ERROR_SUCCESS; |
| 355 | + | ||
| 356 | + // ensure rtmp connected. | ||
| 357 | + if ((ret = connect()) != ERROR_SUCCESS) { | ||
| 358 | + return ret; | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + if (stream_id == video_id) { | ||
| 362 | + // rtsp tbn is ts tbn. | ||
| 363 | + int64_t pts = pkt->timestamp; | ||
| 364 | + if ((ret = vjitter->correct(pts)) != ERROR_SUCCESS) { | ||
| 365 | + srs_error("rtsp: correct by jitter failed. ret=%d", ret); | ||
| 366 | + return ret; | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + // TODO: FIXME: set dts to pts, please finger out the right dts. | ||
| 370 | + int64_t dts = pts; | ||
| 371 | + | ||
| 372 | + return on_rtp_video(pkt, dts, pts); | ||
| 373 | + } else { | ||
| 374 | + // rtsp tbn is ts tbn. | ||
| 375 | + int64_t pts = pkt->timestamp; | ||
| 376 | + if ((ret = ajitter->correct(pts)) != ERROR_SUCCESS) { | ||
| 377 | + srs_error("rtsp: correct by jitter failed. ret=%d", ret); | ||
| 378 | + return ret; | ||
| 379 | + } | ||
| 380 | + | ||
| 381 | + return on_rtp_audio(pkt, pts); | ||
| 382 | + } | ||
| 383 | + | ||
| 268 | return ret; | 384 | return ret; |
| 269 | } | 385 | } |
| 270 | 386 | ||
| @@ -307,6 +423,336 @@ void SrsRtspConn::on_thread_stop() | @@ -307,6 +423,336 @@ void SrsRtspConn::on_thread_stop() | ||
| 307 | caster->remove(this); | 423 | caster->remove(this); |
| 308 | } | 424 | } |
| 309 | 425 | ||
| 426 | +int SrsRtspConn::on_rtp_video(SrsRtpPacket* pkt, int64_t dts, int64_t pts) | ||
| 427 | +{ | ||
| 428 | + int ret = ERROR_SUCCESS; | ||
| 429 | + | ||
| 430 | + if ((ret = kickoff_audio_cache(pkt, dts)) != ERROR_SUCCESS) { | ||
| 431 | + return ret; | ||
| 432 | + } | ||
| 433 | + | ||
| 434 | + if ((ret = write_h264_ipb_frame(pkt->payload->bytes(), pkt->payload->length(), dts / 90, pts / 90)) != ERROR_SUCCESS) { | ||
| 435 | + return ret; | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + return ret; | ||
| 439 | +} | ||
| 440 | + | ||
| 441 | +int SrsRtspConn::on_rtp_audio(SrsRtpPacket* pkt, int64_t dts) | ||
| 442 | +{ | ||
| 443 | + int ret = ERROR_SUCCESS; | ||
| 444 | + | ||
| 445 | + if ((ret = kickoff_audio_cache(pkt, dts)) != ERROR_SUCCESS) { | ||
| 446 | + return ret; | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + // cache current audio to kickoff. | ||
| 450 | + acache->dts = dts; | ||
| 451 | + acache->audio_samples = pkt->audio_samples; | ||
| 452 | + acache->payload = pkt->payload; | ||
| 453 | + | ||
| 454 | + pkt->audio_samples = NULL; | ||
| 455 | + pkt->payload = NULL; | ||
| 456 | + | ||
| 457 | + return ret; | ||
| 458 | +} | ||
| 459 | + | ||
| 460 | +int SrsRtspConn::kickoff_audio_cache(SrsRtpPacket* pkt, int64_t dts) | ||
| 461 | +{ | ||
| 462 | + int ret = ERROR_SUCCESS; | ||
| 463 | + | ||
| 464 | + // nothing to kick off. | ||
| 465 | + if (!acache->payload) { | ||
| 466 | + return ret; | ||
| 467 | + } | ||
| 468 | + | ||
| 469 | + if (dts - acache->dts > 0 && acache->audio_samples->nb_sample_units > 0) { | ||
| 470 | + int64_t delta = (dts - acache->dts) / acache->audio_samples->nb_sample_units; | ||
| 471 | + for (int i = 0; i < acache->audio_samples->nb_sample_units; i++) { | ||
| 472 | + char* frame = acache->audio_samples->sample_units[i].bytes; | ||
| 473 | + int nb_frame = acache->audio_samples->sample_units[i].size; | ||
| 474 | + int64_t timestamp = (acache->dts + delta * i) / 90; | ||
| 475 | + acodec->aac_packet_type = 1; | ||
| 476 | + if ((ret = write_audio_raw_frame(frame, nb_frame, acodec, timestamp)) != ERROR_SUCCESS) { | ||
| 477 | + return ret; | ||
| 478 | + } | ||
| 479 | + } | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + acache->dts = 0; | ||
| 483 | + srs_freep(acache->audio_samples); | ||
| 484 | + srs_freep(acache->payload); | ||
| 485 | + | ||
| 486 | + return ret; | ||
| 487 | +} | ||
| 488 | + | ||
| 489 | +int SrsRtspConn::write_sequence_header() | ||
| 490 | +{ | ||
| 491 | + int ret = ERROR_SUCCESS; | ||
| 492 | + | ||
| 493 | + // use the current dts. | ||
| 494 | + int64_t dts = vjitter->timestamp() / 90; | ||
| 495 | + | ||
| 496 | + // send video sps/pps | ||
| 497 | + if ((ret = write_h264_sps_pps(dts, dts)) != ERROR_SUCCESS) { | ||
| 498 | + return ret; | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + // generate audio sh by audio specific config. | ||
| 502 | + if (true) { | ||
| 503 | + std::string sh = aac_specific_config; | ||
| 504 | + | ||
| 505 | + SrsAvcAacCodec dec; | ||
| 506 | + if ((ret = dec.audio_aac_sequence_header_demux((char*)sh.c_str(), (int)sh.length())) != ERROR_SUCCESS) { | ||
| 507 | + return ret; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + acodec->sound_format = SrsCodecAudioAAC; | ||
| 511 | + acodec->sound_type = (dec.aac_channels == 2)? SrsCodecAudioSoundTypeStereo : SrsCodecAudioSoundTypeMono; | ||
| 512 | + acodec->sound_size = SrsCodecAudioSampleSize16bit; | ||
| 513 | + acodec->aac_packet_type = 0; | ||
| 514 | + | ||
| 515 | + static int aac_sample_rates[] = { | ||
| 516 | + 96000, 88200, 64000, 48000, | ||
| 517 | + 44100, 32000, 24000, 22050, | ||
| 518 | + 16000, 12000, 11025, 8000, | ||
| 519 | + 7350, 0, 0, 0 | ||
| 520 | + }; | ||
| 521 | + switch (aac_sample_rates[dec.aac_sample_rate]) { | ||
| 522 | + case 11025: | ||
| 523 | + acodec->sound_rate = SrsCodecAudioSampleRate11025; | ||
| 524 | + break; | ||
| 525 | + case 22050: | ||
| 526 | + acodec->sound_rate = SrsCodecAudioSampleRate22050; | ||
| 527 | + break; | ||
| 528 | + case 44100: | ||
| 529 | + acodec->sound_rate = SrsCodecAudioSampleRate44100; | ||
| 530 | + break; | ||
| 531 | + default: | ||
| 532 | + break; | ||
| 533 | + }; | ||
| 534 | + | ||
| 535 | + if ((ret = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), acodec, dts)) != ERROR_SUCCESS) { | ||
| 536 | + return ret; | ||
| 537 | + } | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + return ret; | ||
| 541 | +} | ||
| 542 | + | ||
| 543 | +int SrsRtspConn::write_h264_sps_pps(u_int32_t dts, u_int32_t pts) | ||
| 544 | +{ | ||
| 545 | + int ret = ERROR_SUCCESS; | ||
| 546 | + | ||
| 547 | + // h264 raw to h264 packet. | ||
| 548 | + std::string sh; | ||
| 549 | + if ((ret = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != ERROR_SUCCESS) { | ||
| 550 | + return ret; | ||
| 551 | + } | ||
| 552 | + | ||
| 553 | + // h264 packet to flv packet. | ||
| 554 | + int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame; | ||
| 555 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader; | ||
| 556 | + char* flv = NULL; | ||
| 557 | + int nb_flv = 0; | ||
| 558 | + if ((ret = avc->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 559 | + return ret; | ||
| 560 | + } | ||
| 561 | + | ||
| 562 | + // the timestamp in rtmp message header is dts. | ||
| 563 | + u_int32_t timestamp = dts; | ||
| 564 | + if ((ret = rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv)) != ERROR_SUCCESS) { | ||
| 565 | + return ret; | ||
| 566 | + } | ||
| 567 | + | ||
| 568 | + return ret; | ||
| 569 | +} | ||
| 570 | + | ||
| 571 | +int SrsRtspConn::write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts) | ||
| 572 | +{ | ||
| 573 | + int ret = ERROR_SUCCESS; | ||
| 574 | + | ||
| 575 | + std::string ibp; | ||
| 576 | + int8_t frame_type; | ||
| 577 | + if ((ret = avc->mux_ipb_frame(frame, frame_size, dts, pts, ibp, frame_type)) != ERROR_SUCCESS) { | ||
| 578 | + return ret; | ||
| 579 | + } | ||
| 580 | + | ||
| 581 | + int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU; | ||
| 582 | + char* flv = NULL; | ||
| 583 | + int nb_flv = 0; | ||
| 584 | + if ((ret = avc->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) { | ||
| 585 | + return ret; | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + // the timestamp in rtmp message header is dts. | ||
| 589 | + u_int32_t timestamp = dts; | ||
| 590 | + return rtmp_write_packet(SrsCodecFlvTagVideo, timestamp, flv, nb_flv); | ||
| 591 | +} | ||
| 592 | + | ||
| 593 | +int SrsRtspConn::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts) | ||
| 594 | +{ | ||
| 595 | + int ret = ERROR_SUCCESS; | ||
| 596 | + | ||
| 597 | + char* data = NULL; | ||
| 598 | + int size = 0; | ||
| 599 | + if ((ret = aac->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != ERROR_SUCCESS) { | ||
| 600 | + return ret; | ||
| 601 | + } | ||
| 602 | + | ||
| 603 | + return rtmp_write_packet(SrsCodecFlvTagAudio, dts, data, size); | ||
| 604 | +} | ||
| 605 | + | ||
| 606 | +int SrsRtspConn::rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size) | ||
| 607 | +{ | ||
| 608 | + int ret = ERROR_SUCCESS; | ||
| 609 | + | ||
| 610 | + SrsSharedPtrMessage* msg = NULL; | ||
| 611 | + | ||
| 612 | + if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, stream_id, &msg)) != ERROR_SUCCESS) { | ||
| 613 | + srs_error("rtsp: create shared ptr msg failed. ret=%d", ret); | ||
| 614 | + return ret; | ||
| 615 | + } | ||
| 616 | + srs_assert(msg); | ||
| 617 | + | ||
| 618 | + // send out encoded msg. | ||
| 619 | + if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { | ||
| 620 | + return ret; | ||
| 621 | + } | ||
| 622 | + | ||
| 623 | + return ret; | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | +// TODO: FIXME: merge all client code. | ||
| 627 | +int SrsRtspConn::connect() | ||
| 628 | +{ | ||
| 629 | + int ret = ERROR_SUCCESS; | ||
| 630 | + | ||
| 631 | + // when ok, ignore. | ||
| 632 | + if (io || client) { | ||
| 633 | + return ret; | ||
| 634 | + } | ||
| 635 | + | ||
| 636 | + // parse uri | ||
| 637 | + if (!req) { | ||
| 638 | + req = new SrsRequest(); | ||
| 639 | + | ||
| 640 | + std::string schema, host, vhost, app, port, param; | ||
| 641 | + srs_discovery_tc_url(rtsp_tcUrl, schema, host, vhost, app, port, param); | ||
| 642 | + | ||
| 643 | + // generate output by template. | ||
| 644 | + std::string output = output_template; | ||
| 645 | + output = srs_string_replace(output, "[app]", app); | ||
| 646 | + output = srs_string_replace(output, "[stream]", rtsp_stream); | ||
| 647 | + | ||
| 648 | + size_t pos = string::npos; | ||
| 649 | + string uri = req->tcUrl = output; | ||
| 650 | + | ||
| 651 | + // tcUrl, stream | ||
| 652 | + if ((pos = uri.rfind("/")) != string::npos) { | ||
| 653 | + req->stream = uri.substr(pos + 1); | ||
| 654 | + req->tcUrl = uri = uri.substr(0, pos); | ||
| 655 | + } | ||
| 656 | + | ||
| 657 | + srs_discovery_tc_url(req->tcUrl, | ||
| 658 | + req->schema, req->host, req->vhost, req->app, req->port, | ||
| 659 | + req->param); | ||
| 660 | + } | ||
| 661 | + | ||
| 662 | + // connect host. | ||
| 663 | + if ((ret = srs_socket_connect(req->host, ::atoi(req->port.c_str()), ST_UTIME_NO_TIMEOUT, &stfd)) != ERROR_SUCCESS) { | ||
| 664 | + srs_error("rtsp: connect server %s:%s failed. ret=%d", req->host.c_str(), req->port.c_str(), ret); | ||
| 665 | + return ret; | ||
| 666 | + } | ||
| 667 | + io = new SrsStSocket(stfd); | ||
| 668 | + client = new SrsRtmpClient(io); | ||
| 669 | + | ||
| 670 | + client->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US); | ||
| 671 | + client->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US); | ||
| 672 | + | ||
| 673 | + // connect to vhost/app | ||
| 674 | + if ((ret = client->handshake()) != ERROR_SUCCESS) { | ||
| 675 | + srs_error("rtsp: handshake with server failed. ret=%d", ret); | ||
| 676 | + return ret; | ||
| 677 | + } | ||
| 678 | + if ((ret = connect_app(req->host, req->port)) != ERROR_SUCCESS) { | ||
| 679 | + srs_error("rtsp: connect with server failed. ret=%d", ret); | ||
| 680 | + return ret; | ||
| 681 | + } | ||
| 682 | + if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) { | ||
| 683 | + srs_error("rtsp: connect with server failed, stream_id=%d. ret=%d", stream_id, ret); | ||
| 684 | + return ret; | ||
| 685 | + } | ||
| 686 | + | ||
| 687 | + // publish. | ||
| 688 | + if ((ret = client->publish(req->stream, stream_id)) != ERROR_SUCCESS) { | ||
| 689 | + srs_error("rtsp: publish failed, stream=%s, stream_id=%d. ret=%d", | ||
| 690 | + req->stream.c_str(), stream_id, ret); | ||
| 691 | + return ret; | ||
| 692 | + } | ||
| 693 | + | ||
| 694 | + return write_sequence_header(); | ||
| 695 | +} | ||
| 696 | + | ||
| 697 | +// TODO: FIXME: refine the connect_app. | ||
| 698 | +int SrsRtspConn::connect_app(string ep_server, string ep_port) | ||
| 699 | +{ | ||
| 700 | + int ret = ERROR_SUCCESS; | ||
| 701 | + | ||
| 702 | + // args of request takes the srs info. | ||
| 703 | + if (req->args == NULL) { | ||
| 704 | + req->args = SrsAmf0Any::object(); | ||
| 705 | + } | ||
| 706 | + | ||
| 707 | + // notify server the edge identity, | ||
| 708 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/147 | ||
| 709 | + SrsAmf0Object* data = req->args; | ||
| 710 | + data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); | ||
| 711 | + data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); | ||
| 712 | + data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); | ||
| 713 | + data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); | ||
| 714 | + data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); | ||
| 715 | + data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); | ||
| 716 | + data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); | ||
| 717 | + data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); | ||
| 718 | + data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); | ||
| 719 | + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); | ||
| 720 | + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); | ||
| 721 | + // for edge to directly get the id of client. | ||
| 722 | + data->set("srs_pid", SrsAmf0Any::number(getpid())); | ||
| 723 | + data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); | ||
| 724 | + | ||
| 725 | + // local ip of edge | ||
| 726 | + std::vector<std::string> ips = srs_get_local_ipv4_ips(); | ||
| 727 | + assert(_srs_config->get_stats_network() < (int)ips.size()); | ||
| 728 | + std::string local_ip = ips[_srs_config->get_stats_network()]; | ||
| 729 | + data->set("srs_server_ip", SrsAmf0Any::str(local_ip.c_str())); | ||
| 730 | + | ||
| 731 | + // generate the tcUrl | ||
| 732 | + std::string param = ""; | ||
| 733 | + std::string tc_url = srs_generate_tc_url(ep_server, req->vhost, req->app, ep_port, param); | ||
| 734 | + | ||
| 735 | + // upnode server identity will show in the connect_app of client. | ||
| 736 | + // @see https://github.com/winlinvip/simple-rtmp-server/issues/160 | ||
| 737 | + // the debug_srs_upnode is config in vhost and default to true. | ||
| 738 | + bool debug_srs_upnode = _srs_config->get_debug_srs_upnode(req->vhost); | ||
| 739 | + if ((ret = client->connect_app(req->app, tc_url, req, debug_srs_upnode)) != ERROR_SUCCESS) { | ||
| 740 | + srs_error("rtsp: connect with server failed, tcUrl=%s, dsu=%d. ret=%d", | ||
| 741 | + tc_url.c_str(), debug_srs_upnode, ret); | ||
| 742 | + return ret; | ||
| 743 | + } | ||
| 744 | + | ||
| 745 | + return ret; | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | +void SrsRtspConn::close() | ||
| 749 | +{ | ||
| 750 | + srs_freep(client); | ||
| 751 | + srs_freep(io); | ||
| 752 | + srs_freep(req); | ||
| 753 | + srs_close_stfd(stfd); | ||
| 754 | +} | ||
| 755 | + | ||
| 310 | SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c) | 756 | SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c) |
| 311 | { | 757 | { |
| 312 | // TODO: FIXME: support reload. | 758 | // TODO: FIXME: support reload. |
| @@ -46,6 +46,15 @@ class SrsRtspStack; | @@ -46,6 +46,15 @@ class SrsRtspStack; | ||
| 46 | class SrsRtspCaster; | 46 | class SrsRtspCaster; |
| 47 | class SrsConfDirective; | 47 | class SrsConfDirective; |
| 48 | class SrsRtpPacket; | 48 | class SrsRtpPacket; |
| 49 | +class SrsRequest; | ||
| 50 | +class SrsStSocket; | ||
| 51 | +class SrsRtmpClient; | ||
| 52 | +class SrsRawH264Stream; | ||
| 53 | +class SrsRawAacStream; | ||
| 54 | +class SrsRawAacStreamCodec; | ||
| 55 | +class SrsSharedPtrMessage; | ||
| 56 | +class SrsCodecSample; | ||
| 57 | +class SrsSimpleBuffer; | ||
| 49 | 58 | ||
| 50 | /** | 59 | /** |
| 51 | * a rtp connection which transport a stream. | 60 | * a rtp connection which transport a stream. |
| @@ -70,12 +79,45 @@ public: | @@ -70,12 +79,45 @@ public: | ||
| 70 | }; | 79 | }; |
| 71 | 80 | ||
| 72 | /** | 81 | /** |
| 82 | +* audio is group by frames. | ||
| 83 | +*/ | ||
| 84 | +struct SrsRtspAudioCache | ||
| 85 | +{ | ||
| 86 | + int64_t dts; | ||
| 87 | + SrsCodecSample* audio_samples; | ||
| 88 | + SrsSimpleBuffer* payload; | ||
| 89 | + | ||
| 90 | + SrsRtspAudioCache(); | ||
| 91 | + virtual ~SrsRtspAudioCache(); | ||
| 92 | +}; | ||
| 93 | + | ||
| 94 | +/** | ||
| 95 | +* the time jitter correct for rtsp. | ||
| 96 | +*/ | ||
| 97 | +class SrsRtspJitter | ||
| 98 | +{ | ||
| 99 | +private: | ||
| 100 | + int64_t previous_timestamp; | ||
| 101 | + int64_t pts; | ||
| 102 | + int delta; | ||
| 103 | +public: | ||
| 104 | + SrsRtspJitter(); | ||
| 105 | + virtual ~SrsRtspJitter(); | ||
| 106 | +public: | ||
| 107 | + virtual int64_t timestamp(); | ||
| 108 | + virtual int correct(int64_t& ts); | ||
| 109 | +}; | ||
| 110 | + | ||
| 111 | +/** | ||
| 73 | * the rtsp connection serve the fd. | 112 | * the rtsp connection serve the fd. |
| 74 | */ | 113 | */ |
| 75 | class SrsRtspConn : public ISrsThreadHandler | 114 | class SrsRtspConn : public ISrsThreadHandler |
| 76 | { | 115 | { |
| 77 | private: | 116 | private: |
| 78 | - std::string output; | 117 | + std::string output_template; |
| 118 | + std::string rtsp_tcUrl; | ||
| 119 | + std::string rtsp_stream; | ||
| 120 | + | ||
| 79 | private: | 121 | private: |
| 80 | std::string session; | 122 | std::string session; |
| 81 | // video stream. | 123 | // video stream. |
| @@ -88,17 +130,28 @@ private: | @@ -88,17 +130,28 @@ private: | ||
| 88 | int audio_sample_rate; | 130 | int audio_sample_rate; |
| 89 | int audio_channel; | 131 | int audio_channel; |
| 90 | SrsRtpConn* audio_rtp; | 132 | SrsRtpConn* audio_rtp; |
| 91 | - // video sequence header. | ||
| 92 | - std::string sps; | ||
| 93 | - std::string pps; | ||
| 94 | - // audio sequence header. | ||
| 95 | - std::string asc; | ||
| 96 | private: | 133 | private: |
| 97 | st_netfd_t stfd; | 134 | st_netfd_t stfd; |
| 98 | SrsStSocket* skt; | 135 | SrsStSocket* skt; |
| 99 | SrsRtspStack* rtsp; | 136 | SrsRtspStack* rtsp; |
| 100 | SrsRtspCaster* caster; | 137 | SrsRtspCaster* caster; |
| 101 | SrsThread* trd; | 138 | SrsThread* trd; |
| 139 | +private: | ||
| 140 | + SrsRequest* req; | ||
| 141 | + SrsStSocket* io; | ||
| 142 | + SrsRtmpClient* client; | ||
| 143 | + SrsRtspJitter* vjitter; | ||
| 144 | + SrsRtspJitter* ajitter; | ||
| 145 | + int stream_id; | ||
| 146 | +private: | ||
| 147 | + SrsRawH264Stream* avc; | ||
| 148 | + std::string h264_sps; | ||
| 149 | + std::string h264_pps; | ||
| 150 | +private: | ||
| 151 | + SrsRawAacStream* aac; | ||
| 152 | + SrsRawAacStreamCodec* acodec; | ||
| 153 | + std::string aac_specific_config; | ||
| 154 | + SrsRtspAudioCache* acache; | ||
| 102 | public: | 155 | public: |
| 103 | SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o); | 156 | SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o); |
| 104 | virtual ~SrsRtspConn(); | 157 | virtual ~SrsRtspConn(); |
| @@ -108,11 +161,28 @@ private: | @@ -108,11 +161,28 @@ private: | ||
| 108 | virtual int do_cycle(); | 161 | virtual int do_cycle(); |
| 109 | // internal methods | 162 | // internal methods |
| 110 | public: | 163 | public: |
| 111 | - virtual int on_rtp_packet(SrsRtpPacket* pkt); | 164 | + virtual int on_rtp_packet(SrsRtpPacket* pkt, int stream_id); |
| 112 | // interface ISrsThreadHandler | 165 | // interface ISrsThreadHandler |
| 113 | public: | 166 | public: |
| 114 | virtual int cycle(); | 167 | virtual int cycle(); |
| 115 | virtual void on_thread_stop(); | 168 | virtual void on_thread_stop(); |
| 169 | +private: | ||
| 170 | + virtual int on_rtp_video(SrsRtpPacket* pkt, int64_t dts, int64_t pts); | ||
| 171 | + virtual int on_rtp_audio(SrsRtpPacket* pkt, int64_t dts); | ||
| 172 | + virtual int kickoff_audio_cache(SrsRtpPacket* pkt, int64_t dts); | ||
| 173 | +private: | ||
| 174 | + virtual int write_sequence_header(); | ||
| 175 | + virtual int write_h264_sps_pps(u_int32_t dts, u_int32_t pts); | ||
| 176 | + virtual int write_h264_ipb_frame(char* frame, int frame_size, u_int32_t dts, u_int32_t pts); | ||
| 177 | + virtual int write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t dts); | ||
| 178 | + virtual int rtmp_write_packet(char type, u_int32_t timestamp, char* data, int size); | ||
| 179 | +private: | ||
| 180 | + // connect to rtmp output url. | ||
| 181 | + // @remark ignore when not connected, reconnect when disconnected. | ||
| 182 | + virtual int connect(); | ||
| 183 | + virtual int connect_app(std::string ep_server, std::string ep_port); | ||
| 184 | + // close the connected io and rtmp to ready to be re-connect. | ||
| 185 | + virtual void close(); | ||
| 116 | }; | 186 | }; |
| 117 | 187 | ||
| 118 | /** | 188 | /** |
| @@ -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 119 | 34 | +#define VERSION_REVISION 120 |
| 35 | 35 | ||
| 36 | // server info. | 36 | // server info. |
| 37 | #define RTMP_SIG_SRS_KEY "SRS" | 37 | #define RTMP_SIG_SRS_KEY "SRS" |
| @@ -279,58 +279,12 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample | @@ -279,58 +279,12 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample | ||
| 279 | srs_freep(aac_extra_data); | 279 | srs_freep(aac_extra_data); |
| 280 | aac_extra_data = new char[aac_extra_size]; | 280 | aac_extra_data = new char[aac_extra_size]; |
| 281 | memcpy(aac_extra_data, stream->data() + stream->pos(), aac_extra_size); | 281 | memcpy(aac_extra_data, stream->data() + stream->pos(), aac_extra_size); |
| 282 | - } | ||
| 283 | - | ||
| 284 | - // only need to decode the first 2bytes: | ||
| 285 | - // audioObjectType, aac_profile, 5bits. | ||
| 286 | - // samplingFrequencyIndex, aac_sample_rate, 4bits. | ||
| 287 | - // channelConfiguration, aac_channels, 4bits | ||
| 288 | - if (!stream->require(2)) { | ||
| 289 | - ret = ERROR_HLS_DECODE_ERROR; | ||
| 290 | - srs_error("audio codec decode aac sequence header failed. ret=%d", ret); | ||
| 291 | - return ret; | ||
| 292 | - } | ||
| 293 | - u_int8_t profile_ObjectType = stream->read_1bytes(); | ||
| 294 | - u_int8_t samplingFrequencyIndex = stream->read_1bytes(); | ||
| 295 | - | ||
| 296 | - aac_channels = (samplingFrequencyIndex >> 3) & 0x0f; | ||
| 297 | - samplingFrequencyIndex = ((profile_ObjectType << 1) & 0x0e) | ((samplingFrequencyIndex >> 7) & 0x01); | ||
| 298 | - profile_ObjectType = (profile_ObjectType >> 3) & 0x1f; | ||
| 299 | - | ||
| 300 | - // set the aac sample rate. | ||
| 301 | - aac_sample_rate = samplingFrequencyIndex; | ||
| 302 | 282 | ||
| 303 | - // the profile = object_id + 1 | ||
| 304 | - // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, | ||
| 305 | - // Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types | ||
| 306 | - aac_profile = profile_ObjectType + 1; | ||
| 307 | - | ||
| 308 | - // the valid aac profile: | ||
| 309 | - // MPEG-2 profile | ||
| 310 | - // Main profile (ID == 1) | ||
| 311 | - // Low Complexity profile (LC) (ID == 2) | ||
| 312 | - // Scalable Sampling Rate profile (SSR) (ID == 3) | ||
| 313 | - // (reserved) (ID == 4) | ||
| 314 | - // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, | ||
| 315 | - // Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types | ||
| 316 | - if (aac_profile > 4) { | ||
| 317 | - ret = ERROR_HLS_DECODE_ERROR; | ||
| 318 | - srs_error("audio codec decode aac sequence header failed, " | ||
| 319 | - "adts object=%d invalid. ret=%d", profile_ObjectType, ret); | ||
| 320 | - return ret; | 283 | + // demux the sequence header. |
| 284 | + if ((ret = audio_aac_sequence_header_demux(aac_extra_data, aac_extra_size)) != ERROR_SUCCESS) { | ||
| 285 | + return ret; | ||
| 286 | + } | ||
| 321 | } | 287 | } |
| 322 | - | ||
| 323 | - // TODO: FIXME: to support aac he/he-v2, see: ngx_rtmp_codec_parse_aac_header | ||
| 324 | - // @see: https://github.com/winlinvip/nginx-rtmp-module/commit/3a5f9eea78fc8d11e8be922aea9ac349b9dcbfc2 | ||
| 325 | - // | ||
| 326 | - // donot force to LC, @see: https://github.com/winlinvip/simple-rtmp-server/issues/81 | ||
| 327 | - // the source will print the sequence header info. | ||
| 328 | - //if (aac_profile > 3) { | ||
| 329 | - // Mark all extended profiles as LC | ||
| 330 | - // to make Android as happy as possible. | ||
| 331 | - // @see: ngx_rtmp_hls_parse_aac_header | ||
| 332 | - //aac_profile = 1; | ||
| 333 | - //} | ||
| 334 | } else if (aac_packet_type == SrsCodecAudioTypeRawData) { | 288 | } else if (aac_packet_type == SrsCodecAudioTypeRawData) { |
| 335 | // ensure the sequence header demuxed | 289 | // ensure the sequence header demuxed |
| 336 | if (aac_extra_size <= 0 || !aac_extra_data) { | 290 | if (aac_extra_size <= 0 || !aac_extra_data) { |
| @@ -403,6 +357,68 @@ int SrsAvcAacCodec::audio_mp3_demux(char* data, int size, SrsCodecSample* sample | @@ -403,6 +357,68 @@ int SrsAvcAacCodec::audio_mp3_demux(char* data, int size, SrsCodecSample* sample | ||
| 403 | return ret; | 357 | return ret; |
| 404 | } | 358 | } |
| 405 | 359 | ||
| 360 | +int SrsAvcAacCodec::audio_aac_sequence_header_demux(char* data, int size) | ||
| 361 | +{ | ||
| 362 | + int ret = ERROR_SUCCESS; | ||
| 363 | + | ||
| 364 | + if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) { | ||
| 365 | + return ret; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + // only need to decode the first 2bytes: | ||
| 369 | + // audioObjectType, aac_profile, 5bits. | ||
| 370 | + // samplingFrequencyIndex, aac_sample_rate, 4bits. | ||
| 371 | + // channelConfiguration, aac_channels, 4bits | ||
| 372 | + if (!stream->require(2)) { | ||
| 373 | + ret = ERROR_HLS_DECODE_ERROR; | ||
| 374 | + srs_error("audio codec decode aac sequence header failed. ret=%d", ret); | ||
| 375 | + return ret; | ||
| 376 | + } | ||
| 377 | + u_int8_t profile_ObjectType = stream->read_1bytes(); | ||
| 378 | + u_int8_t samplingFrequencyIndex = stream->read_1bytes(); | ||
| 379 | + | ||
| 380 | + aac_channels = (samplingFrequencyIndex >> 3) & 0x0f; | ||
| 381 | + samplingFrequencyIndex = ((profile_ObjectType << 1) & 0x0e) | ((samplingFrequencyIndex >> 7) & 0x01); | ||
| 382 | + profile_ObjectType = (profile_ObjectType >> 3) & 0x1f; | ||
| 383 | + | ||
| 384 | + // set the aac sample rate. | ||
| 385 | + aac_sample_rate = samplingFrequencyIndex; | ||
| 386 | + | ||
| 387 | + // the profile = object_id + 1 | ||
| 388 | + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, | ||
| 389 | + // Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types | ||
| 390 | + aac_profile = profile_ObjectType + 1; | ||
| 391 | + | ||
| 392 | + // the valid aac profile: | ||
| 393 | + // MPEG-2 profile | ||
| 394 | + // Main profile (ID == 1) | ||
| 395 | + // Low Complexity profile (LC) (ID == 2) | ||
| 396 | + // Scalable Sampling Rate profile (SSR) (ID == 3) | ||
| 397 | + // (reserved) (ID == 4) | ||
| 398 | + // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 78, | ||
| 399 | + // Table 1. A.9 ¨C MPEG-2 Audio profiles and MPEG-4 Audio object types | ||
| 400 | + if (aac_profile > 4) { | ||
| 401 | + ret = ERROR_HLS_DECODE_ERROR; | ||
| 402 | + srs_error("audio codec decode aac sequence header failed, " | ||
| 403 | + "adts object=%d invalid. ret=%d", profile_ObjectType, ret); | ||
| 404 | + return ret; | ||
| 405 | + } | ||
| 406 | + | ||
| 407 | + // TODO: FIXME: to support aac he/he-v2, see: ngx_rtmp_codec_parse_aac_header | ||
| 408 | + // @see: https://github.com/winlinvip/nginx-rtmp-module/commit/3a5f9eea78fc8d11e8be922aea9ac349b9dcbfc2 | ||
| 409 | + // | ||
| 410 | + // donot force to LC, @see: https://github.com/winlinvip/simple-rtmp-server/issues/81 | ||
| 411 | + // the source will print the sequence header info. | ||
| 412 | + //if (aac_profile > 3) { | ||
| 413 | + // Mark all extended profiles as LC | ||
| 414 | + // to make Android as happy as possible. | ||
| 415 | + // @see: ngx_rtmp_hls_parse_aac_header | ||
| 416 | + //aac_profile = 1; | ||
| 417 | + //} | ||
| 418 | + | ||
| 419 | + return ret; | ||
| 420 | +} | ||
| 421 | + | ||
| 406 | int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample) | 422 | int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample) |
| 407 | { | 423 | { |
| 408 | int ret = ERROR_SUCCESS; | 424 | int ret = ERROR_SUCCESS; |
| @@ -475,6 +475,11 @@ public: | @@ -475,6 +475,11 @@ public: | ||
| 475 | * demux the h.264 NALUs to sampe units. | 475 | * demux the h.264 NALUs to sampe units. |
| 476 | */ | 476 | */ |
| 477 | virtual int video_avc_demux(char* data, int size, SrsCodecSample* sample); | 477 | virtual int video_avc_demux(char* data, int size, SrsCodecSample* sample); |
| 478 | +public: | ||
| 479 | + /** | ||
| 480 | + * directly demux the sequence header, without RTMP packet header. | ||
| 481 | + */ | ||
| 482 | + virtual int audio_aac_sequence_header_demux(char* data, int size); | ||
| 478 | private: | 483 | private: |
| 479 | /** | 484 | /** |
| 480 | * when avc packet type is SrsCodecVideoAVCTypeSequenceHeader, | 485 | * when avc packet type is SrsCodecVideoAVCTypeSequenceHeader, |
| @@ -147,6 +147,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | @@ -147,6 +147,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 147 | #define ERROR_RTP_HEADER_CORRUPT 2044 | 147 | #define ERROR_RTP_HEADER_CORRUPT 2044 |
| 148 | #define ERROR_RTP_TYPE96_CORRUPT 2045 | 148 | #define ERROR_RTP_TYPE96_CORRUPT 2045 |
| 149 | #define ERROR_RTP_TYPE97_CORRUPT 2046 | 149 | #define ERROR_RTP_TYPE97_CORRUPT 2046 |
| 150 | +#define ERROR_RTSP_AUDIO_CONFIG 2047 | ||
| 150 | // | 151 | // |
| 151 | // system control message, | 152 | // system control message, |
| 152 | // not an error, but special control logic. | 153 | // not an error, but special control logic. |
| @@ -621,3 +621,41 @@ char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_s | @@ -621,3 +621,41 @@ char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_s | ||
| 621 | return ret; | 621 | return ret; |
| 622 | } | 622 | } |
| 623 | 623 | ||
| 624 | +#define SPACE_CHARS " \t\r\n" | ||
| 625 | + | ||
| 626 | +int av_toupper(int c) | ||
| 627 | +{ | ||
| 628 | + if (c >= 'a' && c <= 'z') { | ||
| 629 | + c ^= 0x20; | ||
| 630 | + } | ||
| 631 | + return c; | ||
| 632 | +} | ||
| 633 | + | ||
| 634 | +int ff_hex_to_data(u_int8_t* data, const char* p) | ||
| 635 | +{ | ||
| 636 | + int c, len, v; | ||
| 637 | + | ||
| 638 | + len = 0; | ||
| 639 | + v = 1; | ||
| 640 | + for (;;) { | ||
| 641 | + p += strspn(p, SPACE_CHARS); | ||
| 642 | + if (*p == '\0') | ||
| 643 | + break; | ||
| 644 | + c = av_toupper((unsigned char) *p++); | ||
| 645 | + if (c >= '0' && c <= '9') | ||
| 646 | + c = c - '0'; | ||
| 647 | + else if (c >= 'A' && c <= 'F') | ||
| 648 | + c = c - 'A' + 10; | ||
| 649 | + else | ||
| 650 | + break; | ||
| 651 | + v = (v << 4) | c; | ||
| 652 | + if (v & 0x100) { | ||
| 653 | + if (data) | ||
| 654 | + data[len] = v; | ||
| 655 | + len++; | ||
| 656 | + v = 1; | ||
| 657 | + } | ||
| 658 | + } | ||
| 659 | + return len; | ||
| 660 | +} | ||
| 661 | + |
| @@ -115,5 +115,12 @@ extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, i | @@ -115,5 +115,12 @@ extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, i | ||
| 115 | */ | 115 | */ |
| 116 | #define SRS_AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) | 116 | #define SRS_AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) |
| 117 | 117 | ||
| 118 | +/** | ||
| 119 | +* convert hex string to data. | ||
| 120 | +* for example, p=config='139056E5A0' | ||
| 121 | +* output hex to data={0x13, 0x90, 0x56, 0xe5, 0xa0} | ||
| 122 | +*/ | ||
| 123 | +extern int ff_hex_to_data(u_int8_t* data, const char* p); | ||
| 124 | + | ||
| 118 | #endif | 125 | #endif |
| 119 | 126 |
| @@ -200,6 +200,8 @@ int SrsRtpPacket::decode(SrsStream* stream) | @@ -200,6 +200,8 @@ int SrsRtpPacket::decode(SrsStream* stream) | ||
| 200 | timestamp = stream->read_4bytes(); | 200 | timestamp = stream->read_4bytes(); |
| 201 | ssrc = stream->read_4bytes(); | 201 | ssrc = stream->read_4bytes(); |
| 202 | 202 | ||
| 203 | + // TODO: FIXME: check sequence number. | ||
| 204 | + | ||
| 203 | // video codec. | 205 | // video codec. |
| 204 | if (payload_type == 96) { | 206 | if (payload_type == 96) { |
| 205 | return decode_96(stream); | 207 | return decode_96(stream); |
| @@ -232,7 +234,6 @@ int SrsRtpPacket::decode_97(SrsStream* stream) | @@ -232,7 +234,6 @@ int SrsRtpPacket::decode_97(SrsStream* stream) | ||
| 232 | } | 234 | } |
| 233 | 235 | ||
| 234 | int nb_samples = au_size / 2; | 236 | int nb_samples = au_size / 2; |
| 235 | - int guess_sample_size = (stream->size() - stream->pos() - au_size) / nb_samples; | ||
| 236 | int required_size = 0; | 237 | int required_size = 0; |
| 237 | 238 | ||
| 238 | // append left bytes to payload. | 239 | // append left bytes to payload. |
| @@ -247,11 +248,9 @@ int SrsRtpPacket::decode_97(SrsStream* stream) | @@ -247,11 +248,9 @@ int SrsRtpPacket::decode_97(SrsStream* stream) | ||
| 247 | lasv = stream->read_1bytes(); | 248 | lasv = stream->read_1bytes(); |
| 248 | 249 | ||
| 249 | u_int16_t sample_size = ((hasv << 5) & 0xE0) | ((lasv >> 3) & 0x1f); | 250 | u_int16_t sample_size = ((hasv << 5) & 0xE0) | ((lasv >> 3) & 0x1f); |
| 250 | - if (sample_size != guess_sample_size) { | ||
| 251 | - // guess the size lost 0x100. | ||
| 252 | - if (guess_sample_size == (sample_size | 0x100)) { | ||
| 253 | - sample_size = guess_sample_size; | ||
| 254 | - } | 251 | + // TODO: FIXME: finger out how to parse the size of sample. |
| 252 | + if (sample_size < 0x100 && stream->require(required_size + sample_size + 0x100)) { | ||
| 253 | + sample_size = sample_size | 0x100; | ||
| 255 | } | 254 | } |
| 256 | 255 | ||
| 257 | char* sample = p + required_size; | 256 | char* sample = p + required_size; |
| @@ -541,7 +540,17 @@ int SrsRtspSdp::parse_fmtp_attribute(string attr) | @@ -541,7 +540,17 @@ int SrsRtspSdp::parse_fmtp_attribute(string attr) | ||
| 541 | } else if (item_key == "indexdeltalength") { | 540 | } else if (item_key == "indexdeltalength") { |
| 542 | audio_index_delta_length = item_value; | 541 | audio_index_delta_length = item_value; |
| 543 | } else if (item_key == "config") { | 542 | } else if (item_key == "config") { |
| 544 | - audio_sh = base64_decode(item_value); | 543 | + if (item_value.length() <= 0) { |
| 544 | + ret = ERROR_RTSP_AUDIO_CONFIG; | ||
| 545 | + srs_error("rtsp: audio config failed. ret=%d", ret); | ||
| 546 | + return ret; | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + char* tmp_sh = new char[item_value.length()]; | ||
| 550 | + SrsAutoFree(char, tmp_sh); | ||
| 551 | + int nb_tmp_sh = ff_hex_to_data((u_int8_t*)tmp_sh, item_value.c_str()); | ||
| 552 | + srs_assert(nb_tmp_sh > 0); | ||
| 553 | + audio_sh.append(tmp_sh, nb_tmp_sh); | ||
| 545 | } | 554 | } |
| 546 | } | 555 | } |
| 547 | } | 556 | } |
-
请 注册 或 登录 后发表评论