/** * @file gst_video/h264.c H.264 Packetization * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include "gst_video.h" /** NAL unit types (RFC 3984, Table 1) */ enum { H264_NAL_UNKNOWN = 0, /* 1-23 NAL unit Single NAL unit packet per H.264 */ H264_NAL_SLICE = 1, H264_NAL_DPA = 2, H264_NAL_DPB = 3, H264_NAL_DPC = 4, H264_NAL_IDR_SLICE = 5, H264_NAL_SEI = 6, H264_NAL_SPS = 7, H264_NAL_PPS = 8, H264_NAL_AUD = 9, H264_NAL_END_SEQUENCE = 10, H264_NAL_END_STREAM = 11, H264_NAL_FILLER_DATA = 12, H264_NAL_SPS_EXT = 13, H264_NAL_AUX_SLICE = 19, H264_NAL_STAP_A = 24, /**< Single-time aggregation packet */ H264_NAL_STAP_B = 25, /**< Single-time aggregation packet */ H264_NAL_MTAP16 = 26, /**< Multi-time aggregation packet */ H264_NAL_MTAP24 = 27, /**< Multi-time aggregation packet */ H264_NAL_FU_A = 28, /**< Fragmentation unit */ H264_NAL_FU_B = 29, /**< Fragmentation unit */ }; const uint8_t h264_level_idc = 0x0c; /* * Find the NAL start sequence in a H.264 byte stream * * @note: copied from ffmpeg source */ static const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end) { const uint8_t *a = p + 4 - ((long)p & 3); for (end -= 3; p < a && p < end; p++ ) { if (p[0] == 0 && p[1] == 0 && p[2] == 1) return p; } for (end -= 3; p < end; p += 4) { uint32_t x = *(const uint32_t*)(void *)p; if ( (x - 0x01010101) & (~x) & 0x80808080 ) { if (p[1] == 0 ) { if ( p[0] == 0 && p[2] == 1 ) return p; if ( p[2] == 0 && p[3] == 1 ) return p+1; } if ( p[3] == 0 ) { if ( p[2] == 0 && p[4] == 1 ) return p+2; if ( p[4] == 0 && p[5] == 1 ) return p+3; } } } for (end += 3; p < end; p++) { if (p[0] == 0 && p[1] == 0 && p[2] == 1) return p; } return end + 3; } static int rtp_send_data(const uint8_t *hdr, size_t hdr_sz, const uint8_t *buf, size_t sz, bool eof, videnc_packet_h *pkth, void *arg) { return pkth(eof, hdr, hdr_sz, buf, sz, arg); } static int h264_nal_send(bool first, bool last, bool marker, uint32_t ihdr, const uint8_t *buf, size_t size, size_t maxsz, videnc_packet_h *pkth, void *arg) { uint8_t hdr = (uint8_t)ihdr; int err = 0; if (first && last && size <= maxsz) { err = rtp_send_data(&hdr, 1, buf, size, marker, pkth, arg); } else { uint8_t fu_hdr[2]; const uint8_t type = hdr & 0x1f; const uint8_t nri = hdr & 0x60; const size_t sz = maxsz - 2; fu_hdr[0] = nri | H264_NAL_FU_A; fu_hdr[1] = first ? (1<<7 | type) : type; while (size > sz) { err |= rtp_send_data(fu_hdr, 2, buf, sz, false, pkth, arg); buf += sz; size -= sz; fu_hdr[1] &= ~(1 << 7); } if (last) fu_hdr[1] |= 1<<6; /* end bit */ err |= rtp_send_data(fu_hdr, 2, buf, size, marker && last, pkth, arg); } return err; } int h264_packetize(const uint8_t *buf, size_t len, size_t pktsize, videnc_packet_h *pkth, void *arg) { const uint8_t *start = buf; const uint8_t *end = start + len; const uint8_t *r; int err = 0; r = h264_find_startcode(buf, end); while (r < end) { const uint8_t *r1; /* skip zeros */ while (!*(r++)) ; r1 = h264_find_startcode(r, end); err |= h264_nal_send(true, true, (r1 >= end), r[0], r+1, r1-r-1, pktsize, pkth, arg); r = r1; } return err; }