• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavformat/sdp.c

Go to the documentation of this file.
00001 /*
00002  * copyright (c) 2007 Luca Abeni
00003  *
00004  * This file is part of FFmpeg.
00005  *
00006  * FFmpeg is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * FFmpeg is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with FFmpeg; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 
00021 #include <string.h>
00022 #include "libavutil/avstring.h"
00023 #include "libavutil/base64.h"
00024 #include "avformat.h"
00025 #include "internal.h"
00026 #include "avc.h"
00027 #include "rtp.h"
00028 #if CONFIG_NETWORK
00029 #include "network.h"
00030 #endif
00031 
00032 #if CONFIG_RTP_MUXER
00033 #define MAX_EXTRADATA_SIZE ((INT_MAX - 10) / 2)
00034 
00035 struct sdp_session_level {
00036     int sdp_version;      
00037     int id;               
00038     int version;          
00039     int start_time;       
00041     int end_time;         
00043     int ttl;              
00044     const char *user;     
00045     const char *src_addr; 
00046     const char *dst_addr; 
00047     const char *name;     
00048 };
00049 
00050 static void sdp_write_address(char *buff, int size, const char *dest_addr, int ttl)
00051 {
00052     if (dest_addr) {
00053         if (ttl > 0) {
00054             av_strlcatf(buff, size, "c=IN IP4 %s/%d\r\n", dest_addr, ttl);
00055         } else {
00056             av_strlcatf(buff, size, "c=IN IP4 %s\r\n", dest_addr);
00057         }
00058     }
00059 }
00060 
00061 static void sdp_write_header(char *buff, int size, struct sdp_session_level *s)
00062 {
00063     av_strlcatf(buff, size, "v=%d\r\n"
00064                             "o=- %d %d IN IP4 %s\r\n"
00065                             "s=%s\r\n",
00066                             s->sdp_version,
00067                             s->id, s->version, s->src_addr,
00068                             s->name);
00069     sdp_write_address(buff, size, s->dst_addr, s->ttl);
00070     av_strlcatf(buff, size, "t=%d %d\r\n"
00071                             "a=tool:libavformat " AV_STRINGIFY(LIBAVFORMAT_VERSION) "\r\n",
00072                             s->start_time, s->end_time);
00073 }
00074 
00075 #if CONFIG_NETWORK
00076 static void resolve_destination(char *dest_addr, int size)
00077 {
00078     struct addrinfo hints, *ai, *cur;
00079 
00080     if (!dest_addr[0])
00081         return;
00082 
00083     /* Resolve the destination, since it must be written
00084      * as a numeric IP address in the SDP. */
00085 
00086     memset(&hints, 0, sizeof(hints));
00087     /* We only support IPv4 addresses in the SDP at the moment. */
00088     hints.ai_family = AF_INET;
00089     if (getaddrinfo(dest_addr, NULL, &hints, &ai))
00090         return;
00091     for (cur = ai; cur; cur = cur->ai_next) {
00092         if (cur->ai_family == AF_INET) {
00093             getnameinfo(cur->ai_addr, cur->ai_addrlen, dest_addr, size,
00094                         NULL, 0, NI_NUMERICHOST);
00095             break;
00096         }
00097     }
00098     freeaddrinfo(ai);
00099 }
00100 #else
00101 static void resolve_destination(char *dest_addr, int size)
00102 {
00103 }
00104 #endif
00105 
00106 static int sdp_get_address(char *dest_addr, int size, int *ttl, const char *url)
00107 {
00108     int port;
00109     const char *p;
00110     char proto[32];
00111 
00112     ff_url_split(proto, sizeof(proto), NULL, 0, dest_addr, size, &port, NULL, 0, url);
00113 
00114     *ttl = 0;
00115 
00116     if (strcmp(proto, "rtp")) {
00117         /* The url isn't for the actual rtp sessions,
00118          * don't parse out anything else than the destination.
00119          */
00120         return 0;
00121     }
00122 
00123     p = strchr(url, '?');
00124     if (p) {
00125         char buff[64];
00126         int is_multicast = find_info_tag(buff, sizeof(buff), "multicast", p);
00127 
00128         if (is_multicast) {
00129             if (find_info_tag(buff, sizeof(buff), "ttl", p)) {
00130                 *ttl = strtol(buff, NULL, 10);
00131             } else {
00132                 *ttl = 5;
00133             }
00134         }
00135     }
00136 
00137     return port;
00138 }
00139 
00140 #define MAX_PSET_SIZE 1024
00141 static char *extradata2psets(AVCodecContext *c)
00142 {
00143     char *psets, *p;
00144     const uint8_t *r;
00145     const char *pset_string = "; sprop-parameter-sets=";
00146 
00147     if (c->extradata_size > MAX_EXTRADATA_SIZE) {
00148         av_log(c, AV_LOG_ERROR, "Too much extradata!\n");
00149 
00150         return NULL;
00151     }
00152 
00153     psets = av_mallocz(MAX_PSET_SIZE);
00154     if (psets == NULL) {
00155         av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the parameter sets.\n");
00156         return NULL;
00157     }
00158     memcpy(psets, pset_string, strlen(pset_string));
00159     p = psets + strlen(pset_string);
00160     r = ff_avc_find_startcode(c->extradata, c->extradata + c->extradata_size);
00161     while (r < c->extradata + c->extradata_size) {
00162         const uint8_t *r1;
00163         uint8_t nal_type;
00164 
00165         while (!*(r++));
00166         nal_type = *r & 0x1f;
00167         r1 = ff_avc_find_startcode(r, c->extradata + c->extradata_size);
00168         if (nal_type != 7 && nal_type != 8) { /* Only output SPS and PPS */
00169             r = r1;
00170             continue;
00171         }
00172         if (p != (psets + strlen(pset_string))) {
00173             *p = ',';
00174             p++;
00175         }
00176         if (av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r) == NULL) {
00177             av_log(c, AV_LOG_ERROR, "Cannot Base64-encode %td %td!\n", MAX_PSET_SIZE - (p - psets), r1 - r);
00178             av_free(psets);
00179 
00180             return NULL;
00181         }
00182         p += strlen(p);
00183         r = r1;
00184     }
00185 
00186     return psets;
00187 }
00188 
00189 static char *extradata2config(AVCodecContext *c)
00190 {
00191     char *config;
00192 
00193     if (c->extradata_size > MAX_EXTRADATA_SIZE) {
00194         av_log(c, AV_LOG_ERROR, "Too much extradata!\n");
00195 
00196         return NULL;
00197     }
00198     config = av_malloc(10 + c->extradata_size * 2);
00199     if (config == NULL) {
00200         av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n");
00201         return NULL;
00202     }
00203     memcpy(config, "; config=", 9);
00204     ff_data_to_hex(config + 9, c->extradata, c->extradata_size, 0);
00205     config[9 + c->extradata_size * 2] = 0;
00206 
00207     return config;
00208 }
00209 
00210 static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type)
00211 {
00212     char *config = NULL;
00213 
00214     switch (c->codec_id) {
00215         case CODEC_ID_H264:
00216             if (c->extradata_size) {
00217                 config = extradata2psets(c);
00218             }
00219             av_strlcatf(buff, size, "a=rtpmap:%d H264/90000\r\n"
00220                                     "a=fmtp:%d packetization-mode=1%s\r\n",
00221                                      payload_type,
00222                                      payload_type, config ? config : "");
00223             break;
00224         case CODEC_ID_H263:
00225         case CODEC_ID_H263P:
00226             av_strlcatf(buff, size, "a=rtpmap:%d H263-2000/90000\r\n", payload_type);
00227             break;
00228         case CODEC_ID_MPEG4:
00229             if (c->extradata_size) {
00230                 config = extradata2config(c);
00231             }
00232             av_strlcatf(buff, size, "a=rtpmap:%d MP4V-ES/90000\r\n"
00233                                     "a=fmtp:%d profile-level-id=1%s\r\n",
00234                                      payload_type,
00235                                      payload_type, config ? config : "");
00236             break;
00237         case CODEC_ID_AAC:
00238             if (c->extradata_size) {
00239                 config = extradata2config(c);
00240             } else {
00241                 /* FIXME: maybe we can forge config information based on the
00242                  *        codec parameters...
00243                  */
00244                 av_log(c, AV_LOG_ERROR, "AAC with no global headers is currently not supported.\n");
00245                 return NULL;
00246             }
00247             if (config == NULL) {
00248                 return NULL;
00249             }
00250             av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n"
00251                                     "a=fmtp:%d profile-level-id=1;"
00252                                     "mode=AAC-hbr;sizelength=13;indexlength=3;"
00253                                     "indexdeltalength=3%s\r\n",
00254                                      payload_type, c->sample_rate, c->channels,
00255                                      payload_type, config);
00256             break;
00257         case CODEC_ID_PCM_S16BE:
00258             if (payload_type >= RTP_PT_PRIVATE)
00259                 av_strlcatf(buff, size, "a=rtpmap:%d L16/%d/%d\r\n",
00260                                          payload_type,
00261                                          c->sample_rate, c->channels);
00262             break;
00263         case CODEC_ID_PCM_MULAW:
00264             if (payload_type >= RTP_PT_PRIVATE)
00265                 av_strlcatf(buff, size, "a=rtpmap:%d PCMU/%d/%d\r\n",
00266                                          payload_type,
00267                                          c->sample_rate, c->channels);
00268             break;
00269         case CODEC_ID_PCM_ALAW:
00270             if (payload_type >= RTP_PT_PRIVATE)
00271                 av_strlcatf(buff, size, "a=rtpmap:%d PCMA/%d/%d\r\n",
00272                                          payload_type,
00273                                          c->sample_rate, c->channels);
00274             break;
00275         case CODEC_ID_AMR_NB:
00276             av_strlcatf(buff, size, "a=rtpmap:%d AMR/%d/%d\r\n"
00277                                     "a=fmtp:%d octet-align=1\r\n",
00278                                      payload_type, c->sample_rate, c->channels,
00279                                      payload_type);
00280             break;
00281         case CODEC_ID_AMR_WB:
00282             av_strlcatf(buff, size, "a=rtpmap:%d AMR-WB/%d/%d\r\n"
00283                                     "a=fmtp:%d octet-align=1\r\n",
00284                                      payload_type, c->sample_rate, c->channels,
00285                                      payload_type);
00286             break;
00287         default:
00288             /* Nothing special to do here... */
00289             break;
00290     }
00291 
00292     av_free(config);
00293 
00294     return buff;
00295 }
00296 
00297 void ff_sdp_write_media(char *buff, int size, AVCodecContext *c, const char *dest_addr, int port, int ttl)
00298 {
00299     const char *type;
00300     int payload_type;
00301 
00302     payload_type = ff_rtp_get_payload_type(c);
00303     if (payload_type < 0) {
00304         payload_type = RTP_PT_PRIVATE + (c->codec_type == AVMEDIA_TYPE_AUDIO);
00305     }
00306 
00307     switch (c->codec_type) {
00308         case AVMEDIA_TYPE_VIDEO   : type = "video"      ; break;
00309         case AVMEDIA_TYPE_AUDIO   : type = "audio"      ; break;
00310         case AVMEDIA_TYPE_SUBTITLE: type = "text"       ; break;
00311         default                 : type = "application"; break;
00312     }
00313 
00314     av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type);
00315     sdp_write_address(buff, size, dest_addr, ttl);
00316     if (c->bit_rate) {
00317         av_strlcatf(buff, size, "b=AS:%d\r\n", c->bit_rate / 1000);
00318     }
00319 
00320     sdp_write_media_attributes(buff, size, c, payload_type);
00321 }
00322 
00323 int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size)
00324 {
00325     AVMetadataTag *title = av_metadata_get(ac[0]->metadata, "title", NULL, 0);
00326     struct sdp_session_level s;
00327     int i, j, port, ttl;
00328     char dst[32];
00329 
00330     memset(buff, 0, size);
00331     memset(&s, 0, sizeof(struct sdp_session_level));
00332     s.user = "-";
00333     s.src_addr = "127.0.0.1";    /* FIXME: Properly set this */
00334     s.name = title ? title->value : "No Name";
00335 
00336     port = 0;
00337     ttl = 0;
00338     if (n_files == 1) {
00339         port = sdp_get_address(dst, sizeof(dst), &ttl, ac[0]->filename);
00340         resolve_destination(dst, sizeof(dst));
00341         if (dst[0]) {
00342             s.dst_addr = dst;
00343             s.ttl = ttl;
00344         }
00345     }
00346     sdp_write_header(buff, size, &s);
00347 
00348     dst[0] = 0;
00349     for (i = 0; i < n_files; i++) {
00350         if (n_files != 1) {
00351             port = sdp_get_address(dst, sizeof(dst), &ttl, ac[i]->filename);
00352             resolve_destination(dst, sizeof(dst));
00353         }
00354         for (j = 0; j < ac[i]->nb_streams; j++) {
00355             ff_sdp_write_media(buff, size,
00356                                   ac[i]->streams[j]->codec, dst[0] ? dst : NULL,
00357                                   (port > 0) ? port + j * 2 : 0, ttl);
00358             if (port <= 0) {
00359                 av_strlcatf(buff, size,
00360                                    "a=control:streamid=%d\r\n", i + j);
00361             }
00362         }
00363     }
00364 
00365     return 0;
00366 }
00367 #else
00368 int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size)
00369 {
00370     return AVERROR(ENOSYS);
00371 }
00372 
00373 void ff_sdp_write_media(char *buff, int size, AVCodecContext *c,
00374                         const char *dest_addr, int port, int ttl)
00375 {
00376 }
00377 #endif

Generated on Fri Sep 16 2011 17:17:51 for FFmpeg by  doxygen 1.7.1