ffmpeg_rtp

来源:互联网 发布:php入门教程电子书 编辑:程序博客网 时间:2024/06/10 23:26

1. struct define

typedef struct AVOutputFormat {
    const char *name;
    /**
     * Descriptive name for the format, meant to be more human-readable
     * than \p name. You \e should use the NULL_IF_CONFIG_SMALL() macro
     * to define it.
     */
    const char *long_name;
    const char *mime_type;
    const char *extensions; /**< comma-separated filename extensions */
    /** size of private data so that it can be allocated in the wrapper */
    int priv_data_size;
    /* output support */
    enum CodecID audio_codec; /**< default audio codec */
    enum CodecID video_codec; /**< default video codec */
    int (*write_header)(struct AVFormatContext *);
    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
    int (*write_trailer)(struct AVFormatContext *);
    /** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */
    int flags;
    /** Currently only used to set pixel format if not YUV420P. */
    int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);
    int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
                             AVPacket *in, int flush);

    /**
     * List of supported codec_id-codec_tag pairs, ordered by "better
     * choice first". The arrays are all terminated by CODEC_ID_NONE.
     */
    const struct AVCodecTag * const *codec_tag;

    enum CodecID subtitle_codec; /**< default subtitle codec */

    const AVMetadataConv *metadata_conv;

    /* private fields */
    struct AVOutputFormat *next;
} AVOutputFormat;


2. callback set etc

AVOutputFormat rtp_muxer = {

    "rtp",
    NULL_IF_CONFIG_SMALL("RTP output format"),
    NULL,
    NULL,
    sizeof(RTPMuxContext) * RTP_MAX_STREAMS,
    CODEC_ID_PCM_MULAW,
    CODEC_ID_NONE,
    rtp_write_header,
    rtp_write_packet,
    rtp_write_trailer,
};


3. relative functions

3.1 rtp_write_header

static int rtp_write_header(AVFormatContext *s1)
{
    RTPMuxContext *s = s1->priv_data;
    int payload_type, max_packet_size, n;
    AVStream *st;
    int i = 0;

    if (s1->nb_streams > RTP_MAX_STREAMS) return -1;

start:
    st = s1->streams[i];
    s->stream_index = i;
    s->ntp_time_pts0 = 0;

    payload_type = ff_rtp_get_payload_type(st->codec);
    if (payload_type < 0)
        payload_type = RTP_PT_PRIVATE+st->index; /* private payload type */
    s->payload_type = payload_type;

    av_log(s1, AV_LOG_DEBUG, "latm: %d \n", s1->audio_latm );
// following 2 FIXMEs could be set based on the current time, there is normally no info leak, as RTP will likely be transmitted immediately
    s->base_timestamp = 0; /* FIXME: was random(), what should this be? */
    s->timestamp = s->base_timestamp;
    s->cur_timestamp = 0;
    s->ssrc = 0; /* FIXME: was random(), what should this be? */
    s->first_packet = 1;
    s->first_rtcp_ntp_time = AV_NOPTS_VALUE;

    max_packet_size = url_fget_max_packet_size(s1->pb);
    if (max_packet_size <= 12)
        return AVERROR(EIO);
    s->buf = av_malloc(max_packet_size);
    if (s->buf == NULL) {
        return AVERROR(ENOMEM);
    }
    s->max_payload_size = max_packet_size - 12;

    s->max_frames_per_packet = 0;
    if (s1->max_delay) {
        if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
            if (st->codec->frame_size == 0) {
                av_log(s1, AV_LOG_ERROR, "Cannot respect max delay: frame size = 0\n");
            } else {
                s->max_frames_per_packet = av_rescale_rnd(s1->max_delay, st->codec->sample_rate, AV_TIME_BASE * st->codec->frame_size, AV_ROUND_DOWN);
            }
        }
        if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
            /* FIXME: We should round down here... */
            s->max_frames_per_packet = av_rescale_q(s1->max_delay, (AVRational){1, 1000000}, st->codec->time_base);
        }
    }

    av_set_pts_info(st, 32, 1, 90000);
    switch(st->codec->codec_id) {
    case CODEC_ID_MP2:
    case CODEC_ID_MP3:
        s->buf_ptr = s->buf + 4;
        break;
    case CODEC_ID_MPEG1VIDEO:
    case CODEC_ID_MPEG2VIDEO:
        break;
    case CODEC_ID_MPEG2TS:
        n = s->max_payload_size / TS_PACKET_SIZE;
        if (n < 1)
            n = 1;
        s->max_payload_size = n * TS_PACKET_SIZE;
        s->buf_ptr = s->buf;
        break;
    case CODEC_ID_AAC:
    case CODEC_ID_AMR_NB:
    case CODEC_ID_AMR_WB:
        s->num_frames = 0;
    default:
        if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
            av_set_pts_info(st, 32, 1, st->codec->sample_rate);
        }
        s->buf_ptr = s->buf;
        break;
    }

    /* to avoid changing a lot of indents, use 'goto' instead of 'for' */
    i ++;
    if (i < s1->nb_streams) {
        s ++;
        goto start;
    }

    return 0;
}


3.2 rtp_write_packet

/* write an RTP packet. 'buf1' must contain a single specific frame. */
static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
{
    RTPMuxContext *s = (RTPMuxContext *)s1->priv_data + pkt->stream_index;
    AVStream *st = s1->streams[pkt->stream_index];
    int rtcp_bytes;
    int size= pkt->size;
    uint8_t *buf1= pkt->data;

    dprintf(s1, "%d: write len=%d\n", pkt->stream_index, size);

    rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) /
        RTCP_TX_RATIO_DEN;

    s->cur_timestamp = s->base_timestamp + pkt->pts;

    if (s->first_packet || ((rtcp_bytes >= RTCP_SR_SIZE) &&
                           /* CMMB requests that rtcp SR interval < 2s */
                           (ntp_time() - s->last_rtcp_ntp_time > 1500000))) {
        rtcp_send_sr(s1, s, ntp_time());
        s->last_octet_count = s->octet_count;
        s->first_packet = 0;
    }

    switch(st->codec->codec_id) {
    case CODEC_ID_PCM_MULAW:
    case CODEC_ID_PCM_ALAW:
    case CODEC_ID_PCM_U8:
    case CODEC_ID_PCM_S8:
        rtp_send_samples(s1, s, buf1, size, 1 * st->codec->channels);
        break;
    case CODEC_ID_PCM_U16BE:
    case CODEC_ID_PCM_U16LE:
    case CODEC_ID_PCM_S16BE:
    case CODEC_ID_PCM_S16LE:
        rtp_send_samples(s1, s, buf1, size, 2 * st->codec->channels);
        break;
    case CODEC_ID_MP2:
    case CODEC_ID_MP3:
        rtp_send_mpegaudio(s1, s, buf1, size);
        break;
    case CODEC_ID_MPEG1VIDEO:
    case CODEC_ID_MPEG2VIDEO:
        ff_rtp_send_mpegvideo(s1, s, buf1, size);
        break;
    case CODEC_ID_AAC:
        ff_rtp_send_aac(s1, s, buf1, size);
        break;
    case CODEC_ID_MPEG2TS:
        rtp_send_mpegts_raw(s1, s, buf1, size);
        break;
    case CODEC_ID_H264:
        ff_rtp_send_h264(s1, s, buf1, size);     // What  focused on
        break;
    case CODEC_ID_H263:
    case CODEC_ID_H263P:
        ff_rtp_send_h263(s1, s, buf1, size);
        break;
    case CODEC_ID_AMR_NB:
    case CODEC_ID_AMR_WB:
       ff_rtp_send_amr(s1, s, buf1, size);
       break;
    default:
        /* better than nothing : send the codec raw data */
        rtp_send_raw(s1, s, buf1, size);
        break;
    }
    return 0;
}

3.3 rtp_write_trailer

static int rtp_write_trailer(AVFormatContext *s1)
{
    RTPMuxContext *s = s1->priv_data;
    int i;

    for (i = 0; i < s1->nb_streams; i ++) {
        av_freep(&s->buf);
        s ++;
    }

    return 0;
}

4. rtp_h264

static void nal_send(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf, int size, int last)
{
    uint8_t type = buf[0] & 0x1F;

    av_log(s1, AV_LOG_DEBUG, "Sending NAL %x of len %d M=%d\n", buf[0] & 0x1F, size, last);

    /* don't send NAL with type = access unit delimiter, some players don't like it */
    if (type == 9) return;

    if (size <= s->max_payload_size) {
        ff_rtp_send_data(s1, s, buf, size, last);
    } else {
        uint8_t nri = buf[0] & 0x60;

        av_log(s1, AV_LOG_DEBUG, "NAL size %d > %d\n", size, s->max_payload_size);
        s->buf[0] = 28;        /* FU Indicator; Type = 28 ---> FU-A */
        s->buf[0] |= nri;
        s->buf[1] = type;
        s->buf[1] |= 1 << 7;
        buf += 1;
        size -= 1;
        while (size + 2 > s->max_payload_size) {
            memcpy(&s->buf[2], buf, s->max_payload_size - 2);
            ff_rtp_send_data(s1, s, s->buf, s->max_payload_size, 0);
            buf += s->max_payload_size - 2;
            size -= s->max_payload_size - 2;
            s->buf[1] &= ~(1 << 7);
        }
        s->buf[1] |= 1 << 6;
        memcpy(&s->buf[2], buf, size);
        ff_rtp_send_data(s1, s, s->buf, size + 2, last);
    }
}

void ff_rtp_send_h264(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf1, int size)
{
    const uint8_t *r;

    s->timestamp = s->cur_timestamp;
    r = ff_avc_find_startcode(buf1, buf1 + size);
    while (r < buf1 + size) {
        const uint8_t *r1;

        while(!*(r++));
        r1 = ff_avc_find_startcode(r, buf1 + size);
        nal_send(s1, s, r, r1 - r, (r1 == buf1 + size));
        r = r1;
    }
}

/* send an rtp packet. sequence number is incremented, but the caller
   must update the timestamp itself */
void ff_rtp_send_data(AVFormatContext *s1, RTPMuxContext *s, const uint8_t *buf1, int len, int m)
{
    dprintf(s1, "rtp_send_data size=%d\n", len);

    set_rtp_output_stream_index(s1, s->stream_index);

    /* build the RTP header */
    put_byte(s1->pb, (RTP_VERSION << 6));
    put_byte(s1->pb, (s->payload_type & 0x7f) | ((m & 0x01) << 7));
    put_be16(s1->pb, s->seq);
    put_be32(s1->pb, s->timestamp);
    put_be32(s1->pb, s->ssrc);

    put_buffer(s1->pb, buf1, len);
    put_flush_packet(s1->pb);

    s->seq++;
    s->octet_count += len;
    s->packet_count++;
}


原创粉丝点击