diff options
Diffstat (limited to 'pjmedia/src/pjmedia-codec/h263_packetizer.c')
-rw-r--r-- | pjmedia/src/pjmedia-codec/h263_packetizer.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia-codec/h263_packetizer.c b/pjmedia/src/pjmedia-codec/h263_packetizer.c new file mode 100644 index 00000000..149c2e64 --- /dev/null +++ b/pjmedia/src/pjmedia-codec/h263_packetizer.c @@ -0,0 +1,287 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 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 <pjmedia-codec/h263_packetizer.h> +#include <pjmedia/types.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/string.h> + +#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_MTU; + } + + *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 = 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 = 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 = q - bits; + + pktz->unpack_prev_lost = PJ_FALSE; + + return PJ_SUCCESS; +} |