blob: 1d3b92de97474d5ad7a177e04294e94cf2992d87 [file] [log] [blame]
/* $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_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;
}
#endif /* PJMEDIA_HAS_VIDEO */