blob: 0e71604b13e137440910f07769e926fd88a2dcb0 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2011 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/h264_packetizer.h>
20#include <pjmedia/types.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/log.h>
24#include <pj/pool.h>
25#include <pj/string.h>
26
27
28#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
29
30
31#define THIS_FILE "h264_packetizer.c"
32
33#define DBG_PACKETIZE 0
34#define DBG_UNPACKETIZE 0
35
36
37/* H.264 packetizer definition */
38struct pjmedia_h264_packetizer
39{
40 /* Current settings */
41 pjmedia_h264_packetizer_cfg cfg;
42
43 /* Unpacketizer state */
44 unsigned unpack_last_sync_pos;
45 pj_bool_t unpack_prev_lost;
46};
47
48
49/* Enumeration of H.264 NAL unit types */
50enum
51{
52 NAL_TYPE_SINGLE_NAL_MIN = 1,
53 NAL_TYPE_SINGLE_NAL_MAX = 23,
54 NAL_TYPE_STAP_A = 24,
55 NAL_TYPE_FU_A = 28,
56};
57
58
59/*
60 * Find next NAL unit from the specified H.264 bitstream data.
61 */
62static pj_uint8_t* find_next_nal_unit(pj_uint8_t *start,
63 pj_uint8_t *end)
64{
65 pj_uint8_t *p = start;
66
67 /* Simply lookup "0x000001" pattern */
68 while (p <= end-3 && (p[0] || p[1] || p[2]!=1))
69 ++p;
70
71 if (p > end-3)
72 /* No more NAL unit in this bitstream */
73 return NULL;
74
75 /* Include 8 bits leading zero */
76 if (p>start && *(p-1)==0)
77 return (p-1);
78
79 return p;
80}
81
82
83/*
84 * Create H264 packetizer.
85 */
86PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create(
87 pj_pool_t *pool,
88 const pjmedia_h264_packetizer_cfg *cfg,
89 pjmedia_h264_packetizer **p)
90{
91 pjmedia_h264_packetizer *p_;
92
93 PJ_ASSERT_RETURN(pool && p, PJ_EINVAL);
94
95 if (cfg &&
96 cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED &&
97 cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
98 {
99 return PJ_ENOTSUP;
100 }
101
102 p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h264_packetizer);
103 if (cfg) {
104 pj_memcpy(&p_->cfg, cfg, sizeof(*cfg));
105 } else {
106 p_->cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
107 p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
108 }
109
110 *p = p_;
111
112 return PJ_SUCCESS;
113}
114
115
116
117/*
118 * Generate an RTP payload from H.264 frame bitstream, in-place processing.
119 */
120PJ_DEF(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz,
121 pj_uint8_t *buf,
122 pj_size_t buf_len,
123 unsigned *pos,
124 const pj_uint8_t **payload,
125 pj_size_t *payload_len)
126{
127 pj_uint8_t *nal_start = NULL, *nal_end = NULL, *nal_octet = NULL;
128 pj_uint8_t *p, *end;
129 enum {
130 HEADER_SIZE_FU_A = 2,
131 HEADER_SIZE_STAP_A = 3,
132 };
133 enum { MAX_NALS_IN_AGGR = 32 };
134
135#if DBG_PACKETIZE
136 if (*pos == 0 && buf_len) {
137 PJ_LOG(3, ("h264pack", "<< Start packing new frame >>"));
138 }
139#endif
140
141 p = buf + *pos;
142 end = buf + buf_len;
143
144 /* Find NAL unit startcode */
145 if (end-p >= 4)
146 nal_start = find_next_nal_unit(p, p+4);
147 if (nal_start) {
148 /* Get NAL unit octet pointer */
149 while (*nal_start++ == 0);
150 nal_octet = nal_start;
151 } else {
152 /* This NAL unit is being fragmented */
153 nal_start = p;
154 }
155
156 /* Get end of NAL unit */
157 p = nal_start+pktz->cfg.mtu+1;
158 if (p > end || pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL)
159 p = end;
160 nal_end = find_next_nal_unit(nal_start, p);
161 if (!nal_end)
162 nal_end = p;
163
164 /* Validate MTU vs NAL length on single NAL unit packetization */
165 if ((pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
166 nal_end - nal_start > pktz->cfg.mtu)
167 {
168 //pj_assert(!"MTU too small for H.264 single NAL packetization mode");
169 PJ_LOG(2,("h264_packetizer.c",
170 "MTU too small for H.264 (required=%u, MTU=%u)",
171 nal_end - nal_start, pktz->cfg.mtu));
172 return PJ_ETOOSMALL;
173 }
174
175 /* Evaluate the proper payload format structure */
176
177 /* Fragmentation (FU-A) packet */
178 if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
179 (!nal_octet || nal_end-nal_start > pktz->cfg.mtu))
180 {
181 pj_uint8_t NRI, TYPE;
182
183 if (nal_octet) {
184 /* We have NAL unit octet, so this is the first fragment */
185 NRI = (*nal_octet & 0x60) >> 5;
186 TYPE = *nal_octet & 0x1F;
187
188 /* Skip nal_octet in nal_start to be overriden by FU header */
189 ++nal_start;
190 } else {
191 /* Not the first fragment, get NRI and NAL unit type
192 * from the previous fragment.
193 */
194 p = nal_start - pktz->cfg.mtu;
195 NRI = (*p & 0x60) >> 5;
196 TYPE = *(p+1) & 0x1F;
197 }
198
199 /* Init FU indicator (one octet: F+NRI+TYPE) */
200 p = nal_start - HEADER_SIZE_FU_A;
201 *p = (NRI << 5) | NAL_TYPE_FU_A;
202 ++p;
203
204 /* Init FU header (one octed: S+E+R+TYPE) */
205 *p = TYPE;
206 if (nal_octet)
207 *p |= (1 << 7); /* S bit flag = start of fragmentation */
208 if (nal_end-nal_start+HEADER_SIZE_FU_A <= pktz->cfg.mtu)
209 *p |= (1 << 6); /* E bit flag = end of fragmentation */
210
211 /* Set payload, payload length */
212 *payload = nal_start - HEADER_SIZE_FU_A;
213 if (nal_end-nal_start+HEADER_SIZE_FU_A > pktz->cfg.mtu)
214 *payload_len = pktz->cfg.mtu;
215 else
216 *payload_len = nal_end - nal_start + HEADER_SIZE_FU_A;
217 *pos = (unsigned)(*payload + *payload_len - buf);
218
219#if DBG_PACKETIZE
220 PJ_LOG(3, ("h264pack", "Packetized fragmented H264 NAL unit "
221 "(pos=%d, type=%d, NRI=%d, S=%d, E=%d, len=%d/%d)",
222 *payload-buf, TYPE, NRI, *p>>7, (*p>>6)&1, *payload_len,
223 buf_len));
224#endif
225
226 return PJ_SUCCESS;
227 }
228
229 /* Aggregation (STAP-A) packet */
230 if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) &&
231 (nal_end != end) &&
232 (nal_end - nal_start + HEADER_SIZE_STAP_A) < pktz->cfg.mtu)
233 {
234 int total_size;
235 unsigned nal_cnt = 1;
236 pj_uint8_t *nal[MAX_NALS_IN_AGGR];
237 pj_size_t nal_size[MAX_NALS_IN_AGGR];
238 pj_uint8_t NRI;
239
240 pj_assert(nal_octet);
241
242 /* Init the first NAL unit in the packet */
243 nal[0] = nal_start;
244 nal_size[0] = nal_end - nal_start;
245 total_size = (int)nal_size[0] + HEADER_SIZE_STAP_A;
246 NRI = (*nal_octet & 0x60) >> 5;
247
248 /* Populate next NAL units */
249 while (nal_cnt < MAX_NALS_IN_AGGR) {
250 pj_uint8_t *tmp_end;
251
252 /* Find start address of the next NAL unit */
253 p = nal[nal_cnt-1] + nal_size[nal_cnt-1];
254 while (*p++ == 0);
255 nal[nal_cnt] = p;
256
257 /* Find end address of the next NAL unit */
258 tmp_end = p + (pktz->cfg.mtu - total_size);
259 if (tmp_end > end)
260 tmp_end = end;
261 p = find_next_nal_unit(p+1, tmp_end);
262 if (p) {
263 nal_size[nal_cnt] = p - nal[nal_cnt];
264 } else {
265 break;
266 }
267
268 /* Update total payload size (2 octet NAL size + NAL) */
269 total_size += (2 + (int)nal_size[nal_cnt]);
270 if (total_size <= pktz->cfg.mtu) {
271 pj_uint8_t tmp_nri;
272
273 /* Get maximum NRI of the aggregated NAL units */
274 tmp_nri = (*(nal[nal_cnt]-1) & 0x60) >> 5;
275 if (tmp_nri > NRI)
276 NRI = tmp_nri;
277 } else {
278 break;
279 }
280
281 ++nal_cnt;
282 }
283
284 /* Only use STAP-A when we found more than one NAL units */
285 if (nal_cnt > 1) {
286 unsigned i;
287
288 /* Init STAP-A NAL header (F+NRI+TYPE) */
289 p = nal[0] - HEADER_SIZE_STAP_A;
290 *p++ = (NRI << 5) | NAL_TYPE_STAP_A;
291
292 /* Append all populated NAL units into payload (SIZE+NAL) */
293 for (i = 0; i < nal_cnt; ++i) {
294 /* Put size (2 octets in network order) */
295 pj_assert(nal_size[i] <= 0xFFFF);
296 *p++ = (pj_uint8_t)(nal_size[i] >> 8);
297 *p++ = (pj_uint8_t)(nal_size[i] & 0xFF);
298
299 /* Append NAL unit, watchout memmove()-ing bitstream! */
300 if (p != nal[i])
301 pj_memmove(p, nal[i], nal_size[i]);
302 p += nal_size[i];
303 }
304
305 /* Set payload, payload length, and pos */
306 *payload = nal[0] - HEADER_SIZE_STAP_A;
307 pj_assert(*payload >= buf+*pos);
308 *payload_len = p - *payload;
309 *pos = (unsigned)(nal[nal_cnt-1] + nal_size[nal_cnt-1] - buf);
310
311#if DBG_PACKETIZE
312 PJ_LOG(3, ("h264pack", "Packetized aggregation of "
313 "%d H264 NAL units (pos=%d, NRI=%d len=%d/%d)",
314 nal_cnt, *payload-buf, NRI, *payload_len, buf_len));
315#endif
316
317 return PJ_SUCCESS;
318 }
319 }
320
321 /* Single NAL unit packet */
322 *payload = nal_start;
323 *payload_len = nal_end - nal_start;
324 *pos = (unsigned)(nal_end - buf);
325
326#if DBG_PACKETIZE
327 PJ_LOG(3, ("h264pack", "Packetized single H264 NAL unit "
328 "(pos=%d, type=%d, NRI=%d, len=%d/%d)",
329 nal_start-buf, *nal_octet&0x1F, (*nal_octet&0x60)>>5,
330 *payload_len, buf_len));
331#endif
332
333 return PJ_SUCCESS;
334}
335
336
337/*
338 * Append RTP payload to a H.264 picture bitstream. Note that the only
339 * payload format that cares about packet lost is the NAL unit
340 * fragmentation format (FU-A/B), so we will only manage the "prev_lost"
341 * state for the FU-A/B packets.
342 */
343PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz,
344 const pj_uint8_t *payload,
345 pj_size_t payload_len,
346 pj_uint8_t *bits,
347 pj_size_t bits_len,
348 unsigned *bits_pos)
349{
350 const pj_uint8_t nal_start_code[3] = {0, 0, 1};
351 enum { MIN_PAYLOAD_SIZE = 2 };
352 pj_uint8_t nal_type;
353
354 PJ_UNUSED_ARG(pktz);
355
356#if DBG_UNPACKETIZE
357 if (*bits_pos == 0 && payload_len) {
358 PJ_LOG(3, ("h264unpack", ">> Start unpacking new frame <<"));
359 }
360#endif
361
362 /* Check if this is a missing/lost packet */
363 if (payload == NULL) {
364 pktz->unpack_prev_lost = PJ_TRUE;
365 return PJ_SUCCESS;
366 }
367
368 /* H264 payload size */
369 if (payload_len < MIN_PAYLOAD_SIZE) {
370 /* Invalid bitstream, discard this payload */
371 pktz->unpack_prev_lost = PJ_TRUE;
372 return PJ_EINVAL;
373 }
374
375 /* Reset last sync point for every new picture bitstream */
376 if (*bits_pos == 0)
377 pktz->unpack_last_sync_pos = 0;
378
379 nal_type = *payload & 0x1F;
380 if (nal_type >= NAL_TYPE_SINGLE_NAL_MIN &&
381 nal_type <= NAL_TYPE_SINGLE_NAL_MAX)
382 {
383 /* Single NAL unit packet */
384 pj_uint8_t *p = bits + *bits_pos;
385
386 /* Validate bitstream length */
387 if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
388 /* Insufficient bistream buffer, discard this payload */
389 pj_assert(!"Insufficient H.263 bitstream buffer");
390 return PJ_ETOOSMALL;
391 }
392
393 /* Write NAL unit start code */
394 pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
395 p += PJ_ARRAY_SIZE(nal_start_code);
396
397 /* Write NAL unit */
398 pj_memcpy(p, payload, payload_len);
399 p += payload_len;
400
401 /* Update the bitstream writing offset */
402 *bits_pos = (unsigned)(p - bits);
403 pktz->unpack_last_sync_pos = *bits_pos;
404
405#if DBG_UNPACKETIZE
406 PJ_LOG(3, ("h264unpack", "Unpacked single H264 NAL unit "
407 "(type=%d, NRI=%d, len=%d)",
408 nal_type, (*payload&0x60)>>5, payload_len));
409#endif
410
411 }
412 else if (nal_type == NAL_TYPE_STAP_A)
413 {
414 /* Aggregation packet */
415 pj_uint8_t *p, *p_end;
416 const pj_uint8_t *q, *q_end;
417 unsigned cnt = 0;
418
419 /* Validate bitstream length */
420 if (bits_len - *bits_pos < payload_len + 32) {
421 /* Insufficient bistream buffer, discard this payload */
422 pj_assert(!"Insufficient H.263 bitstream buffer");
423 return PJ_ETOOSMALL;
424 }
425
426 /* Fill bitstream */
427 p = bits + *bits_pos;
428 p_end = bits + bits_len;
429 q = payload + 1;
430 q_end = payload + payload_len;
431 while (q < q_end && p < p_end) {
432 pj_uint16_t tmp_nal_size;
433
434 /* Write NAL unit start code */
435 pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
436 p += PJ_ARRAY_SIZE(nal_start_code);
437
438 /* Get NAL unit size */
439 tmp_nal_size = (*q << 8) | *(q+1);
440 q += 2;
441 if (q + tmp_nal_size > q_end) {
442 /* Invalid bitstream, discard the rest of the payload */
443 return PJ_EINVAL;
444 }
445
446 /* Write NAL unit */
447 pj_memcpy(p, q, tmp_nal_size);
448 p += tmp_nal_size;
449 q += tmp_nal_size;
450 ++cnt;
451
452 /* Update the bitstream writing offset */
453 *bits_pos = (unsigned)(p - bits);
454 pktz->unpack_last_sync_pos = *bits_pos;
455 }
456
457#if DBG_UNPACKETIZE
458 PJ_LOG(3, ("h264unpack", "Unpacked %d H264 NAL units (len=%d)",
459 cnt, payload_len));
460#endif
461
462 }
463 else if (nal_type == NAL_TYPE_FU_A)
464 {
465 /* Fragmentation packet */
466 pj_uint8_t *p;
467 const pj_uint8_t *q = payload;
468 pj_uint8_t NRI, TYPE, S, E;
469
470 p = bits + *bits_pos;
471
472 /* Validate bitstream length */
473 if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) {
474 /* Insufficient bistream buffer, drop this packet */
475 pj_assert(!"Insufficient H.263 bitstream buffer");
476 pktz->unpack_prev_lost = PJ_TRUE;
477 return PJ_ETOOSMALL;
478 }
479
480 /* Get info */
481 S = *(q+1) & 0x80; /* Start bit flag */
482 E = *(q+1) & 0x40; /* End bit flag */
483 TYPE = *(q+1) & 0x1f;
484 NRI = (*q & 0x60) >> 5;
485
486 /* Fill bitstream */
487 if (S) {
488 /* This is the first part, write NAL unit start code */
489 pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code));
490 p += PJ_ARRAY_SIZE(nal_start_code);
491
492 /* Write NAL unit octet */
493 *p++ = (NRI << 5) | TYPE;
494 } else if (pktz->unpack_prev_lost) {
495 /* If prev packet was lost, revert the bitstream pointer to
496 * the last sync point.
497 */
498 pj_assert(pktz->unpack_last_sync_pos <= *bits_pos);
499 *bits_pos = pktz->unpack_last_sync_pos;
500 /* And discard this payload (and the following fragmentation
501 * payloads carrying this same NAL unit.
502 */
503 return PJ_EIGNORED;
504 }
505 q += 2;
506
507 /* Write NAL unit */
508 pj_memcpy(p, q, payload_len - 2);
509 p += (payload_len - 2);
510
511 /* Update the bitstream writing offset */
512 *bits_pos = (unsigned)(p - bits);
513 if (E) {
514 /* Update the sync pos only if the end bit flag is set */
515 pktz->unpack_last_sync_pos = *bits_pos;
516 }
517
518#if DBG_UNPACKETIZE
519 PJ_LOG(3, ("h264unpack", "Unpacked fragmented H264 NAL unit "
520 "(type=%d, NRI=%d, len=%d)",
521 TYPE, NRI, payload_len));
522#endif
523
524 } else {
525 *bits_pos = 0;
526 return PJ_ENOTSUP;
527 }
528
529 pktz->unpack_prev_lost = PJ_FALSE;
530
531 return PJ_SUCCESS;
532}
533
534
535#endif /* PJMEDIA_HAS_VIDEO */