From c82bf15dca00f67a701d126e47ea9075fc9459cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 23 Feb 2015 23:06:01 +0200 Subject: [PATCH] rtpenc: Merge the h264 and hevc packetizers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They share a great deal of common structure; only a few minor bits in the headers differ. This also fixes an off-by-one in sending of the last fragment of large HEVC nals (where it previously sent len+2 bytes, even if it should have been len+RTP_HEVC_HEADERS_SIZE aka len+3). Signed-off-by: Martin Storsjö --- libavformat/Makefile | 3 +- libavformat/rtpenc.c | 4 +- libavformat/rtpenc.h | 3 +- libavformat/rtpenc_h264.c | 136 ---------------------- libavformat/rtpenc_h264_hevc.c | 207 +++++++++++++++++++++++++++++++++ libavformat/rtpenc_hevc.c | 170 --------------------------- 6 files changed, 211 insertions(+), 312 deletions(-) delete mode 100644 libavformat/rtpenc_h264.c create mode 100644 libavformat/rtpenc_h264_hevc.c delete mode 100644 libavformat/rtpenc_hevc.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 387926f0d2..a89d723341 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -299,11 +299,10 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_h261.o \ rtpenc_h263.o \ rtpenc_h263_rfc2190.o \ - rtpenc_hevc.o \ + rtpenc_h264_hevc.o \ rtpenc_jpeg.o \ rtpenc_mpv.o \ rtpenc.o \ - rtpenc_h264.o \ rtpenc_vp8.o \ rtpenc_xiph.o \ avc.o hevc.o diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index bf82da0969..0c1f57a50d 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -577,7 +577,7 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) rtp_send_mpegts_raw(s1, pkt->data, size); break; case AV_CODEC_ID_H264: - ff_rtp_send_h264(s1, pkt->data, size); + ff_rtp_send_h264_hevc(s1, pkt->data, size); break; case AV_CODEC_ID_H261: ff_rtp_send_h261(s1, pkt->data, size); @@ -596,7 +596,7 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) ff_rtp_send_h263(s1, pkt->data, size); break; case AV_CODEC_ID_HEVC: - ff_rtp_send_hevc(s1, pkt->data, size); + ff_rtp_send_h264_hevc(s1, pkt->data, size); break; case AV_CODEC_ID_VORBIS: case AV_CODEC_ID_THEORA: diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index f4925941bf..c6ac8da43d 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -81,12 +81,11 @@ typedef struct RTPMuxContext RTPMuxContext; 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_hevc(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_rfc2190(AVFormatContext *s1, const uint8_t *buf1, int size, const uint8_t *mb_info, int mb_info_size); -void ff_rtp_send_hevc(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size); diff --git a/libavformat/rtpenc_h264.c b/libavformat/rtpenc_h264.c deleted file mode 100644 index baf8b8a695..0000000000 --- a/libavformat/rtpenc_h264.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * RTP packetization for H.264 (RFC3984) - * Copyright (c) 2008 Luca Abeni - * - * 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 - */ - -/** - * @file - * @brief H.264 packetization - * @author Luca Abeni - */ - -#include "libavutil/intreadwrite.h" - -#include "avformat.h" -#include "avc.h" -#include "rtpenc.h" - -static void flush_buffered(AVFormatContext *s1, int last) -{ - RTPMuxContext *s = s1->priv_data; - if (s->buf_ptr != s->buf) { - // If we're only sending one single NAL unit, send it as such, skip - // the STAP-A framing - if (s->buffered_nals == 1) - ff_rtp_send_data(s1, s->buf + 3, s->buf_ptr - s->buf - 3, last); - else - ff_rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, last); - } - s->buf_ptr = s->buf; - s->buffered_nals = 0; -} - -static void nal_send(AVFormatContext *s1, const uint8_t *buf, int size, int last) -{ - RTPMuxContext *s = s1->priv_data; - - av_log(s1, AV_LOG_DEBUG, "Sending NAL %x of len %d M=%d\n", buf[0] & 0x1F, size, last); - if (size <= s->max_payload_size) { - int buffered_size = s->buf_ptr - s->buf; - // Flush buffered NAL units if the current unit doesn't fit - if (buffered_size + 2 + size > s->max_payload_size) { - flush_buffered(s1, 0); - buffered_size = 0; - } - // If we aren't using mode 0, and the NAL unit fits including the - // framing (2 bytes length, plus 1 byte for the STAP-A marker), - // write the unit to the buffer as a STAP-A packet, otherwise flush - // and send as single NAL. - if (buffered_size + 3 + size <= s->max_payload_size && - !(s->flags & FF_RTP_FLAG_H264_MODE0)) { - if (buffered_size == 0) - *s->buf_ptr++ = 24; - AV_WB16(s->buf_ptr, size); - s->buf_ptr += 2; - memcpy(s->buf_ptr, buf, size); - s->buf_ptr += size; - s->buffered_nals++; - } else { - flush_buffered(s1, 0); - ff_rtp_send_data(s1, buf, size, last); - } - } else { - uint8_t type = buf[0] & 0x1F; - uint8_t nri = buf[0] & 0x60; - - flush_buffered(s1, 0); - if (s->flags & FF_RTP_FLAG_H264_MODE0) { - av_log(s1, AV_LOG_ERROR, - "NAL size %d > %d, try -slice-max-size %d\n", size, - s->max_payload_size, s->max_payload_size); - return; - } - 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->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->buf, size + 2, last); - } -} - -void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size) -{ - const uint8_t *r, *end = buf1 + size; - RTPMuxContext *s = s1->priv_data; - - s->timestamp = s->cur_timestamp; - s->buf_ptr = s->buf; - if (s->nal_length_size) - r = ff_avc_mp4_find_startcode(buf1, end, s->nal_length_size) ? buf1 : end; - else - r = ff_avc_find_startcode(buf1, end); - while (r < end) { - const uint8_t *r1; - - if (s->nal_length_size) { - r1 = ff_avc_mp4_find_startcode(r, end, s->nal_length_size); - if (!r1) - r1 = end; - r += s->nal_length_size; - } else { - while (!*(r++)); - r1 = ff_avc_find_startcode(r, end); - } - nal_send(s1, r, r1 - r, r1 == end); - r = r1; - } - flush_buffered(s1, 1); -} diff --git a/libavformat/rtpenc_h264_hevc.c b/libavformat/rtpenc_h264_hevc.c new file mode 100644 index 0000000000..9cfe0e2c64 --- /dev/null +++ b/libavformat/rtpenc_h264_hevc.c @@ -0,0 +1,207 @@ +/* + * RTP packetization for H.264 (RFC3984) + * RTP packetizer for HEVC/H.265 payload format (draft version 6) + * Copyright (c) 2008 Luca Abeni + * Copyright (c) 2014 Thomas Volkert + * + * 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 + */ + +/** + * @file + * @brief H.264/HEVC packetization + * @author Luca Abeni + */ + +#include "libavutil/intreadwrite.h" + +#include "avformat.h" +#include "avc.h" +#include "rtpenc.h" + +static void flush_buffered(AVFormatContext *s1, int last) +{ + RTPMuxContext *s = s1->priv_data; + if (s->buf_ptr != s->buf) { + // If we're only sending one single NAL unit, send it as such, skip + // the STAP-A/AP framing + if (s->buffered_nals == 1) { + enum AVCodecID codec = s1->streams[0]->codec->codec_id; + if (codec == AV_CODEC_ID_H264) + ff_rtp_send_data(s1, s->buf + 3, s->buf_ptr - s->buf - 3, last); + else + ff_rtp_send_data(s1, s->buf + 4, s->buf_ptr - s->buf - 4, last); + } else + ff_rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, last); + } + s->buf_ptr = s->buf; + s->buffered_nals = 0; +} + +static void nal_send(AVFormatContext *s1, const uint8_t *buf, int size, int last) +{ + RTPMuxContext *s = s1->priv_data; + enum AVCodecID codec = s1->streams[0]->codec->codec_id; + + av_log(s1, AV_LOG_DEBUG, "Sending NAL %x of len %d M=%d\n", buf[0] & 0x1F, size, last); + if (size <= s->max_payload_size) { + int buffered_size = s->buf_ptr - s->buf; + int header_size; + int skip_aggregate = 0; + + if (codec == AV_CODEC_ID_H264) { + header_size = 1; + skip_aggregate = s->flags & FF_RTP_FLAG_H264_MODE0; + } else { + header_size = 2; + } + + // Flush buffered NAL units if the current unit doesn't fit + if (buffered_size + 2 + size > s->max_payload_size) { + flush_buffered(s1, 0); + buffered_size = 0; + } + // If we aren't using mode 0, and the NAL unit fits including the + // framing (2 bytes length, plus 1/2 bytes for the STAP-A/AP marker), + // write the unit to the buffer as a STAP-A/AP packet, otherwise flush + // and send as single NAL. + if (buffered_size + 2 + header_size + size <= s->max_payload_size && + !skip_aggregate) { + if (buffered_size == 0) { + if (codec == AV_CODEC_ID_H264) { + *s->buf_ptr++ = 24; + } else { + *s->buf_ptr++ = 48 << 1; + *s->buf_ptr++ = 1; + } + } + AV_WB16(s->buf_ptr, size); + s->buf_ptr += 2; + memcpy(s->buf_ptr, buf, size); + s->buf_ptr += size; + s->buffered_nals++; + } else { + flush_buffered(s1, 0); + ff_rtp_send_data(s1, buf, size, last); + } + } else { + int flag_byte, header_size; + flush_buffered(s1, 0); + if (codec == AV_CODEC_ID_H264 && (s->flags & FF_RTP_FLAG_H264_MODE0)) { + av_log(s1, AV_LOG_ERROR, + "NAL size %d > %d, try -slice-max-size %d\n", size, + s->max_payload_size, s->max_payload_size); + return; + } + av_log(s1, AV_LOG_DEBUG, "NAL size %d > %d\n", size, s->max_payload_size); + if (codec == AV_CODEC_ID_H264) { + uint8_t type = buf[0] & 0x1F; + uint8_t nri = buf[0] & 0x60; + + 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; + + flag_byte = 1; + header_size = 2; + } else { + uint8_t nal_type = (buf[0] >> 1) & 0x3F; + /* + * create the HEVC payload header and transmit the buffer as fragmentation units (FU) + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F| Type | LayerId | TID | + * +-------------+-----------------+ + * + * F = 0 + * Type = 49 (fragmentation unit (FU)) + * LayerId = 0 + * TID = 1 + */ + s->buf[0] = 49 << 1; + s->buf[1] = 1; + + /* + * create the FU header + * + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |S|E| FuType | + * +---------------+ + * + * S = variable + * E = variable + * FuType = NAL unit type + */ + s->buf[2] = nal_type; + /* set the S bit: mark as start fragment */ + s->buf[2] |= 1 << 7; + + /* pass the original NAL header */ + buf += 2; + size -= 2; + + flag_byte = 2; + header_size = 3; + } + + while (size + header_size > s->max_payload_size) { + memcpy(&s->buf[header_size], buf, s->max_payload_size - header_size); + ff_rtp_send_data(s1, s->buf, s->max_payload_size, 0); + buf += s->max_payload_size - header_size; + size -= s->max_payload_size - header_size; + s->buf[flag_byte] &= ~(1 << 7); + } + s->buf[flag_byte] |= 1 << 6; + memcpy(&s->buf[header_size], buf, size); + ff_rtp_send_data(s1, s->buf, size + header_size, last); + } +} + +void ff_rtp_send_h264_hevc(AVFormatContext *s1, const uint8_t *buf1, int size) +{ + const uint8_t *r, *end = buf1 + size; + RTPMuxContext *s = s1->priv_data; + + s->timestamp = s->cur_timestamp; + s->buf_ptr = s->buf; + if (s->nal_length_size) + r = ff_avc_mp4_find_startcode(buf1, end, s->nal_length_size) ? buf1 : end; + else + r = ff_avc_find_startcode(buf1, end); + while (r < end) { + const uint8_t *r1; + + if (s->nal_length_size) { + r1 = ff_avc_mp4_find_startcode(r, end, s->nal_length_size); + if (!r1) + r1 = end; + r += s->nal_length_size; + } else { + while (!*(r++)); + r1 = ff_avc_find_startcode(r, end); + } + nal_send(s1, r, r1 - r, r1 == end); + r = r1; + } + flush_buffered(s1, 1); +} diff --git a/libavformat/rtpenc_hevc.c b/libavformat/rtpenc_hevc.c deleted file mode 100644 index 1dd3dbf6d1..0000000000 --- a/libavformat/rtpenc_hevc.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * RTP packetizer for HEVC/H.265 payload format (draft version 6) - * Copyright (c) 2014 Thomas Volkert - * - * 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 "libavutil/intreadwrite.h" - -#include "avc.h" -#include "avformat.h" -#include "rtpenc.h" - -#define RTP_HEVC_HEADERS_SIZE 3 - -static void flush_buffered(AVFormatContext *s1, int last) -{ - RTPMuxContext *s = s1->priv_data; - if (s->buf_ptr != s->buf) { - // If only sending one single NAL unit, skip the aggregation framing - if (s->buffered_nals == 1) - ff_rtp_send_data(s1, s->buf + 4, s->buf_ptr - s->buf - 4, last); - else - ff_rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, last); - } - s->buf_ptr = s->buf; - s->buffered_nals = 0; -} - -static void nal_send(AVFormatContext *ctx, const uint8_t *buf, int len, int last_packet_of_frame) -{ - RTPMuxContext *rtp_ctx = ctx->priv_data; - int rtp_payload_size = rtp_ctx->max_payload_size - RTP_HEVC_HEADERS_SIZE; - int nal_type = (buf[0] >> 1) & 0x3F; - - /* send it as one single NAL unit? */ - if (len <= rtp_ctx->max_payload_size) { - int buffered_size = rtp_ctx->buf_ptr - rtp_ctx->buf; - /* Flush buffered NAL units if the current unit doesn't fit */ - if (buffered_size + 2 + len > rtp_ctx->max_payload_size) { - flush_buffered(ctx, 0); - buffered_size = 0; - } - /* If the NAL unit fits including the framing, write the unit - * to the buffer as an aggregate packet, otherwise flush and - * send as single NAL. */ - if (buffered_size + 4 + len <= rtp_ctx->max_payload_size) { - if (buffered_size == 0) { - *rtp_ctx->buf_ptr++ = 48 << 1; - *rtp_ctx->buf_ptr++ = 1; - } - AV_WB16(rtp_ctx->buf_ptr, len); - rtp_ctx->buf_ptr += 2; - memcpy(rtp_ctx->buf_ptr, buf, len); - rtp_ctx->buf_ptr += len; - rtp_ctx->buffered_nals++; - } else { - flush_buffered(ctx, 0); - ff_rtp_send_data(ctx, buf, len, last_packet_of_frame); - } - } else { - flush_buffered(ctx, 0); - /* - create the HEVC payload header and transmit the buffer as fragmentation units (FU) - - 0 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |F| Type | LayerId | TID | - +-------------+-----------------+ - - F = 0 - Type = 49 (fragmentation unit (FU)) - LayerId = 0 - TID = 1 - */ - rtp_ctx->buf[0] = 49 << 1; - rtp_ctx->buf[1] = 1; - - /* - create the FU header - - 0 1 2 3 4 5 6 7 - +-+-+-+-+-+-+-+-+ - |S|E| FuType | - +---------------+ - - S = variable - E = variable - FuType = NAL unit type - */ - rtp_ctx->buf[2] = nal_type; - /* set the S bit: mark as start fragment */ - rtp_ctx->buf[2] |= 1 << 7; - - /* pass the original NAL header */ - buf += 2; - len -= 2; - - while (len > rtp_payload_size) { - /* complete and send current RTP packet */ - memcpy(&rtp_ctx->buf[RTP_HEVC_HEADERS_SIZE], buf, rtp_payload_size); - ff_rtp_send_data(ctx, rtp_ctx->buf, rtp_ctx->max_payload_size, 0); - - buf += rtp_payload_size; - len -= rtp_payload_size; - - /* reset the S bit */ - rtp_ctx->buf[2] &= ~(1 << 7); - } - - /* set the E bit: mark as last fragment */ - rtp_ctx->buf[2] |= 1 << 6; - - /* complete and send last RTP packet */ - memcpy(&rtp_ctx->buf[RTP_HEVC_HEADERS_SIZE], buf, len); - ff_rtp_send_data(ctx, rtp_ctx->buf, len + 2, last_packet_of_frame); - } -} - -void ff_rtp_send_hevc(AVFormatContext *ctx, const uint8_t *frame_buf, int frame_size) -{ - const uint8_t *next_NAL_unit; - const uint8_t *buf_ptr, *buf_end = frame_buf + frame_size; - RTPMuxContext *rtp_ctx = ctx->priv_data; - - /* use the default 90 KHz time stamp */ - rtp_ctx->timestamp = rtp_ctx->cur_timestamp; - rtp_ctx->buf_ptr = rtp_ctx->buf; - - if (rtp_ctx->nal_length_size) - buf_ptr = ff_avc_mp4_find_startcode(frame_buf, buf_end, rtp_ctx->nal_length_size) ? frame_buf : buf_end; - else - buf_ptr = ff_avc_find_startcode(frame_buf, buf_end); - - /* find all NAL units and send them as separate packets */ - while (buf_ptr < buf_end) { - if (rtp_ctx->nal_length_size) { - next_NAL_unit = ff_avc_mp4_find_startcode(buf_ptr, buf_end, rtp_ctx->nal_length_size); - if (!next_NAL_unit) - next_NAL_unit = buf_end; - - buf_ptr += rtp_ctx->nal_length_size; - } else { - while (!*(buf_ptr++)) - ; - next_NAL_unit = ff_avc_find_startcode(buf_ptr, buf_end); - } - /* send the next NAL unit */ - nal_send(ctx, buf_ptr, next_NAL_unit - buf_ptr, next_NAL_unit == buf_end); - - /* jump to the next NAL unit */ - buf_ptr = next_NAL_unit; - } - flush_buffered(ctx, 1); -}