blob: 66d23c9d497277faa2096fe7bac3153b86da9ccb [file] [log] [blame]
Nanang Izzuddind91af572011-03-31 17:29:54 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-codec/h263_packetizer.h>
20#include <pjmedia/types.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/string.h>
24
25#define THIS_FILE "h263_packetizer.c"
26
27
28/* H.263 packetizer definition */
29struct pjmedia_h263_packetizer {
30 /* Current settings */
31 pjmedia_h263_packetizer_cfg cfg;
32
33 /* Unpacketizer state */
34 unsigned unpack_last_sync_pos;
35 pj_bool_t unpack_prev_lost;
36};
37
38
39/*
40 * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263
41 * bitstream.
42 */
43static pj_uint8_t* find_sync_point(pj_uint8_t *data,
44 pj_size_t data_len)
45{
46 pj_uint8_t *p = data, *end = data+data_len;
47
48 while (p < end && *p && *(p+1))
49 ++p;
50
51 if (p == end)
52 return NULL;
53
54 return p;
55}
56
57
58/*
59 * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263
60 * bitstream, in reversed manner.
61 */
62static pj_uint8_t* find_sync_point_rev(pj_uint8_t *data,
63 pj_size_t data_len)
64{
65 pj_uint8_t *p = data+data_len-1;
66
67 while (p > data && *p && *(p+1))
68 --p;
69
70 if (p == data)
71 return (data + data_len);
72
73 return p;
74}
75
76
77/*
78 * Create H263 packetizer.
79 */
80PJ_DEF(pj_status_t) pjmedia_h263_packetizer_create(
81 pj_pool_t *pool,
82 const pjmedia_h263_packetizer_cfg *cfg,
83 pjmedia_h263_packetizer **p)
84{
85 pjmedia_h263_packetizer *p_;
86
87 PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
88
89 if (cfg && cfg->mode != PJMEDIA_H263_PACKETIZER_MODE_RFC4629)
90 return PJ_ENOTSUP;
91
92 p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h263_packetizer);
93 if (cfg) {
94 pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
95 } else {
96 p_->cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629;
97 p_->cfg.mtu = PJMEDIA_MAX_MTU;
98 }
99
100 *p = p_;
101
102 return PJ_SUCCESS;
103}
104
105
106/*
107 * Generate an RTP payload from H.263 frame bitstream, in-place processing.
108 */
109PJ_DEF(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz,
110 pj_uint8_t *bits,
111 pj_size_t bits_len,
112 unsigned *pos,
113 const pj_uint8_t **payload,
114 pj_size_t *payload_len)
115{
116 pj_uint8_t *p, *end;
117
118 pj_assert(pktz && bits && pos && payload && payload_len);
119 pj_assert(*pos <= bits_len);
120
121 p = bits + *pos;
122 end = bits + bits_len;
123
124 /* Put two octets payload header */
125 if ((end-p > 2) && *p==0 && *(p+1)==0) {
126 /* The bitstream starts with synchronization point, just override
127 * the two zero octets (sync point mark) for payload header.
128 */
129 *p = 0x04;
130 } else {
131 /* Not started in synchronization point, we will use two octets
132 * preceeding the bitstream for payload header!
133 */
134
135 if (*pos < 2) {
136 /* Invalid H263 bitstream, it's not started with PSC */
137 return PJ_EINVAL;
138 }
139
140 p -= 2;
141 *p = 0;
142 }
143 *(p+1) = 0;
144
145 /* When bitstream truncation needed because of payload length/MTU
146 * limitation, try to use sync point for the payload boundary.
147 */
148 if (end-p > pktz->cfg.mtu) {
149 end = find_sync_point_rev(p+2, pktz->cfg.mtu-2);
150 }
151
152 *payload = p;
153 *payload_len = end-p;
154 *pos = end - bits;
155
156 return PJ_SUCCESS;
157}
158
159
160/*
161 * Append an RTP payload to a H.263 picture bitstream.
162 */
163PJ_DEF(pj_status_t) pjmedia_h263_unpacketize (pjmedia_h263_packetizer *pktz,
164 const pj_uint8_t *payload,
165 pj_size_t payload_len,
166 pj_uint8_t *bits,
167 pj_size_t bits_size,
168 unsigned *pos)
169{
170 pj_uint8_t P, V, PLEN;
171 const pj_uint8_t *p = payload;
172 pj_uint8_t *q;
173
174 q = bits + *pos;
175
176 /* Check if this is a missing/lost packet */
177 if (payload == NULL) {
178 pktz->unpack_prev_lost = PJ_TRUE;
179 return PJ_SUCCESS;
180 }
181
182 /* H263 payload header size is two octets */
183 if (payload_len < 2) {
184 /* Invalid bitstream, discard this payload */
185 pktz->unpack_prev_lost = PJ_TRUE;
186 return PJ_EINVAL;
187 }
188
189 /* Get payload header info */
190 P = *p & 0x04;
191 V = *p & 0x02;
192 PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3);
193
194 /* Get start bitstream pointer */
195 p += 2; /* Skip payload header */
196 if (V)
197 p += 1; /* Skip VRC data */
198 if (PLEN)
199 p += PLEN; /* Skip extra picture header data */
200
201 /* Get bitstream length */
202 if (payload_len > (pj_size_t)(p - payload)) {
203 payload_len -= (p - payload);
204 } else {
205 /* Invalid bitstream, discard this payload */
206 pktz->unpack_prev_lost = PJ_TRUE;
207 return PJ_EINVAL;
208 }
209
210 /* Validate bitstream length */
211 if (bits_size < *pos + payload_len + 2) {
212 /* Insufficient bistream buffer, discard this payload */
213 pj_assert(!"Insufficient H.263 bitstream buffer");
214 pktz->unpack_prev_lost = PJ_TRUE;
215 return PJ_ETOOSMALL;
216 }
217
218 /* Start writing bitstream */
219
220 /* No sync point flag */
221 if (!P) {
222 if (*pos == 0) {
223 /* Previous packet must be lost */
224 pktz->unpack_prev_lost = PJ_TRUE;
225
226 /* If there is extra picture header, let's use it. */
227 if (PLEN) {
228 /* Write two zero octets for PSC */
229 *q++ = 0;
230 *q++ = 0;
231 /* Copy the picture header */
232 p -= PLEN;
233 pj_memcpy(q, p, PLEN);
234 p += PLEN;
235 q += PLEN;
236 }
237 } else if (pktz->unpack_prev_lost) {
238 /* If prev packet was lost, revert the bitstream pointer to
239 * the last sync point.
240 */
241 pj_assert(pktz->unpack_last_sync_pos <= *pos);
242 q = bits + pktz->unpack_last_sync_pos;
243 }
244
245 /* There was packet lost, see if this payload contain sync point
246 * (usable data).
247 */
248 if (pktz->unpack_prev_lost) {
249 pj_uint8_t *sync;
250 sync = find_sync_point((pj_uint8_t*)p, payload_len);
251 if (sync) {
252 /* Got sync point, update P/sync-point flag */
253 P = 1;
254 /* Skip the two zero octets */
255 sync += 2;
256 /* Update payload length and start bitstream pointer */
257 payload_len -= (sync - p);
258 p = sync;
259 } else {
260 /* No sync point in it, just discard this payload */
261 return PJ_EIGNORED;
262 }
263 }
264 }
265
266 /* Write two zero octets when payload flagged with sync point */
267 if (P) {
268 pktz->unpack_last_sync_pos = q - bits;
269 *q++ = 0;
270 *q++ = 0;
271 }
272
273 /* Write the payload to the bitstream */
274 pj_memcpy(q, p, payload_len);
275 q += payload_len;
276
277 /* Update the bitstream writing offset */
278 *pos = q - bits;
279
280 pktz->unpack_prev_lost = PJ_FALSE;
281
282 return PJ_SUCCESS;
283}