From 0cc693a3b81fd7e097578a6552873ac48c5b4192 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 18 Feb 2015 11:49:42 +0800 Subject: [PATCH] for #133, decode the h264 NALU from rtp. --- trunk/src/app/srs_app_rtsp.cpp | 64 +++++++++- trunk/src/app/srs_app_rtsp.hpp | 8 +- trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/protocol/srs_rtsp_stack.cpp | 116 ++++++++++++++++++ trunk/src/protocol/srs_rtsp_stack.hpp | 164 ++++++++++++++++++++++++++ 5 files changed, 349 insertions(+), 4 deletions(-) diff --git a/trunk/src/app/srs_app_rtsp.cpp b/trunk/src/app/srs_app_rtsp.cpp index 3d7418e1d..37fbfbaf3 100644 --- a/trunk/src/app/srs_app_rtsp.cpp +++ b/trunk/src/app/srs_app_rtsp.cpp @@ -33,19 +33,24 @@ using namespace std; #include #include #include +#include +#include #ifdef SRS_AUTO_STREAM_CASTER -SrsRtpConn::SrsRtpConn(SrsRtspConn* r, int p) +SrsRtpConn::SrsRtpConn(SrsRtspConn* r, int p, int sid) { rtsp = r; _port = p; + stream_id = sid; listener = new SrsUdpListener(this, p); + cache = new SrsRtpPacket(); } SrsRtpConn::~SrsRtpConn() { srs_freep(listener); + srs_freep(cache); } int SrsRtpConn::port() @@ -61,6 +66,53 @@ int SrsRtpConn::listen() int SrsRtpConn::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) { int ret = ERROR_SUCCESS; + + if (true) { + SrsStream stream; + + if ((ret = stream.initialize(buf, nb_buf)) != ERROR_SUCCESS) { + return ret; + } + + SrsRtpPacket pkt; + if ((ret = pkt.decode(&stream)) != ERROR_SUCCESS) { + srs_error("rtsp: decode rtp packet failed. ret=%d", ret); + return ret; + } + + if (pkt.chunked) { + if (!cache) { + cache = new SrsRtpPacket(); + } + cache->copy(&pkt); + cache->payload->append(pkt.payload->bytes(), pkt.payload->length()); + if (!cache->completed) { + srs_trace("rtsp: rtp chunked %dB, vt=%d/%u, sts=%u/%#x/%#x, paylod=%dB", + nb_buf, cache->version, cache->payload_type, cache->sequence_number, cache->timestamp, cache->ssrc, + cache->payload->length() + ); + return ret; + } + } else { + srs_freep(cache); + cache = new SrsRtpPacket(); + cache->reap(&pkt); + } + } + + srs_trace("rtsp: rtp %dB, vt=%d/%u, sts=%u/%#x/%#x, paylod=%dB, chunked=%d", + nb_buf, cache->version, cache->payload_type, cache->sequence_number, cache->timestamp, cache->ssrc, + cache->payload->length(), cache->chunked + ); + + // always free it. + SrsAutoFree(SrsRtpPacket, cache); + + if ((ret = rtsp->on_rtp_packet(cache)) != ERROR_SUCCESS) { + srs_error("rtsp: process rtp packet failed. ret=%d", ret); + return ret; + } + return ret; } @@ -162,10 +214,10 @@ int SrsRtspConn::do_cycle() SrsRtpConn* rtp = NULL; if (req->stream_id == video_id) { srs_freep(video_rtp); - rtp = video_rtp = new SrsRtpConn(this, lpm); + rtp = video_rtp = new SrsRtpConn(this, lpm, video_id); } else { srs_freep(audio_rtp); - rtp = audio_rtp = new SrsRtpConn(this, lpm); + rtp = audio_rtp = new SrsRtpConn(this, lpm, audio_id); } if ((ret = rtp->listen()) != ERROR_SUCCESS) { srs_error("rtsp: rtp listen at port=%d failed. ret=%d", lpm, ret); @@ -210,6 +262,12 @@ int SrsRtspConn::do_cycle() return ret; } +int SrsRtspConn::on_rtp_packet(SrsRtpPacket* pkt) +{ + int ret = ERROR_SUCCESS; + return ret; +} + int SrsRtspConn::cycle() { // serve the rtsp client. diff --git a/trunk/src/app/srs_app_rtsp.hpp b/trunk/src/app/srs_app_rtsp.hpp index febb6012b..a57e89952 100644 --- a/trunk/src/app/srs_app_rtsp.hpp +++ b/trunk/src/app/srs_app_rtsp.hpp @@ -45,6 +45,7 @@ class SrsRtspConn; class SrsRtspStack; class SrsRtspCaster; class SrsConfDirective; +class SrsRtpPacket; /** * a rtp connection which transport a stream. @@ -54,9 +55,11 @@ class SrsRtpConn: public ISrsUdpHandler private: SrsUdpListener* listener; SrsRtspConn* rtsp; + SrsRtpPacket* cache; + int stream_id; int _port; public: - SrsRtpConn(SrsRtspConn* r, int p); + SrsRtpConn(SrsRtspConn* r, int p, int sid); virtual ~SrsRtpConn(); public: virtual int port(); @@ -103,6 +106,9 @@ public: virtual int serve(); private: virtual int do_cycle(); +// internal methods +public: + virtual int on_rtp_packet(SrsRtpPacket* pkt); // interface ISrsThreadHandler public: virtual int cycle(); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index ce2a42bbf..c714eac8c 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -144,6 +144,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_RTMP_MIC_CACHE_OVERFLOW 2041 #define ERROR_RTSP_TOKEN_NOT_NORMAL 2042 #define ERROR_RTSP_REQUEST_HEADER_EOF 2043 +#define ERROR_RTP_HEADER_CORRUPT 2044 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index 2ab64049e..0f0c2b7fc 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -34,6 +34,7 @@ using namespace std; #include #include #include +#include #ifdef SRS_AUTO_STREAM_CASTER @@ -118,6 +119,121 @@ std::string srs_generate_rtsp_method_str(SrsRtspMethod method) } } +SrsRtpPacket::SrsRtpPacket() +{ + version = 2; + padding = 0; + extension = 0; + csrc_count = 0; + marker = 1; + + payload_type = 0; + sequence_number = 0; + timestamp = 0; + ssrc = 0; + + payload = new SrsSimpleBuffer(); + chunked = false; + completed = false; +} + +SrsRtpPacket::~SrsRtpPacket() +{ + srs_freep(payload); +} + +void SrsRtpPacket::copy(SrsRtpPacket* src) +{ + version = src->version; + padding = src->padding; + extension = src->extension; + csrc_count = src->csrc_count; + marker = src->marker; + payload_type = src->payload_type; + sequence_number = src->sequence_number; + timestamp = src->timestamp; + ssrc = src->ssrc; + + chunked = src->chunked; + completed = src->completed; +} + +void SrsRtpPacket::reap(SrsRtpPacket* src) +{ + copy(src); + + payload = src->payload; + src->payload = NULL; +} + +int SrsRtpPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // 12bytes header, atleast 2bytes content. + if (!stream->require(14)) { + ret = ERROR_RTP_HEADER_CORRUPT; + srs_error("rtsp: rtp header corrupt. ret=%d", ret); + return ret; + } + + int8_t vv = stream->read_1bytes(); + version = (vv >> 6) & 0x03; + padding = (vv >> 5) & 0x01; + extension = (vv >> 4) & 0x01; + csrc_count = vv & 0x0f; + + int8_t mv = stream->read_1bytes(); + marker = (mv >> 7) & 0x01; + payload_type = mv & 0x7f; + + sequence_number = stream->read_2bytes(); + timestamp = stream->read_4bytes(); + ssrc = stream->read_4bytes(); + + // frame type + // 0... .... reserverd + // .11. .... NALU[0]&0x60 + // ...1 11.. FU indicator + // .... ..00 reserverd + int8_t ftv = stream->read_1bytes(); + int8_t nalu_0x60 = ftv & 0x60; + int8_t fu_indicator = ftv & 0x1c; + + // nri, whatever + // 10.. .... first chunk. + // 00.. .... continous chunk. + // 01.. .... last chunk. + // ...1 1111 NALU[0]&0x1f + int8_t nriv = stream->read_1bytes(); + bool first_chunk = (nriv & 0xC0) == 0x80; + bool last_chunk = (nriv & 0xC0) == 0x40; + bool contious_chunk = (nriv & 0xC0) == 0x00; + int8_t nalu_0x1f = nriv & 0x1f; + + // chunked, generate the first byte NALU. + if (fu_indicator == 0x1c && (first_chunk || last_chunk || contious_chunk)) { + chunked = true; + completed = last_chunk; + + // generate and append the first byte NALU. + if (first_chunk) { + int8_t nalu_byte0 = nalu_0x60 | nalu_0x1f; + payload->append((char*)&nalu_byte0, 1); + } + + payload->append(stream->data() + stream->pos(), stream->size() - stream->pos()); + return ret; + } + + // no chunked, append to payload. + stream->skip(-2); + payload->append(stream->data() + stream->pos(), stream->size() - stream->pos()); + completed = true; + + return ret; +} + SrsRtspSdp::SrsRtspSdp() { state = SrsRtspSdpStateOthers; diff --git a/trunk/src/protocol/srs_rtsp_stack.hpp b/trunk/src/protocol/srs_rtsp_stack.hpp index 5b258a65c..1cd1adcc3 100644 --- a/trunk/src/protocol/srs_rtsp_stack.hpp +++ b/trunk/src/protocol/srs_rtsp_stack.hpp @@ -37,6 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef SRS_AUTO_STREAM_CASTER +class SrsStream; class SrsSimpleBuffer; class ISrsProtocolReaderWriter; @@ -146,6 +147,169 @@ enum SrsRtspTokenState SrsRtspTokenStateEOF = 102, }; +/** +* the rtp packet. +* 5. RTP Data Transfer Protocol, @see rtp-rfc3550-2003.pdf, page 12 +*/ +class SrsRtpPacket +{ +public: + /** + * version (V): 2 bits + * This eld identi es the version of RTP. The version de ned by this speci cation is two (2). + * (The value 1 is used by the rst draft version of RTP and the value 0 is used by the protocol + * initially implemented in the \vat" audio tool.) + */ + int8_t version; //2bits + /** + * padding (P): 1 bit + * If the padding bit is set, the packet contains one or more additional padding octets at the + * end which are not part of the payload. The last octet of the padding contains a count of + * how many padding octets should be ignored, including itself. Padding may be needed by + * some encryption algorithms with xed block sizes or for carrying several RTP packets in a + * lower-layer protocol data unit. + */ + int8_t padding; //1bit + /** + * extension (X): 1 bit + * If the extension bit is set, the xed header must be followed by exactly one header extension, + * with a format de ned in Section 5.3.1. + */ + int8_t extension; //1bit + /** + * CSRC count (CC): 4 bits + * The CSRC count contains the number of CSRC identi ers that follow the xed header. + */ + int8_t csrc_count; //4bits + /** + * marker (M): 1 bit + * The interpretation of the marker is de ned by a pro le. It is intended to allow signi cant + * events such as frame boundaries to be marked in the packet stream. A pro le may de ne + * additional marker bits or specify that there is no marker bit by changing the number of bits + * in the payload type eld (see Section 5.3). + */ + int8_t marker; //1bit + /** + * payload type (PT): 7 bits + * This eld identi es the format of the RTP payload and determines its interpretation by the + * application. A pro le may specify a default static mapping of payload type codes to payload + * formats. Additional payload type codes may be de ned dynamically through non-RTP means + * (see Section 3). A set of default mappings for audio and video is speci ed in the companion + * RFC 3551 [1]. An RTP source may change the payload type during a session, but this eld + * should not be used for multiplexing separate media streams (see Section 5.2). + * A receiver must ignore packets with payload types that it does not understand. + */ + int8_t payload_type; //7bits + /** + * sequence number: 16 bits + * The sequence number increments by one for each RTP data packet sent, and may be used + * by the receiver to detect packet loss and to restore packet sequence. The initial value of the + * sequence number should be random (unpredictable) to make known-plaintext attacks on + * encryption more dicult, even if the source itself does not encrypt according to the method + * in Section 9.1, because the packets may flow through a translator that does. Techniques for + * choosing unpredictable numbers are discussed in [17]. + */ + u_int16_t sequence_number; //16bits + /** + * timestamp: 32 bits + * The timestamp reflects the sampling instant of the rst octet in the RTP data packet. The + * sampling instant must be derived from a clock that increments monotonically and linearly + * in time to allow synchronization and jitter calculations (see Section 6.4.1). The resolution + * of the clock must be sucient for the desired synchronization accuracy and for measuring + * packet arrival jitter (one tick per video frame is typically not sucient). The clock frequency + * is dependent on the format of data carried as payload and is speci ed statically in the pro le + * or payload format speci cation that de nes the format, or may be speci ed dynamically for + * payload formats de ned through non-RTP means. If RTP packets are generated periodically, + * the nominal sampling instant as determined from the sampling clock is to be used, not a + * reading of the system clock. As an example, for xed-rate audio the timestamp clock would + * likely increment by one for each sampling period. If an audio application reads blocks covering + * 160 sampling periods from the input device, the timestamp would be increased by 160 for + * each such block, regardless of whether the block is transmitted in a packet or dropped as + * silent. + * + * The initial value of the timestamp should be random, as for the sequence number. Several + * consecutive RTP packets will have equal timestamps if they are (logically) generated at once, + * e.g., belong to the same video frame. Consecutive RTP packets may contain timestamps that + * are not monotonic if the data is not transmitted in the order it was sampled, as in the case + * of MPEG interpolated video frames. (The sequence numbers of the packets as transmitted + * will still be monotonic.) + * + * RTP timestamps from di erent media streams may advance at di erent rates and usually + * have independent, random o sets. Therefore, although these timestamps are sucient to + * reconstruct the timing of a single stream, directly comparing RTP timestamps from di erent + * media is not e ective for synchronization. Instead, for each medium the RTP timestamp + * is related to the sampling instant by pairing it with a timestamp from a reference clock + * (wallclock) that represents the time when the data corresponding to the RTP timestamp was + * sampled. The reference clock is shared by all media to be synchronized. The timestamp + * pairs are not transmitted in every data packet, but at a lower rate in RTCP SR packets as + * described in Section 6.4. + * + * The sampling instant is chosen as the point of reference for the RTP timestamp because it is + * known to the transmitting endpoint and has a common de nition for all media, independent + * of encoding delays or other processing. The purpose is to allow synchronized presentation of + * all media sampled at the same time. + * + * Applications transmitting stored data rather than data sampled in real time typically use a + * virtual presentation timeline derived from wallclock time to determine when the next frame + * or other unit of each medium in the stored data should be presented. In this case, the RTP + * timestamp would reflect the presentation time for each unit. That is, the RTP timestamp for + * each unit would be related to the wallclock time at which the unit becomes current on the + * virtual presentation timeline. Actual presentation occurs some time later as determined by + * the receiver. + * + * An example describing live audio narration of prerecorded video illustrates the signi cance + * of choosing the sampling instant as the reference point. In this scenario, the video would + * be presented locally for the narrator to view and would be simultaneously transmitted using + * RTP. The \sampling instant" of a video frame transmitted in RTP would be established by + * referencing its timestamp to the wallclock time when that video frame was presented to the + * narrator. The sampling instant for the audio RTP packets containing the narrator's speech + * would be established by referencing the same wallclock time when the audio was sampled. + * The audio and video may even be transmitted by di erent hosts if the reference clocks on + * the two hosts are synchronized by some means such as NTP. A receiver can then synchronize + * presentation of the audio and video packets by relating their RTP timestamps using the + * timestamp pairs in RTCP SR packets. + */ + u_int32_t timestamp; //32bits + /** + * SSRC: 32 bits + * The SSRC eld identi es the synchronization source. This identi er should be chosen + * randomly, with the intent that no two synchronization sources within the same RTP session + * will have the same SSRC identi er. An example algorithm for generating a random identi er + * is presented in Appendix A.6. Although the probability of multiple sources choosing the same + * identi er is low, all RTP implementations must be prepared to detect and resolve collisions. + * Section 8 describes the probability of collision along with a mechanism for resolving collisions + * and detecting RTP-level forwarding loops based on the uniqueness of the SSRC identi er. If + * a source changes its source transport address, it must also choose a new SSRC identi er to + * avoid being interpreted as a looped source (see Section 8.2). + */ + u_int32_t ssrc; //32bits + + // the payload. + SrsSimpleBuffer* payload; + // whether transport in chunked payload. + bool chunked; + // whether message is completed. + // normal message always completed. + // while chunked completed when the last chunk arriaved. + bool completed; +public: + SrsRtpPacket(); + virtual ~SrsRtpPacket(); +public: + /** + * copy the header from src. + */ + virtual void copy(SrsRtpPacket* src); + /** + * reap the src to this packet, reap the payload. + */ + virtual void reap(SrsRtpPacket* src); + /** + * decode rtp packet from stream. + */ + virtual int decode(SrsStream* stream); +}; + /** * the sdp in announce, @see rtsp-rfc2326-1998.pdf, page 159 * Appendix C: Use of SDP for RTSP Session Descriptions