/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "h263_packetizer.c" /* H.263 packetizer definition */ struct pjmedia_h263_packetizer { /* Current settings */ pjmedia_h263_packetizer_cfg cfg; /* Unpacketizer state */ unsigned unpack_last_sync_pos; pj_bool_t unpack_prev_lost; }; /* * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 * bitstream. */ static pj_uint8_t* find_sync_point(pj_uint8_t *data, pj_size_t data_len) { pj_uint8_t *p = data, *end = data+data_len-1; while (p < end && (*p || *(p+1))) ++p; if (p == end) return NULL; return p; } /* * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 * bitstream, in reversed manner. */ static pj_uint8_t* find_sync_point_rev(pj_uint8_t *data, pj_size_t data_len) { pj_uint8_t *p = data+data_len-2; while (p >= data && (*p || *(p+1))) --p; if (p < data) return (data + data_len); return p; } /* * Create H263 packetizer. */ PJ_DEF(pj_status_t) pjmedia_h263_packetizer_create( pj_pool_t *pool, const pjmedia_h263_packetizer_cfg *cfg, pjmedia_h263_packetizer **p) { pjmedia_h263_packetizer *p_; PJ_ASSERT_RETURN(pool && p, PJ_EINVAL); if (cfg && cfg->mode != PJMEDIA_H263_PACKETIZER_MODE_RFC4629) return PJ_ENOTSUP; p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h263_packetizer); if (cfg) { pj_memcpy(&p_->cfg, cfg, sizeof(*cfg)); } else { p_->cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629; p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; } *p = p_; return PJ_SUCCESS; } /* * Generate an RTP payload from H.263 frame bitstream, in-place processing. */ PJ_DEF(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz, pj_uint8_t *bits, pj_size_t bits_len, unsigned *pos, const pj_uint8_t **payload, pj_size_t *payload_len) { pj_uint8_t *p, *end; pj_assert(pktz && bits && pos && payload && payload_len); pj_assert(*pos <= bits_len); p = bits + *pos; end = bits + bits_len; /* Put two octets payload header */ if ((end-p > 2) && *p==0 && *(p+1)==0) { /* The bitstream starts with synchronization point, just override * the two zero octets (sync point mark) for payload header. */ *p = 0x04; } else { /* Not started in synchronization point, we will use two octets * preceeding the bitstream for payload header! */ if (*pos < 2) { /* Invalid H263 bitstream, it's not started with PSC */ return PJ_EINVAL; } p -= 2; *p = 0; } *(p+1) = 0; /* When bitstream truncation needed because of payload length/MTU * limitation, try to use sync point for the payload boundary. */ if (end-p > pktz->cfg.mtu) { end = find_sync_point_rev(p+2, pktz->cfg.mtu-2); } *payload = p; *payload_len = end-p; *pos = (unsigned)(end - bits); return PJ_SUCCESS; } /* * Append an RTP payload to a H.263 picture bitstream. */ PJ_DEF(pj_status_t) pjmedia_h263_unpacketize (pjmedia_h263_packetizer *pktz, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_size, unsigned *pos) { pj_uint8_t P, V, PLEN; const pj_uint8_t *p = payload; pj_uint8_t *q; q = bits + *pos; /* Check if this is a missing/lost packet */ if (payload == NULL) { pktz->unpack_prev_lost = PJ_TRUE; return PJ_SUCCESS; } /* H263 payload header size is two octets */ if (payload_len < 2) { /* Invalid bitstream, discard this payload */ pktz->unpack_prev_lost = PJ_TRUE; return PJ_EINVAL; } /* Reset last sync point for every new picture bitstream */ if (*pos == 0) pktz->unpack_last_sync_pos = 0; /* Get payload header info */ P = *p & 0x04; V = *p & 0x02; PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3); /* Get start bitstream pointer */ p += 2; /* Skip payload header */ if (V) p += 1; /* Skip VRC data */ if (PLEN) p += PLEN; /* Skip extra picture header data */ /* Get bitstream length */ if (payload_len > (pj_size_t)(p - payload)) { payload_len -= (p - payload); } else { /* Invalid bitstream, discard this payload */ pktz->unpack_prev_lost = PJ_TRUE; return PJ_EINVAL; } /* Validate bitstream length */ if (bits_size < *pos + payload_len + 2) { /* Insufficient bistream buffer, discard this payload */ pj_assert(!"Insufficient H.263 bitstream buffer"); pktz->unpack_prev_lost = PJ_TRUE; return PJ_ETOOSMALL; } /* Start writing bitstream */ /* No sync point flag */ if (!P) { if (*pos == 0) { /* Previous packet must be lost */ pktz->unpack_prev_lost = PJ_TRUE; /* If there is extra picture header, let's use it. */ if (PLEN) { /* Write two zero octets for PSC */ *q++ = 0; *q++ = 0; /* Copy the picture header */ p -= PLEN; pj_memcpy(q, p, PLEN); p += PLEN; q += PLEN; } } else if (pktz->unpack_prev_lost) { /* If prev packet was lost, revert the bitstream pointer to * the last sync point. */ pj_assert(pktz->unpack_last_sync_pos <= *pos); q = bits + pktz->unpack_last_sync_pos; } /* There was packet lost, see if this payload contain sync point * (usable data). */ if (pktz->unpack_prev_lost) { pj_uint8_t *sync; sync = find_sync_point((pj_uint8_t*)p, payload_len); if (sync) { /* Got sync point, update P/sync-point flag */ P = 1; /* Skip the two zero octets */ sync += 2; /* Update payload length and start bitstream pointer */ payload_len -= (sync - p); p = sync; } else { /* No sync point in it, just discard this payload */ return PJ_EIGNORED; } } } /* Write two zero octets when payload flagged with sync point */ if (P) { pktz->unpack_last_sync_pos = (unsigned)(q - bits); *q++ = 0; *q++ = 0; } /* Write the payload to the bitstream */ pj_memcpy(q, p, payload_len); q += payload_len; /* Update the bitstream writing offset */ *pos = (unsigned)(q - bits); pktz->unpack_prev_lost = PJ_FALSE; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */