美文网首页
RTP系列:RTP 代码分析

RTP系列:RTP 代码分析

作者: 捧着漏勺喝汤 | 来源:发表于2020-02-29 14:29 被阅读0次

    1、封包
    代码位置:FFmpeg的文件rtpenc.c(4.0.5版本)
    主要是把RTP头信息放到AVStream里面,然后再进行处理。过程不是很明显,可以看下面的解包代码

    static int rtp_write_header(AVFormatContext *s1)
    {
        RTPMuxContext *s = s1->priv_data;
        int n, ret = AVERROR(EINVAL);
        AVStream *st;
    
        if (s1->nb_streams != 1) {
            av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
            return AVERROR(EINVAL);
        }
        st = s1->streams[0];
        if (!is_supported(st->codecpar->codec_id)) {
            av_log(s1, AV_LOG_ERROR, "Unsupported codec %s\n", avcodec_get_name(st->codecpar->codec_id));
    
            return -1;
        }
    
        if (s->payload_type < 0) {
            /* Re-validate non-dynamic payload types */
            if (st->id < RTP_PT_PRIVATE)
                st->id = ff_rtp_get_payload_type(s1, st->codecpar, -1);
    
            s->payload_type = st->id;
        } else {
            /* private option takes priority */
            st->id = s->payload_type;
        }
    
        s->base_timestamp = av_get_random_seed();
        s->timestamp = s->base_timestamp;
        s->cur_timestamp = 0;
        if (!s->ssrc)
            s->ssrc = av_get_random_seed();
        s->first_packet = 1;
        s->first_rtcp_ntp_time = ff_ntp_time();
        if (s1->start_time_realtime != 0  &&  s1->start_time_realtime != AV_NOPTS_VALUE)
            /* Round the NTP time to whole milliseconds. */
            s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 +
                                     NTP_OFFSET_US;
        // Pick a random sequence start number, but in the lower end of the
        // available range, so that any wraparound doesn't happen immediately.
        // (Immediate wraparound would be an issue for SRTP.)
        if (s->seq < 0) {
            if (s1->flags & AVFMT_FLAG_BITEXACT) {
                s->seq = 0;
            } else
                s->seq = av_get_random_seed() & 0x0fff;
        } else
            s->seq &= 0xffff; // Use the given parameter, wrapped to the right interval
    
        if (s1->packet_size) {
            if (s1->pb->max_packet_size)
                s1->packet_size = FFMIN(s1->packet_size,
                                        s1->pb->max_packet_size);
        } else
            s1->packet_size = s1->pb->max_packet_size;
        if (s1->packet_size <= 12) {
            av_log(s1, AV_LOG_ERROR, "Max packet size %u too low\n", s1->packet_size);
            return AVERROR(EIO);
        }
        s->buf = av_malloc(s1->packet_size);
        if (!s->buf) {
            return AVERROR(ENOMEM);
        }
        s->max_payload_size = s1->packet_size - 12;
    
        if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            avpriv_set_pts_info(st, 32, 1, st->codecpar->sample_rate);
        } else {
            avpriv_set_pts_info(st, 32, 1, 90000);
        }
        s->buf_ptr = s->buf;
        switch(st->codecpar->codec_id) {
        case AV_CODEC_ID_MP2:
        case AV_CODEC_ID_MP3:
            s->buf_ptr = s->buf + 4;
            avpriv_set_pts_info(st, 32, 1, 90000);
            break;
        case AV_CODEC_ID_MPEG1VIDEO:
        case AV_CODEC_ID_MPEG2VIDEO:
            break;
        case AV_CODEC_ID_MPEG2TS:
            n = s->max_payload_size / TS_PACKET_SIZE;
            if (n < 1)
                n = 1;
            s->max_payload_size = n * TS_PACKET_SIZE;
            break;
        case AV_CODEC_ID_DIRAC:
            if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
                av_log(s, AV_LOG_ERROR,
                       "Packetizing VC-2 is experimental and does not use all values "
                       "of the specification "
                       "(even though most receivers may handle it just fine). "
                       "Please set -strict experimental in order to enable it.\n");
                ret = AVERROR_EXPERIMENTAL;
                goto fail;
            }
            break;
        case AV_CODEC_ID_H261:
            if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
                av_log(s, AV_LOG_ERROR,
                       "Packetizing H.261 is experimental and produces incorrect "
                       "packetization for cases where GOBs don't fit into packets "
                       "(even though most receivers may handle it just fine). "
                       "Please set -f_strict experimental in order to enable it.\n");
                ret = AVERROR_EXPERIMENTAL;
                goto fail;
            }
            break;
        case AV_CODEC_ID_H264:
            /* check for H.264 MP4 syntax */
            if (st->codecpar->extradata_size > 4 && st->codecpar->extradata[0] == 1) {
                s->nal_length_size = (st->codecpar->extradata[4] & 0x03) + 1;
            }
            break;
        case AV_CODEC_ID_HEVC:
            /* Only check for the standardized hvcC version of extradata, keeping
             * things simple and similar to the avcC/H.264 case above, instead
             * of trying to handle the pre-standardization versions (as in
             * libavcodec/hevc.c). */
            if (st->codecpar->extradata_size > 21 && st->codecpar->extradata[0] == 1) {
                s->nal_length_size = (st->codecpar->extradata[21] & 0x03) + 1;
            }
            break;
        case AV_CODEC_ID_VP9:
            if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
                av_log(s, AV_LOG_ERROR,
                       "Packetizing VP9 is experimental and its specification is "
                       "still in draft state. "
                       "Please set -strict experimental in order to enable it.\n");
                ret = AVERROR_EXPERIMENTAL;
                goto fail;
            }
            break;
        case AV_CODEC_ID_VORBIS:
        case AV_CODEC_ID_THEORA:
            s->max_frames_per_packet = 15;
            break;
        case AV_CODEC_ID_ADPCM_G722:
            /* Due to a historical error, the clock rate for G722 in RTP is
             * 8000, even if the sample rate is 16000. See RFC 3551. */
            avpriv_set_pts_info(st, 32, 1, 8000);
            break;
        case AV_CODEC_ID_OPUS:
            if (st->codecpar->channels > 2) {
                av_log(s1, AV_LOG_ERROR, "Multistream opus not supported in RTP\n");
                goto fail;
            }
            /* The opus RTP RFC says that all opus streams should use 48000 Hz
             * as clock rate, since all opus sample rates can be expressed in
             * this clock rate, and sample rate changes on the fly are supported. */
            avpriv_set_pts_info(st, 32, 1, 48000);
            break;
        case AV_CODEC_ID_ILBC:
            if (st->codecpar->block_align != 38 && st->codecpar->block_align != 50) {
                av_log(s1, AV_LOG_ERROR, "Incorrect iLBC block size specified\n");
                goto fail;
            }
            s->max_frames_per_packet = s->max_payload_size / st->codecpar->block_align;
            break;
        case AV_CODEC_ID_AMR_NB:
        case AV_CODEC_ID_AMR_WB:
            s->max_frames_per_packet = 50;
            if (st->codecpar->codec_id == AV_CODEC_ID_AMR_NB)
                n = 31;
            else
                n = 61;
            /* max_header_toc_size + the largest AMR payload must fit */
            if (1 + s->max_frames_per_packet + n > s->max_payload_size) {
                av_log(s1, AV_LOG_ERROR, "RTP max payload size too small for AMR\n");
                goto fail;
            }
            if (st->codecpar->channels != 1) {
                av_log(s1, AV_LOG_ERROR, "Only mono is supported\n");
                goto fail;
            }
            break;
        case AV_CODEC_ID_AAC:
            s->max_frames_per_packet = 50;
            break;
        default:
            break;
        }
    
        return 0;
    
    fail:
        av_freep(&s->buf);
        return ret;
    }
    

    2、解包
    代码位置:FFmpeg的文件rtpdec.c(4.0.5版本)

    static int rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt,
                                         const uint8_t *buf, int len)
    {
        unsigned int ssrc;
        int payload_type, seq, flags = 0;
        int ext, csrc;
        AVStream *st;
        uint32_t timestamp;
        int rv = 0;
    
        csrc         = buf[0] & 0x0f; //CC:CSRC计数器
        ext          = buf[0] & 0x10;//X:扩展标志
        payload_type = buf[1] & 0x7f;//PT: 有效载荷类型
        if (buf[1] & 0x80)//M: 标记
            flags |= RTP_FLAG_MARKER;
        seq       = AV_RB16(buf + 2);//序列号
        timestamp = AV_RB32(buf + 4);//时间戳
        ssrc      = AV_RB32(buf + 8);//SSRC
        /* store the ssrc in the RTPDemuxContext */
        s->ssrc = ssrc;
    
        /* NOTE: we can handle only one payload type */
        if (s->payload_type != payload_type)
            return -1;
    
        st = s->st;
        // only do something with this if all the rtp checks pass...
        if (!rtp_valid_packet_in_sequence(&s->statistics, seq)) {
            av_log(s->ic, AV_LOG_ERROR,
                   "RTP: PT=%02x: bad cseq %04x expected=%04x\n",
                   payload_type, seq, ((s->seq + 1) & 0xffff));
            return -1;
        }
        //P:填充标志
        if (buf[0] & 0x20) {
            int padding = buf[len - 1];
            if (len >= 12 + padding)
                len -= padding;
        }
    
        s->seq = seq;
        len   -= 12;
        buf   += 12;
    
        len   -= 4 * csrc;
        buf   += 4 * csrc;
        if (len < 0)
            return AVERROR_INVALIDDATA;
    
        /* RFC 3550 Section 5.3.1 RTP Header Extension handling */
        //扩展报头处理
        if (ext) {
            if (len < 4)
                return -1;
            /* calculate the header extension length (stored as number
             * of 32-bit words) */
            ext = (AV_RB16(buf + 2) + 1) << 2;
    
            if (len < ext)
                return -1;
            // skip past RTP header extension
            len -= ext;
            buf += ext;
        }
    
        if (s->handler && s->handler->parse_packet) {
            rv = s->handler->parse_packet(s->ic, s->dynamic_protocol_context,
                                          s->st, pkt, &timestamp, buf, len, seq,
                                          flags);
        } else if (st) {
            if ((rv = av_new_packet(pkt, len)) < 0)
                return rv;
            memcpy(pkt->data, buf, len);
            pkt->stream_index = st->index;
        } else {
            return AVERROR(EINVAL);
        }
    
        // now perform timestamp things....
        finalize_packet(s, pkt, timestamp);
    
        return rv;
    }
    

    相关文章

      网友评论

          本文标题:RTP系列:RTP 代码分析

          本文链接:https://www.haomeiwen.com/subject/ispphhtx.html