The packetizer only supports splitting at GOB headers - if such aren't available frequently enough, it splits at any random byte offset (not at a macroblock boundary either, which would be allowed by the spec) and sends a payload header pretend that it starts with a GOB header. As long as a receiver doesn't try to handle such cases cleverly but just drops broken frames, this shouldn't matter too much in practice. Signed-off-by: Martin Storsjö <martin@martin.st>tags/n2.6
| @@ -7,6 +7,7 @@ version <next>: | |||||
| - avplay now exits by default at the end of playback | - avplay now exits by default at the end of playback | ||||
| - XCB-based screen-grabber | - XCB-based screen-grabber | ||||
| - creating DASH compatible fragmented MP4, MPEG-DASH segmenting muxer | - creating DASH compatible fragmented MP4, MPEG-DASH segmenting muxer | ||||
| - H.261 RTP payload format (RFC 4587) depacketizer and experimental packetizer | |||||
| version 11: | version 11: | ||||
| @@ -32,6 +32,7 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ | |||||
| rtpdec_amr.o \ | rtpdec_amr.o \ | ||||
| rtpdec_asf.o \ | rtpdec_asf.o \ | ||||
| rtpdec_g726.o \ | rtpdec_g726.o \ | ||||
| rtpdec_h261.o \ | |||||
| rtpdec_h263.o \ | rtpdec_h263.o \ | ||||
| rtpdec_h263_rfc2190.o \ | rtpdec_h263_rfc2190.o \ | ||||
| rtpdec_h264.o \ | rtpdec_h264.o \ | ||||
| @@ -290,6 +291,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ | |||||
| rtpenc_aac.o \ | rtpenc_aac.o \ | ||||
| rtpenc_latm.o \ | rtpenc_latm.o \ | ||||
| rtpenc_amr.o \ | rtpenc_amr.o \ | ||||
| rtpenc_h261.o \ | |||||
| rtpenc_h263.o \ | rtpenc_h263.o \ | ||||
| rtpenc_h263_rfc2190.o \ | rtpenc_h263_rfc2190.o \ | ||||
| rtpenc_hevc.o \ | rtpenc_hevc.o \ | ||||
| @@ -66,6 +66,7 @@ void ff_register_rtp_dynamic_payload_handlers(void) | |||||
| ff_register_dynamic_payload_handler(&ff_g726_24_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_g726_24_dynamic_handler); | ||||
| ff_register_dynamic_payload_handler(&ff_g726_32_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_g726_32_dynamic_handler); | ||||
| ff_register_dynamic_payload_handler(&ff_g726_40_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_g726_40_dynamic_handler); | ||||
| ff_register_dynamic_payload_handler(&ff_h261_dynamic_handler); | |||||
| ff_register_dynamic_payload_handler(&ff_h263_1998_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_h263_1998_dynamic_handler); | ||||
| ff_register_dynamic_payload_handler(&ff_h263_2000_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_h263_2000_dynamic_handler); | ||||
| ff_register_dynamic_payload_handler(&ff_h263_rfc2190_dynamic_handler); | ff_register_dynamic_payload_handler(&ff_h263_rfc2190_dynamic_handler); | ||||
| @@ -41,6 +41,7 @@ extern RTPDynamicProtocolHandler ff_g726_16_dynamic_handler; | |||||
| extern RTPDynamicProtocolHandler ff_g726_24_dynamic_handler; | extern RTPDynamicProtocolHandler ff_g726_24_dynamic_handler; | ||||
| extern RTPDynamicProtocolHandler ff_g726_32_dynamic_handler; | extern RTPDynamicProtocolHandler ff_g726_32_dynamic_handler; | ||||
| extern RTPDynamicProtocolHandler ff_g726_40_dynamic_handler; | extern RTPDynamicProtocolHandler ff_g726_40_dynamic_handler; | ||||
| extern RTPDynamicProtocolHandler ff_h261_dynamic_handler; | |||||
| extern RTPDynamicProtocolHandler ff_h263_1998_dynamic_handler; | extern RTPDynamicProtocolHandler ff_h263_1998_dynamic_handler; | ||||
| extern RTPDynamicProtocolHandler ff_h263_2000_dynamic_handler; | extern RTPDynamicProtocolHandler ff_h263_2000_dynamic_handler; | ||||
| extern RTPDynamicProtocolHandler ff_h263_rfc2190_dynamic_handler; | extern RTPDynamicProtocolHandler ff_h263_rfc2190_dynamic_handler; | ||||
| @@ -0,0 +1,202 @@ | |||||
| /* | |||||
| * RTP parser for H.261 payload format (RFC 4587) | |||||
| * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com> | |||||
| * | |||||
| * This file is part of Libav. | |||||
| * | |||||
| * Libav is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU Lesser General Public | |||||
| * License as published by the Free Software Foundation; either | |||||
| * version 2.1 of the License, or (at your option) any later version. | |||||
| * | |||||
| * Libav is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
| * Lesser General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU Lesser General Public | |||||
| * License along with Libav; if not, write to the Free Software | |||||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||||
| */ | |||||
| #include "libavcodec/get_bits.h" | |||||
| #include "avformat.h" | |||||
| #include "rtpdec_formats.h" | |||||
| #define RTP_H261_PAYLOAD_HEADER_SIZE 4 | |||||
| struct PayloadContext { | |||||
| AVIOContext *buf; | |||||
| uint8_t endbyte; | |||||
| int endbyte_bits; | |||||
| uint32_t timestamp; | |||||
| }; | |||||
| static av_cold PayloadContext *h261_new_context(void) | |||||
| { | |||||
| return av_mallocz(sizeof(PayloadContext)); | |||||
| } | |||||
| static void h261_free_dyn_buffer(AVIOContext **dyn_buf) | |||||
| { | |||||
| uint8_t *ptr_dyn_buffer; | |||||
| avio_close_dyn_buf(*dyn_buf, &ptr_dyn_buffer); | |||||
| av_free(ptr_dyn_buffer); | |||||
| *dyn_buf = NULL; | |||||
| } | |||||
| static av_cold void h261_free_context(PayloadContext *pl_ctx) | |||||
| { | |||||
| /* return if context is invalid */ | |||||
| if (!pl_ctx) | |||||
| return; | |||||
| /* free buffer if it is valid */ | |||||
| if (pl_ctx->buf) { | |||||
| h261_free_dyn_buffer(&pl_ctx->buf); | |||||
| } | |||||
| /* free context */ | |||||
| av_free(pl_ctx); | |||||
| } | |||||
| static av_cold int h261_init(AVFormatContext *ctx, int st_index, | |||||
| PayloadContext *data) | |||||
| { | |||||
| if (st_index < 0) | |||||
| return 0; | |||||
| ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_FULL; | |||||
| return 0; | |||||
| } | |||||
| static int h261_handle_packet(AVFormatContext *ctx, PayloadContext *rtp_h261_ctx, | |||||
| AVStream *st, AVPacket *pkt, uint32_t *timestamp, | |||||
| const uint8_t *buf, int len, uint16_t seq, | |||||
| int flags) | |||||
| { | |||||
| int sbit, ebit, gobn, mbap, quant; | |||||
| int res; | |||||
| /* drop data of previous packets in case of non-continuous (lossy) packet stream */ | |||||
| if (rtp_h261_ctx->buf && rtp_h261_ctx->timestamp != *timestamp) { | |||||
| h261_free_dyn_buffer(&rtp_h261_ctx->buf); | |||||
| rtp_h261_ctx->endbyte_bits = 0; | |||||
| } | |||||
| /* sanity check for size of input packet: 1 byte payload at least */ | |||||
| if (len < RTP_H261_PAYLOAD_HEADER_SIZE + 1) { | |||||
| av_log(ctx, AV_LOG_ERROR, "Too short RTP/H.261 packet, got %d bytes\n", len); | |||||
| return AVERROR_INVALIDDATA; | |||||
| } | |||||
| /* | |||||
| * decode the H.261 payload header according to section 4.1 of RFC 4587: | |||||
| * (uses 4 bytes between RTP header and H.261 stream per packet) | |||||
| * | |||||
| * 0 1 2 3 | |||||
| * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |||||
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |||||
| * |SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD | | |||||
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |||||
| * | |||||
| * Start bit position (SBIT): 3 bits | |||||
| * End bit position (EBIT): 3 bits | |||||
| * INTRA-frame encoded data (I): 1 bit | |||||
| * Motion Vector flag (V): 1 bit | |||||
| * GOB number (GOBN): 4 bits | |||||
| * Macroblock address predictor (MBAP): 5 bits | |||||
| * Quantizer (QUANT): 5 bits | |||||
| * Horizontal motion vector data (HMVD): 5 bits | |||||
| * Vertical motion vector data (VMVD): 5 bits | |||||
| */ | |||||
| sbit = (buf[0] >> 5) & 0x07; | |||||
| ebit = (buf[0] >> 2) & 0x07; | |||||
| gobn = (buf[1] >> 4) & 0x0f; | |||||
| mbap = ((buf[1] << 1) & 0x1e) | ((buf[2] >> 7) & 0x01); | |||||
| quant = (buf[2] >> 2) & 0x1f; | |||||
| /* pass the H.261 payload header and continue with the actual payload */ | |||||
| buf += RTP_H261_PAYLOAD_HEADER_SIZE; | |||||
| len -= RTP_H261_PAYLOAD_HEADER_SIZE; | |||||
| /* start frame buffering with new dynamic buffer */ | |||||
| if (!rtp_h261_ctx->buf) { | |||||
| /* sanity check: a new frame starts with gobn=0, sbit=0, mbap=0, quant=0 */ | |||||
| if (!gobn && !sbit && !mbap && !quant) { | |||||
| res = avio_open_dyn_buf(&rtp_h261_ctx->buf); | |||||
| if (res < 0) | |||||
| return res; | |||||
| /* update the timestamp in the frame packet with the one from the RTP packet */ | |||||
| rtp_h261_ctx->timestamp = *timestamp; | |||||
| } else { | |||||
| /* frame not started yet, need more packets */ | |||||
| return AVERROR(EAGAIN); | |||||
| } | |||||
| } | |||||
| /* do the "byte merging" at the boundaries of two consecutive frame fragments */ | |||||
| if (rtp_h261_ctx->endbyte_bits || sbit) { | |||||
| if (rtp_h261_ctx->endbyte_bits == sbit) { | |||||
| rtp_h261_ctx->endbyte |= buf[0] & (0xff >> sbit); | |||||
| rtp_h261_ctx->endbyte_bits = 0; | |||||
| buf++; | |||||
| len--; | |||||
| avio_w8(rtp_h261_ctx->buf, rtp_h261_ctx->endbyte); | |||||
| } else { | |||||
| /* ebit/sbit values inconsistent, assuming packet loss */ | |||||
| GetBitContext gb; | |||||
| init_get_bits(&gb, buf, len*8 - ebit); | |||||
| skip_bits(&gb, sbit); | |||||
| if (rtp_h261_ctx->endbyte_bits) { | |||||
| rtp_h261_ctx->endbyte |= get_bits(&gb, 8 - rtp_h261_ctx->endbyte_bits); | |||||
| avio_w8(rtp_h261_ctx->buf, rtp_h261_ctx->endbyte); | |||||
| } | |||||
| while (get_bits_left(&gb) >= 8) | |||||
| avio_w8(rtp_h261_ctx->buf, get_bits(&gb, 8)); | |||||
| rtp_h261_ctx->endbyte_bits = get_bits_left(&gb); | |||||
| if (rtp_h261_ctx->endbyte_bits) | |||||
| rtp_h261_ctx->endbyte = get_bits(&gb, rtp_h261_ctx->endbyte_bits) << | |||||
| (8 - rtp_h261_ctx->endbyte_bits); | |||||
| ebit = 0; | |||||
| len = 0; | |||||
| } | |||||
| } | |||||
| if (ebit) { | |||||
| if (len > 0) | |||||
| avio_write(rtp_h261_ctx->buf, buf, len - 1); | |||||
| rtp_h261_ctx->endbyte_bits = 8 - ebit; | |||||
| rtp_h261_ctx->endbyte = buf[len - 1] & (0xff << ebit); | |||||
| } else { | |||||
| avio_write(rtp_h261_ctx->buf, buf, len); | |||||
| } | |||||
| /* RTP marker bit means: last fragment of current frame was received; | |||||
| otherwise, an additional fragment is needed for the current frame */ | |||||
| if (!(flags & RTP_FLAG_MARKER)) | |||||
| return AVERROR(EAGAIN); | |||||
| /* write the completed last byte from the "byte merging" */ | |||||
| if (rtp_h261_ctx->endbyte_bits) | |||||
| avio_w8(rtp_h261_ctx->buf, rtp_h261_ctx->endbyte); | |||||
| rtp_h261_ctx->endbyte_bits = 0; | |||||
| /* close frame buffering and create resulting A/V packet */ | |||||
| res = ff_rtp_finalize_packet(pkt, &rtp_h261_ctx->buf, st->index); | |||||
| if (res < 0) | |||||
| return res; | |||||
| return 0; | |||||
| } | |||||
| RTPDynamicProtocolHandler ff_h261_dynamic_handler = { | |||||
| .enc_name = "H261", | |||||
| .codec_type = AVMEDIA_TYPE_VIDEO, | |||||
| .codec_id = AV_CODEC_ID_H261, | |||||
| .init = h261_init, | |||||
| .alloc = h261_new_context, | |||||
| .free = h261_free_context, | |||||
| .parse_packet = h261_handle_packet, | |||||
| .static_payload_id = 31, | |||||
| }; | |||||
| @@ -49,6 +49,7 @@ static const AVClass rtp_muxer_class = { | |||||
| static int is_supported(enum AVCodecID id) | static int is_supported(enum AVCodecID id) | ||||
| { | { | ||||
| switch(id) { | switch(id) { | ||||
| case AV_CODEC_ID_H261: | |||||
| case AV_CODEC_ID_H263: | case AV_CODEC_ID_H263: | ||||
| case AV_CODEC_ID_H263P: | case AV_CODEC_ID_H263P: | ||||
| case AV_CODEC_ID_H264: | case AV_CODEC_ID_H264: | ||||
| @@ -88,7 +89,7 @@ static int is_supported(enum AVCodecID id) | |||||
| static int rtp_write_header(AVFormatContext *s1) | static int rtp_write_header(AVFormatContext *s1) | ||||
| { | { | ||||
| RTPMuxContext *s = s1->priv_data; | RTPMuxContext *s = s1->priv_data; | ||||
| int n; | |||||
| int n, ret = AVERROR(EINVAL); | |||||
| AVStream *st; | AVStream *st; | ||||
| if (s1->nb_streams != 1) { | if (s1->nb_streams != 1) { | ||||
| @@ -191,6 +192,17 @@ static int rtp_write_header(AVFormatContext *s1) | |||||
| s->max_payload_size = n * TS_PACKET_SIZE; | s->max_payload_size = n * TS_PACKET_SIZE; | ||||
| s->buf_ptr = s->buf; | s->buf_ptr = s->buf; | ||||
| break; | break; | ||||
| case AV_CODEC_ID_H261: | |||||
| if (s1->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { | |||||
| av_log(s, AV_LOG_ERROR, | |||||
| "Packetizing H261 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: | case AV_CODEC_ID_H264: | ||||
| /* check for H.264 MP4 syntax */ | /* check for H.264 MP4 syntax */ | ||||
| if (st->codec->extradata_size > 4 && st->codec->extradata[0] == 1) { | if (st->codec->extradata_size > 4 && st->codec->extradata[0] == 1) { | ||||
| @@ -273,7 +285,7 @@ defaultcase: | |||||
| fail: | fail: | ||||
| av_freep(&s->buf); | av_freep(&s->buf); | ||||
| return AVERROR(EINVAL); | |||||
| return ret; | |||||
| } | } | ||||
| /* send an rtcp sender report packet */ | /* send an rtcp sender report packet */ | ||||
| @@ -567,6 +579,9 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) | |||||
| case AV_CODEC_ID_H264: | case AV_CODEC_ID_H264: | ||||
| ff_rtp_send_h264(s1, pkt->data, size); | ff_rtp_send_h264(s1, pkt->data, size); | ||||
| break; | break; | ||||
| case AV_CODEC_ID_H261: | |||||
| ff_rtp_send_h261(s1, pkt->data, size); | |||||
| break; | |||||
| case AV_CODEC_ID_H263: | case AV_CODEC_ID_H263: | ||||
| if (s->flags & FF_RTP_FLAG_RFC2190) { | if (s->flags & FF_RTP_FLAG_RFC2190) { | ||||
| int mb_info_size = 0; | int mb_info_size = 0; | ||||
| @@ -81,6 +81,7 @@ typedef struct RTPMuxContext RTPMuxContext; | |||||
| void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); | void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); | ||||
| void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size); | void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size); | ||||
| void ff_rtp_send_h261(AVFormatContext *s1, const uint8_t *buf1, int size); | |||||
| void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size); | void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size); | ||||
| void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf1, int size, | void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf1, int size, | ||||
| const uint8_t *mb_info, int mb_info_size); | const uint8_t *mb_info, int mb_info_size); | ||||
| @@ -0,0 +1,102 @@ | |||||
| /* | |||||
| * RTP packetization for H.261 video (RFC 4587) | |||||
| * Copyright (c) 2014 Thomas Volkert <thomas@homer-conferencing.com> | |||||
| * | |||||
| * This file is part of Libav. | |||||
| * | |||||
| * Libav is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU Lesser General Public | |||||
| * License as published by the Free Software Foundation; either | |||||
| * version 2.1 of the License, or (at your option) any later version. | |||||
| * | |||||
| * Libav is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
| * Lesser General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU Lesser General Public | |||||
| * License along with Libav; if not, write to the Free Software | |||||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||||
| */ | |||||
| #include "avformat.h" | |||||
| #include "rtpenc.h" | |||||
| #define RTP_H261_HEADER_SIZE 4 | |||||
| static const uint8_t *find_resync_marker_reverse(const uint8_t *restrict start, | |||||
| const uint8_t *restrict end) | |||||
| { | |||||
| const uint8_t *p = end - 1; | |||||
| start += 1; /* Make sure we never return the original start. */ | |||||
| for (; p > start; p--) { | |||||
| if (p[0] == 0 && p[1] == 1) | |||||
| return p; | |||||
| } | |||||
| return end; | |||||
| } | |||||
| void ff_rtp_send_h261(AVFormatContext *ctx, const uint8_t *frame_buf, int frame_size) | |||||
| { | |||||
| int cur_frame_size; | |||||
| int last_packet_of_frame; | |||||
| RTPMuxContext *rtp_ctx = ctx->priv_data; | |||||
| /* use the default 90 KHz time stamp */ | |||||
| rtp_ctx->timestamp = rtp_ctx->cur_timestamp; | |||||
| /* continue as long as not all frame data is processed */ | |||||
| while (frame_size > 0) { | |||||
| /* | |||||
| * encode the H.261 payload header according to section 4.1 of RFC 4587: | |||||
| * (uses 4 bytes between RTP header and H.261 stream per packet) | |||||
| * | |||||
| * 0 1 2 3 | |||||
| * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |||||
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |||||
| * |SBIT |EBIT |I|V| GOBN | MBAP | QUANT | HMVD | VMVD | | |||||
| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |||||
| * | |||||
| * Start bit position (SBIT): 3 bits | |||||
| * End bit position (EBIT): 3 bits | |||||
| * INTRA-frame encoded data (I): 1 bit | |||||
| * Motion Vector flag (V): 1 bit | |||||
| * GOB number (GOBN): 4 bits | |||||
| * Macroblock address predictor (MBAP): 5 bits | |||||
| * Quantizer (QUANT): 5 bits | |||||
| * Horizontal motion vector data (HMVD): 5 bits | |||||
| * Vertical motion vector data (VMVD): 5 bits | |||||
| */ | |||||
| rtp_ctx->buf[0] = 1; /* sbit=0, ebit=0, i=0, v=1 */ | |||||
| rtp_ctx->buf[1] = 0; /* gobn=0, mbap=0 */ | |||||
| rtp_ctx->buf[2] = 0; /* quant=0, hmvd=5 */ | |||||
| rtp_ctx->buf[3] = 0; /* vmvd=0 */ | |||||
| if (frame_size < 2 || frame_buf[0] != 0 || frame_buf[1] != 1) { | |||||
| /* A full, correct fix for this would be to make the H261 encoder | |||||
| * support inserting extra GOB headers (triggered by setting e.g. | |||||
| * "-ps 1"), and including information about macroblock boundaries | |||||
| * (such as for h263_rfc2190). */ | |||||
| av_log(ctx, AV_LOG_WARNING, | |||||
| "RTP/H261 packet not cut at a GOB boundary, not signaled correctly\n"); | |||||
| } | |||||
| cur_frame_size = FFMIN(rtp_ctx->max_payload_size - RTP_H261_HEADER_SIZE, frame_size); | |||||
| /* look for a better place to split the frame into packets */ | |||||
| if (cur_frame_size < frame_size) { | |||||
| const uint8_t *packet_end = find_resync_marker_reverse(frame_buf, | |||||
| frame_buf + cur_frame_size); | |||||
| cur_frame_size = packet_end - frame_buf; | |||||
| } | |||||
| /* calculate the "marker" bit for the RTP header */ | |||||
| last_packet_of_frame = cur_frame_size == frame_size; | |||||
| /* complete and send RTP packet */ | |||||
| memcpy(&rtp_ctx->buf[RTP_H261_HEADER_SIZE], frame_buf, cur_frame_size); | |||||
| ff_rtp_send_data(ctx, rtp_ctx->buf, RTP_H261_HEADER_SIZE + cur_frame_size, last_packet_of_frame); | |||||
| frame_buf += cur_frame_size; | |||||
| frame_size -= cur_frame_size; | |||||
| } | |||||
| } | |||||
| @@ -498,6 +498,20 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, | |||||
| payload_type, mode, config ? config : ""); | payload_type, mode, config ? config : ""); | ||||
| break; | break; | ||||
| } | } | ||||
| case AV_CODEC_ID_H261: | |||||
| { | |||||
| const char *pic_fmt = NULL; | |||||
| /* only QCIF and CIF are specified as supported in RFC 4587 */ | |||||
| if (c->width == 176 && c->height == 144) | |||||
| pic_fmt = "QCIF=1"; | |||||
| else if (c->width == 352 && c->height == 288) | |||||
| pic_fmt = "CIF=1"; | |||||
| if (payload_type >= RTP_PT_PRIVATE) | |||||
| av_strlcatf(buff, size, "a=rtpmap:%d H261/90000\r\n", payload_type); | |||||
| if (pic_fmt) | |||||
| av_strlcatf(buff, size, "a=fmtp:%d %s\r\n", payload_type, pic_fmt); | |||||
| break; | |||||
| } | |||||
| case AV_CODEC_ID_H263: | case AV_CODEC_ID_H263: | ||||
| case AV_CODEC_ID_H263P: | case AV_CODEC_ID_H263P: | ||||
| /* a=framesize is required by 3GPP TS 26.234 (PSS). It | /* a=framesize is required by 3GPP TS 26.234 (PSS). It | ||||
| @@ -30,8 +30,8 @@ | |||||
| #include "libavutil/version.h" | #include "libavutil/version.h" | ||||
| #define LIBAVFORMAT_VERSION_MAJOR 56 | #define LIBAVFORMAT_VERSION_MAJOR 56 | ||||
| #define LIBAVFORMAT_VERSION_MINOR 7 | |||||
| #define LIBAVFORMAT_VERSION_MICRO 2 | |||||
| #define LIBAVFORMAT_VERSION_MINOR 8 | |||||
| #define LIBAVFORMAT_VERSION_MICRO 0 | |||||
| #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | ||||
| LIBAVFORMAT_VERSION_MINOR, \ | LIBAVFORMAT_VERSION_MINOR, \ | ||||