/* * RTP packetizer for HEVC/H.265 payload format (draft version 6) * Copyright (c) 2014 Thomas Volkert * * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; 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); }