root/vic/branches/mpeg4/codec/rtp_h264_depayloader.cpp

Revision 4400, 19.0 KB (checked in by piers, 5 years ago)

Fixed IOCOM's proprietary H.264 decoding - Previously it was checking for a predefined byte start sequence but this wasn't always present. It has been changed to use the RTP payload type identifier (107 for IOCOM_H.264 or 96 for RFC3984 H.264) which is a better approach.

We should probably change VIC to use ffmpeg's AVPacket structures as well and we could probably eliminate some packet copying which would speed things up - but it seems to work pretty well at resolutions up to 960x720 (from IOCOM)

Line 
1/*
2 * RTP H264 Protocol (RFC3984)
3 * Copyright (c) 2006 Ryan Martell.
4 * Modified by Socrates VaraKliotis and Piers O'Hanlon (c) 2008
5 * - Created H264Depayloader class out of the original C calls
6 * - Added custom handling for IOCOM H.264
7 *
8 * This file was part of FFmpeg.
9 *
10 * FFmpeg is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * FFmpeg is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with FFmpeg; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25/**
26* @file rtp_h264.c
27 * @brief H.264 / RTP Code (RFC3984)
28 * @author Ryan Martell <rdm4@martellventures.com>
29 *
30 * @note Notes:
31 * Notes:
32 * This currently supports packetization mode:
33 * Single Nal Unit Mode (0), or
34 * Non-Interleaved Mode (1).  It currently does not support
35 * Interleaved Mode (2). (This requires implementing STAP-B, MTAP16, MTAP24, FU-B packet types)
36 *
37 * @note TODO:
38 * 1) RTCP sender reports for udp streams are required..
39 *
40 */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <assert.h>
46#include "config.h"
47
48#ifndef WIN32
49#include <unistd.h>
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include <netdb.h>
55#endif
56#include <assert.h>
57
58#include "rtp_h264_depayloader.h"
59#include "packetbuffer.h"
60#include "rtp.h"
61
62//using namespace std;
63
64/**
65 * Return TRUE if val is a prefix of str. If it returns TRUE, ptr is
66 * set to the next character in 'str' after the prefix.
67 *
68 * @param str input string
69 * @param val prefix to test
70 * @param ptr updated after the prefix in str in there is a match
71 * @return TRUE if there is a match
72 */
73int strstart(const char *str, const char *val, const char **ptr)
74{
75    const char *p, *q;
76    p = str;
77    q = val;
78    while (*q != '\0') {
79        if (*p != *q)
80            return 0;
81        p++;
82        q++;
83    }
84    if (ptr)
85        *ptr = p;
86    return 1;
87}
88
89
90//helpers from libavformat/utils.c =============================================
91/**
92 * Default packet destructor.
93 */
94/*void av_destruct_packet(AVPacket *pkt)
95{
96    av_free(pkt->data);
97    pkt->data = NULL; pkt->size = 0;
98}
99*/
100
101/**
102 * Allocate the payload of a packet and intialized its fields to default values.
103 *
104 * @param pkt packet
105 * @param size wanted payload size
106 * @return 0 if OK. AVERROR_xxx otherwise.
107 */
108/*
109int av_new_packet(AVPacket *pkt, int size)
110{
111    uint8_t *data;
112    if((unsigned)size > (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
113        return AVERROR_NOMEM;
114    data = (uint8_t *) av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
115    if (!data)
116        return AVERROR_NOMEM;
117    memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
118
119    av_init_packet(pkt);
120    pkt->data = data;
121    pkt->size = size;
122    pkt->destruct = av_destruct_packet;
123    return 0;
124}
125*/
126
127//helpers from rtsp.c =============================================
128static int redir_isspace(int c)
129{
130    return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
131}
132
133static void skip_spaces(const char **pp)
134{
135    const char *p;
136    p = *pp;
137    while (redir_isspace(*p))
138        p++;
139    *pp = p;
140}
141
142static void get_word_sep(char *buf, int buf_size, const char *sep,
143                         const char **pp)
144{
145    const char *p;
146    char *q;
147
148    p = *pp;
149    if (*p == '/')
150        p++;
151    skip_spaces(&p);
152    q = buf;
153    while (!strchr(sep, *p) && *p != '\0') {
154        if ((q - buf) < buf_size - 1)
155            *q++ = *p;
156        p++;
157    }
158    if (buf_size > 0)
159        *q = '\0';
160    *pp = p;
161}
162//===================================================================
163
164/** parse the attribute line from the fmtp a line of an sdp resonse.  This is broken out as a function
165* because it is used in rtp_h264.c, which is forthcoming.
166*/
167int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size)
168{
169    skip_spaces(p);
170    if(**p)
171    {
172        get_word_sep(attr, attr_size, "=", p);
173        if (**p == '=')
174            (*p)++;
175        get_word_sep(value, value_size, ";", p);
176        if (**p == ';')
177            (*p)++;
178        return 1;
179    }
180    return 0;
181}
182
183
184
185
186
187H264Depayloader::H264Depayloader()
188{
189    h264_extradata = (h264_rtp_extra_data *) h264_new_extradata();
190    //fprintf(stderr, "H264_RTP: H264Depayloader Constructor done.\n");
191    aggregate_pkt = 0;
192}
193
194H264Depayloader::~H264Depayloader()
195{
196    h264_free_extradata(h264_extradata);
197    //fprintf(stderr, "H264_RTP: H264Depayloader Destructor done.\n");
198}
199
200
201
202
203/* ---------------- private code */
204void H264Depayloader::sdp_parse_fmtp_config_h264(AVCodecContext *codec, /*AVStream * stream,*/
205                                       h264_rtp_extra_data * h264_data,
206                                       char *attr, char *value)
207{
208    //AVCodecContext *codec = stream->codec;
209    assert(codec->codec_id == CODEC_ID_H264);
210    assert(h264_data != NULL);
211
212    if (!strcmp(attr, "packetization-mode")) {
213        //fprintf(stderr, /*NULL, AV_LOG_DEBUG,*/ "H.264/RTP Packetization Mode: %d\n", atoi(value));
214        h264_data->packetization_mode = atoi(value);
215        /*
216           Packetization Mode:
217           0 or not present: Single NAL mode (Only nals from 1-23 are allowed)
218           1: Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed.
219           2: Interleaved Mode: 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed.
220         */
221        if (h264_data->packetization_mode > 1)
222            fprintf(stderr, /*stream, AV_LOG_ERROR,*/
223                   "H.264/RTP: Interleaved RTP mode is not supported yet.");
224    } else if (!strcmp(attr, "profile-level-id")) {
225        if (strlen(value) == 6) {
226            char buffer[3];
227            // 6 characters=3 bytes, in hex.
228            uint8_t profile_idc;
229            uint8_t profile_iop;
230            uint8_t level_idc;
231
232            buffer[0] = value[0]; buffer[1] = value[1]; buffer[2] = '\0';
233            profile_idc = (uint8_t)strtol(buffer, NULL, 16);
234            buffer[0] = value[2]; buffer[1] = value[3];
235            profile_iop = (uint8_t)strtol(buffer, NULL, 16);
236            buffer[0] = value[4]; buffer[1] = value[5];
237            level_idc = (uint8_t)strtol(buffer, NULL, 16);
238
239            // set the parameters...
240            //fprintf(stderr, /* NULL, AV_LOG_DEBUG,*/ "H.264/RTP Profile IDC: %x Profile IOP: %x Level: %x\n", profile_idc, profile_iop, level_idc);
241            h264_data->profile_idc = profile_idc;
242            h264_data->profile_iop = profile_iop;
243            h264_data->level_idc = level_idc;
244        }
245    } else  if (!strcmp(attr, "sprop-parameter-sets")) {
246        uint8_t start_sequence[]= { 0, 0, 1 };
247        codec->extradata_size= 0;
248        codec->extradata= NULL;
249
250        while (*value) {
251            char base64packet[1024];
252            uint8_t decoded_packet[1024];
253            uint32_t packet_size;
254            char *dst = base64packet;
255
256            while (*value && *value != ','
257                   && (dst - base64packet) < sizeof(base64packet) - 1) {
258                *dst++ = *value++;
259            }
260            *dst++ = '\0';
261
262            if (*value == ',')
263                value++;
264
265            packet_size=av_base64_decode(decoded_packet, base64packet, sizeof(decoded_packet));
266            if (packet_size) {
267                uint8_t *dest= (uint8_t *) av_malloc(packet_size+sizeof(start_sequence)+codec->extradata_size);
268                if(dest)
269                {
270                    if(codec->extradata_size)
271                    {
272                        // av_realloc?
273                        memcpy(dest, codec->extradata, codec->extradata_size);
274                        av_free(codec->extradata);
275                    }
276
277                    memcpy(dest+codec->extradata_size, start_sequence, sizeof(start_sequence));
278                    memcpy(dest+codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
279
280                    codec->extradata= dest;
281                    codec->extradata_size+= sizeof(start_sequence)+packet_size;
282                } else {
283                    fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP Unable to allocate memory for extradata!");
284                }
285            }
286        }
287        //fprintf(stderr, /*NULL, AV_LOG_DEBUG,*/ "H.264/RTP Extradata set to %p (size: %d)!\n", codec->extradata, codec->extradata_size);
288    }
289}
290
291// return 0 on packet, no more left, 1 on packet, 1 on partial packet...
292int H264Depayloader::h264_handle_packet(h264_rtp_extra_data *data,
293                              int pktIdx, PacketBuffer *pb,
294                              const uint8_t * buf, int len)
295{
296    //h264_rtp_extra_data *data = s->dynamic_protocol_context;
297    uint8_t nal = buf[0];
298    uint8_t type = (nal & 0x1f);
299    int result= 0;
300    char start_sequence[]= {0, 0, 1};
301    int sslen = sizeof(start_sequence);
302
303    assert(data);
304    assert(data->cookie == MAGIC_COOKIE);
305    assert(buf);
306
307    //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: Single NAL type (%d, equiv. to >=1 or <=23), len=%4d\n", type, len);
308
309    if (type >= 1 && type <= 23)
310        type = 1;             
311    // simplify the case. (these are all the nal types used
312    // internally by the h264 codec)
313   
314    switch (type) {
315    case 0:                    // undefined;
316        fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Undefined NAL type (%d)\n", type);
317        result= -1;
318        break;
319
320    case 1:
321
322        //av_new_packet(pkt, len+sizeof(start_sequence));
323        //memcpy(pkt->data, start_sequence, sizeof(start_sequence));
324        //memcpy(pkt->data+sizeof(start_sequence), buf, len);
325
326        //SV: XXX
327/*
328        temp = (char *) malloc(sslen+len);
329        assert(temp);
330        memcpy(temp, start_sequence, sslen);
331        memcpy(temp+sslen, buf, len);
332
333        pb->write(pktIdx, sslen+len, temp); //SV: XXX
334*/     
335
336          pb->writeAppend(pktIdx, sslen, start_sequence); //PO: XXX
337          pb->writeAppend(pktIdx, len, (char *)buf); //PO: XXX
338
339#ifdef DEBUG
340        data->packet_types_received[nal & 0x1f]++;
341#endif
342        break;
343
344    case 24:                   // STAP-A (one packet, multiple nals)
345        // consume the STAP-A NAL
346        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: STAP-A NAL type (%d)\n", type);
347
348        buf++;
349        len--;
350        // first we are going to figure out the total size....
351        {
352            int pass= 0;
353            int total_length= 0;
354            uint8_t *dst= NULL;
355
356            for(pass= 0; pass<2; pass++) {
357                const uint8_t *src= buf;
358                int src_len= len;
359
360                do {
361                    uint16_t nal_size = BE_16(src); // this going to be a problem if unaligned (can it be?)
362
363                    // consume the length of the aggregate...
364                    src += 2;
365                    src_len -= 2;
366
367                    if (nal_size <= src_len) {
368                        if(pass==0) {
369                            // counting...
370                            total_length+= sizeof(start_sequence)+nal_size;
371                        } else {
372                            // copying
373                            assert(dst);
374                            memcpy(dst, start_sequence, sizeof(start_sequence));
375                            dst+= sizeof(start_sequence);
376                            memcpy(dst, src, nal_size);
377#ifdef DEBUG
378                            data->packet_types_received[*src & 0x1f]++;
379#endif
380                            dst+= nal_size;
381                        }
382                    } else {
383                        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: nal size exceeds length: %d %d\n", nal_size, src_len);
384                    }
385
386                    // eat what we handled...
387                    src += nal_size;
388                    src_len -= nal_size;
389
390                    if (src_len < 0) {
391                        ;
392                        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: Consumed more bytes than we got! (%d)\n", src_len);
393                   }
394                } while (src_len > 2);      // because there could be rtp padding..
395
396                if(pass==0) {
397                    // now we know the total size of the packet (with the start sequences added)
398
399                    //SV: XXX
400                    //av_new_packet(pkt, total_length);
401                    //dst= pkt->data;
402
403                    //temp = (char *) malloc(total_length);
404                    pb->setSize(pktIdx,total_length); //PO:
405                    dst = (uint8_t *) pb->packets[pktIdx]->data;
406
407                } else {
408                    //SV: XXX
409                    //assert(dst-pkt->data==total_length);
410                    assert(dst-(uint8_t *)(pb->packets[pktIdx]->data) == total_length);
411                }
412            }
413        }
414        break;
415
416    case 25:                   // STAP-B
417    case 26:                   // MTAP-16
418    case 27:                   // MTAP-24
419    case 29:                   // FU-B
420        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Unhandled type (%d) (See RFC for implementation details\n", type);
421        result= -1;
422        break;
423
424    case 28:                   // FU-A (fragmented nal)
425
426        buf++;
427        len--;                  // skip the fu_indicator
428        {
429            // these are the same as above, we just redo them here for clarity...
430            uint8_t fu_indicator = nal;
431            uint8_t fu_header = *buf;   // read the fu_header.
432            uint8_t start_bit = (fu_header & 0x80) >> 7;
433            // non used uint8_t end_bit = (fu_header & 0x40) >> 6;
434            uint8_t nal_type = (fu_header & 0x1f);
435            uint8_t reconstructed_nal;
436
437            // reconstruct this packet's true nal; only the data follows..
438            reconstructed_nal = fu_indicator & (0xe0);  // the original nal forbidden bit and NRI are stored in this packet's nal;
439            reconstructed_nal |= (nal_type & 0x1f);
440
441            // skip the fu_header...
442            buf++;
443            len--;
444
445#ifdef DEBUG
446            if (start_bit)
447                data->packet_types_received[nal_type & 0x1f]++;
448#endif
449            if(start_bit) {
450                // copy in the start sequence, and the reconstructed nal....
451
452                //SV: XXX
453                //av_new_packet(pkt, sizeof(start_sequence)+sizeof(nal)+len);
454                //memcpy(pkt->data, start_sequence, sizeof(start_sequence));
455                //pkt->data[sizeof(start_sequence)]= reconstructed_nal;
456                //memcpy(pkt->data+sizeof(start_sequence)+sizeof(nal), buf, len);
457/*
458                //SV: XXX
459                temp = (char *) malloc(sizeof(start_sequence)+sizeof(nal)+len);
460                memcpy(temp, start_sequence, sizeof(start_sequence));
461                temp[sizeof(start_sequence)] = reconstructed_nal;
462                memcpy(temp+sizeof(start_sequence)+sizeof(nal), buf, len);
463
464                pb->write(pktIdx, len+sizeof(start_sequence)+sizeof(nal), temp); //SV: XXX
465*/
466        pb->writeAppend(pktIdx, sizeof(start_sequence), start_sequence); //PO: XXX
467        pb->writeAppend(pktIdx, sizeof(nal), (char*)&reconstructed_nal); //PO: XXX
468        pb->writeAppend(pktIdx, len, (char *)buf); //PO: XXX
469
470            } else {
471                //SV: XXX
472                //av_new_packet(pkt, len);
473                //memcpy(pkt->data, buf, len);
474
475                //temp = (char *) malloc(len);
476                //memcpy(temp, buf, len);
477
478                pb->write(pktIdx, len, (char*)buf); //PO: XXX
479            }
480
481            //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: FU-A NAL type (%d): start_bit=%d, end_bit=%d, NAL_Header=0x%02x, FU_Header=0x%02x\n", type, start_bit, end_bit, reconstructed_nal, fu_header);
482
483        }
484        break;
485
486
487    case 30:                   // undefined
488    case 31:                   // undefined
489    default:
490        fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Undefined NAL type (%d)\n", type);
491        result= -1;
492        break;
493    }
494
495    return result;
496}
497
498/* ---------------- public code */
499void *H264Depayloader::h264_new_extradata()
500{
501    h264_rtp_extra_data *data = (h264_rtp_extra_data *) av_mallocz(sizeof(h264_rtp_extra_data) +
502                   FF_INPUT_BUFFER_PADDING_SIZE);
503
504    if (data) {
505        data->cookie = MAGIC_COOKIE;
506    }
507
508    return data;
509}
510
511void H264Depayloader::h264_free_extradata(void *d)
512{
513    h264_rtp_extra_data *data = (h264_rtp_extra_data *) d;
514#ifdef DEBUG
515    int ii;
516
517    for (ii = 0; ii < 32; ii++) {
518        if (data->packet_types_received[ii])
519            debug_msg(/*NULL, AV_LOG_DEBUG,*/ "Received %d packets of type %d\n",
520                   data->packet_types_received[ii], ii);
521    }
522#endif
523
524    assert(data);
525    assert(data->cookie == MAGIC_COOKIE);
526
527    // avoid stale pointers (assert)
528    data->cookie = DEAD_COOKIE;
529
530    // and clear out this...
531    av_free(data);
532}
533
534int H264Depayloader::parse_h264_sdp_line(AVCodecContext *codec, /*AVStream * stream,*/ void *data,
535                               const char *line)
536{
537    //AVCodecContext *codec = stream->codec;
538    h264_rtp_extra_data *h264_data = (h264_rtp_extra_data *) data;
539    const char *p = line;
540
541    assert(h264_data->cookie == MAGIC_COOKIE);
542
543    if (strstart(p, "a=framesize:", &p)) {
544        char buf1[50];
545        char *dst = buf1;
546
547        // remove the protocol identifier..
548        while (*p && *p == ' ') p++; // strip spaces.
549        while (*p && *p != ' ') p++; // eat protocol identifier
550        while (*p && *p == ' ') p++; // strip trailing spaces.
551        while (*p && *p != '-' && (buf1 - dst) < sizeof(buf1) - 1) {
552            *dst++ = *p++;
553        }
554        *dst = '\0';
555
556        // a='framesize:96 320-240'
557        // set our parameters..
558        codec->width = atoi(buf1);
559        codec->height = atoi(p + 1); // skip the -
560        codec->pix_fmt = PIX_FMT_YUV420P;
561    } else if (strstart(p, "a=fmtp:", &p)) { //SV: wtf!!!
562        char attr[256];
563        char value[4096];
564
565        // remove the protocol identifier..
566        while (*p && *p == ' ') p++; // strip spaces.
567        while (*p && *p != ' ') p++; // eat protocol identifier
568        while (*p && *p == ' ') p++; // strip trailing spaces.
569
570        /* loop on each attribute */
571        while (rtsp_next_attr_and_value
572               (&p, attr, sizeof(attr), value, sizeof(value))) {
573            /* grab the codec extra_data from the config parameter of the fmtp line */
574            //fprintf(stderr, "==========> attr=%s, value=%s \n", attr, value);
575            sdp_parse_fmtp_config_h264(codec, /* stream, */ h264_data, attr, value);
576        }
577    } else if (strstart(p, "cliprect:", &p)) {
578        // could use this if we wanted.
579    }
580
581    //av_set_pts_info(stream, 33, 1, 90000);      // 33 should be right, because the pts is 64 bit? (done elsewhere; this is a one time thing)
582
583    return 0;                   // keep processing it the normal way...
584}
585
586/**
587This is the structure for expanding on the dynamic rtp protocols (makes everything static. yay!)
588*/
589/*
590RTPDynamicProtocolHandler ff_h264_dynamic_handler = {
591    "H264",
592    CODEC_TYPE_VIDEO,
593    CODEC_ID_H264,
594    parse_h264_sdp_line,
595    h264_new_extradata,
596    h264_free_extradata,
597    h264_handle_packet
598};
599*/
Note: See TracBrowser for help on using the browser.