rtp_h264.c

Go to the documentation of this file.
00001 /* *
00002  * This file is part of libnemesi
00003  *
00004  * Copyright (C) 2007 by LScube team <team@streaming.polito.it>
00005  * See AUTHORS for more details
00006  *
00007  * libnemesi is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * libnemesi is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with libnemesi; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  *
00021  * */
00022 
00023 #include "rtpparser.h"
00024 #include "rtp_utils.h"
00025 #include <math.h>
00026 
00037 typedef struct {
00038     uint8_t *data;     
00039     long len;          
00040     long data_size;    
00041     unsigned long timestamp;    
00042     uint8_t *conf;
00043     long conf_len;
00044     int configured;
00045 } rtp_h264;
00046 
00047 static const rtpparser_info h264_served = {
00048     -1,
00049     {"H264", NULL}
00050 };
00051 
00052 static int h264_init_parser(rtp_session * rtp_sess, unsigned pt)
00053 {
00054     rtp_h264 *priv = calloc(1, sizeof(rtp_h264));
00055     rtp_pt_attrs *attrs = &rtp_sess->ptdefs[pt]->attrs;
00056     char value[1024];
00057     int i;
00058     int len;
00059 
00060     if (!priv) return RTP_ERRALLOC;
00061 
00062     for (i=0; i < attrs->size; i++) {
00063 
00064         if ((len = nms_get_attr_value(attrs->data[i], "profile-level-id",
00065             value, sizeof(value)))) {
00066             if (len==6){ /*hex string*/}
00067         }
00068         if ((len = nms_get_attr_value(attrs->data[i], "packetization-mode",
00069             value, sizeof(value)))) {
00070             // We do not support anything else.
00071             if (len != 1 || atoi(value) >= 2) {
00072                 nms_printf(NMSML_ERR,
00073                            "Unsupported H.264 packetization mode %s\n", value);
00074                 return RTP_PARSE_ERROR;
00075             }
00076         }
00077         if ((len = nms_get_attr_value(attrs->data[i], "sprop-parameter-sets",
00078             value, sizeof(value)))) {
00079         //shamelessly ripped from ffmpeg
00080             uint8_t start_seq[4] = {0, 0, 0, 1};
00081             char *v = value;
00082             priv->conf_len = 0;
00083             priv->conf = NULL;
00084             while (*v) {
00085                 char base64packet[1024];
00086                 uint8_t decoded_packet[1024];
00087                 unsigned packet_size;
00088                 char *dst = base64packet;
00089 
00090                 while (*v && *v != ','
00091                        && (dst - base64packet) < sizeof(base64packet) - 1) {
00092                     *dst++ = *v++;
00093                 }
00094                 *dst++ = '\0';
00095 
00096                 if (*v == ',')
00097                     v++;
00098 
00099                 packet_size = nms_base64_decode(decoded_packet,
00100                                                 base64packet,
00101                                                 sizeof(decoded_packet));
00102                 if (packet_size) {
00103                     uint8_t *dest = calloc(1, packet_size +
00104                                            sizeof(start_seq) +
00105                                            priv->conf_len);
00106                     if(dest) {
00107                         if(priv->conf_len) {
00108                             memcpy(dest, priv->conf, priv->conf_len);
00109                             free(priv->conf);
00110                         }
00111 
00112                         memcpy(dest+priv->conf_len, start_seq,
00113                                sizeof(start_seq));
00114                         memcpy(dest + priv->conf_len +  sizeof(start_seq),
00115                                decoded_packet, packet_size);
00116 
00117                         priv->conf = dest;
00118                         priv->conf_len += sizeof(start_seq) + packet_size;
00119                     } else {
00120                        goto err_alloc;
00121                     }
00122                 }
00123             }
00124         }
00125     }
00126 
00127     rtp_sess->ptdefs[pt]->priv = priv;
00128 
00129     return 0;
00130 
00131     err_alloc:
00132     free(priv);
00133     return RTP_ERRALLOC;
00134 }
00135 
00136 static int h264_uninit_parser(rtp_ssrc * ssrc, unsigned pt)
00137 {
00138     rtp_h264 *priv = ssrc->rtp_sess->ptdefs[pt]->priv;
00139 
00140     if (priv && priv->data)
00141         free(priv->data);
00142     if (priv && priv->conf)
00143         free(priv->conf);
00144     if (priv)
00145         free(priv);
00146 
00147     ssrc->rtp_sess->ptdefs[pt]->priv = NULL;
00148 
00149     return 0;
00150 }
00151 
00157 static int h264_parse(rtp_ssrc * ssrc, rtp_frame * fr, rtp_buff * config)
00158 {
00159     rtp_pkt *pkt;
00160     size_t len;
00161     rtp_h264 *priv = ssrc->rtp_sess->ptdefs[fr->pt]->priv;
00162     uint8_t *buf;
00163     uint8_t type;
00164     uint8_t start_seq[4] = {0, 0, 0, 1};
00165     int err = RTP_FILL_OK;
00166 
00167     if (!(pkt = rtp_get_pkt(ssrc, &len)))
00168         return RTP_BUFF_EMPTY;
00169 
00170     buf = RTP_PKT_DATA(pkt);
00171     len = RTP_PAYLOAD_SIZE(pkt, len);
00172     type = (buf[0] & 0x1f);
00173 
00174     // In order to produce a compliant bitstream, a PPS NALU should prefix
00175     // the data stream.
00176     if (!priv->configured && priv->conf_len) {
00177         if(nms_alloc_data(&priv->data, &priv->data_size, priv->conf_len)) {
00178             return RTP_ERRALLOC;
00179         }
00180         nms_append_incr(priv->data, &priv->len, priv->conf, priv->conf_len);
00181         priv->configured = 1;
00182     }
00183 
00184     if (priv->conf_len) {
00185         config->data = priv->conf;
00186         config->len = priv->conf_len;
00187     }
00188 
00189     if (type >= 1 && type <= 23) type = 1; // single packet
00190 
00191     switch (type) {
00192     case 0: // undefined;
00193         err = RTP_PKT_UNKNOWN;
00194         break;
00195     case 1:
00196         if(nms_alloc_data(&priv->data, &priv->data_size,
00197                           len + sizeof(start_seq) + priv->len)) {
00198             return RTP_ERRALLOC;
00199         }
00200         nms_append_incr(priv->data, &priv->len, start_seq, sizeof(start_seq));
00201         nms_append_incr(priv->data, &priv->len, buf, len);
00202         fr->data = priv->data;
00203         fr->len = priv->len;
00204         priv->len = 0;
00205         break;
00206     case 24:    // STAP-A (aggregate, output as whole or split it?)
00207         #if 0
00208         {
00209             size_t frame_len;
00210             buf += priv->index + 1; // skip the nal
00211             frame_len = nms_consume_BE2(&buf);
00212             if (!frame_len) {
00213                 nms_printf (NMSML_WARN,"Empty frame\n");
00214                 err = RTP_PARSE_ERROR;
00215                 break;
00216             }
00217 
00218             nms_printf(NMSML_WARN, "NAL: %d", *buf & 0x1f);
00219 
00220             if (frame_len + priv->index < len) {
00221                 nms_printf (NMSML_WARN,"Packet size %d\n",frame_len);
00222 
00223                 priv->data = fr->data = realloc(priv->data,
00224                                                 sizeof(start_seq)
00225                                                 + frame_len);
00226                 memcpy(fr->data, &start_seq, sizeof(start_seq));
00227                 memcpy(fr->data + sizeof(start_seq), buf, frame_len);
00228                 fr->len = sizeof(start_seq) + frame_len;
00229                 priv->index += 2 + frame_len;
00230                 if ( priv->index + 1 < len) return err; // more to output
00231                 else priv->index = 0; // get a new packet
00232             } else {
00233                 nms_printf (NMSML_ERR,"STAP-A corrupted\n");
00234                 err = RTP_PARSE_ERROR;
00235             }
00236         }
00237         break;
00238         #else
00239         // trim stap-a nalu header
00240         buf++;
00241         len--;
00242         // ffmpeg way
00243         {
00244             int pass = 0;
00245             int total_length = 0;
00246 
00247             for(pass= 0; pass<2; pass++) {
00248                 uint8_t *src = buf;
00249                 int src_len = len;
00250                 do {
00251                     uint16_t nal_size = nms_consume_BE2(&src);
00252                     src_len -= 2;
00253 
00254                     if (nal_size <= src_len) {
00255                         if(pass==0) {
00256                             total_length += sizeof(start_seq) + nal_size;
00257                         } else {
00258                             nms_append_incr(priv->data, &priv->len, start_seq,
00259                                             sizeof(start_seq));
00260                             nms_append_incr(priv->data, &priv->len, src,
00261                                             nal_size);
00262                         }
00263                     } else {
00264                         nms_printf(NMSML_ERR,
00265                                    "nal size exceeds length: %d %d\n",
00266                                    nal_size, src_len);
00267                     }
00268 
00269                     src += nal_size;
00270                     src_len -= nal_size;
00271 
00272                     if (src_len < 0)
00273                         nms_printf(NMSML_ERR,
00274                                    "Consumed more bytes than we got! (%d)\n",
00275                                    src_len);
00276                 } while (src_len > 2);  // because there could be rtp padding..
00277 
00278                 if(pass==0) {
00279                     if(nms_alloc_data(&priv->data, &priv->data_size,
00280                                       total_length + priv->len)) {
00281                         return RTP_ERRALLOC;
00282                     }
00283                 }
00284             }
00285         }
00286         fr->data = priv->data;
00287         fr->len = priv->len;
00288         priv->len = 0;
00289         break;
00290         #endif
00291         /* Unsupported for now */
00292     case 25:    // STAP-B
00293         err = RTP_PKT_UNKNOWN;
00294         nms_printf (NMSML_WARN,"STAP-B\n");
00295         break;
00296     case 26:    // MTAP-16
00297         err = RTP_PKT_UNKNOWN;
00298         nms_printf (NMSML_WARN,"MTAP-16\n");
00299         break;
00300     case 27:    // MTAP-24
00301         nms_printf (NMSML_WARN,"MTAP-24\n");
00302         err = RTP_PKT_UNKNOWN;
00303         break;
00304 
00305     case 28:    // FU-A (fragmented nal, output frags or aggregate it)
00306         {
00307             uint8_t fu_indicator = nms_consume_1(&buf);  // read the fu_indicator
00308             uint8_t fu_header    = nms_consume_1(&buf);  // read the fu_header.
00309             uint8_t start_bit    = (fu_header & 0x80) >> 7;
00310             uint8_t end_bit      = (fu_header & 0x40) >> 6;
00311             uint8_t nal_type     = (fu_header & 0x1f);
00312             uint8_t reconstructed_nal;
00313 
00314             len -= 2; // skip fu indicator and header
00315 
00316             // reconstruct this packet's true nal; only the data follows..
00317             // the original nal forbidden bit and NRI are stored in
00318             // this packet's nal;
00319             reconstructed_nal = fu_indicator & 0xe0;
00320             reconstructed_nal |= nal_type;
00321 
00322             if(start_bit && !priv->len) {
00323                 if(nms_alloc_data(&priv->data, &priv->data_size,
00324                                   len + 1 + sizeof(start_seq) + priv->len)) {
00325                     return RTP_ERRALLOC;
00326                 }
00327                 // copy in the start sequence, and the reconstructed nal....
00328                 nms_append_incr(priv->data, &priv->len, start_seq,
00329                                 sizeof(start_seq));
00330                 nms_append_incr(priv->data, &priv->len, &reconstructed_nal, 1);
00331                 nms_append_incr(priv->data, &priv->len, buf, len);
00332             } else {
00333                 if (priv->timestamp != RTP_PKT_TS(pkt)) {
00334                     fr->len = priv->len = 0;
00335                     rtp_rm_pkt(ssrc);
00336                     return RTP_PKT_UNKNOWN;
00337                 }
00338                 if(nms_alloc_data(&priv->data, &priv->data_size,
00339                                   len + priv->len)) {
00340                     return RTP_ERRALLOC;
00341                 }
00342                 nms_append_incr(priv->data, &priv->len, buf, len);
00343             }
00344 
00345             priv->timestamp = RTP_PKT_TS(pkt);
00346             if (!end_bit) {
00347                 err = EAGAIN;
00348             } else {
00349                 fr->data = priv->data;
00350                 fr->len = priv->len;
00351                 priv->len = 0;
00352             }
00353         }
00354         break;
00355     case 30:                   // undefined
00356     case 31:                   // undefined
00357     default:
00358         err = RTP_PKT_UNKNOWN;
00359         break;
00360     }
00361 
00362     rtp_rm_pkt(ssrc);
00363 
00364     return err;
00365 }
00366 
00367 RTP_PARSER_FULL(h264);

Generated on Tue Feb 3 03:10:02 2009 for libnemesi by  doxygen 1.5.4