| /* $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 <pjmedia-codec/h263_packetizer.h> |
| #include <pjmedia/types.h> |
| #include <pj/assert.h> |
| #include <pj/errno.h> |
| #include <pj/string.h> |
| |
| |
| #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 */ |