00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "avstring.h"
00022 #include "avformat.h"
00023 #include "avc.h"
00024 #include "base64.h"
00025 #include "rtp.h"
00026
00027 #ifdef CONFIG_RTP_MUXER
00028 #define MAX_EXTRADATA_SIZE ((INT_MAX - 10) / 2)
00029
00030 struct sdp_session_level {
00031 int sdp_version;
00032 int id;
00033 int version;
00034 int start_time;
00036 int end_time;
00038 int ttl;
00039 const char *user;
00040 const char *src_addr;
00041 const char *dst_addr;
00042 const char *name;
00043 };
00044
00045 static void dest_write(char *buff, int size, const char *dest_addr, int ttl)
00046 {
00047 if (dest_addr) {
00048 if (ttl > 0) {
00049 av_strlcatf(buff, size, "c=IN IP4 %s/%d\r\n", dest_addr, ttl);
00050 } else {
00051 av_strlcatf(buff, size, "c=IN IP4 %s\r\n", dest_addr);
00052 }
00053 }
00054 }
00055
00056 static void sdp_write_header(char *buff, int size, struct sdp_session_level *s)
00057 {
00058 av_strlcatf(buff, size, "v=%d\r\n"
00059 "o=- %d %d IN IPV4 %s\r\n"
00060 "t=%d %d\r\n"
00061 "s=%s\r\n"
00062 "a=tool:libavformat\r\n",
00063 s->sdp_version,
00064 s->id, s->version, s->src_addr,
00065 s->start_time, s->end_time,
00066 s->name[0] ? s->name : "No Name");
00067 dest_write(buff, size, s->dst_addr, s->ttl);
00068 }
00069
00070 static int get_address(char *dest_addr, int size, int *ttl, const char *url)
00071 {
00072 int port;
00073 const char *p;
00074
00075 url_split(NULL, 0, NULL, 0, dest_addr, size, &port, NULL, 0, url);
00076
00077 *ttl = 0;
00078 p = strchr(url, '?');
00079 if (p) {
00080 char buff[64];
00081 int is_multicast = find_info_tag(buff, sizeof(buff), "multicast", p);
00082
00083 if (is_multicast) {
00084 if (find_info_tag(buff, sizeof(buff), "ttl", p)) {
00085 *ttl = strtol(buff, NULL, 10);
00086 } else {
00087 *ttl = 5;
00088 }
00089 }
00090 }
00091
00092 return port;
00093 }
00094
00095 #define MAX_PSET_SIZE 1024
00096 static char *extradata2psets(AVCodecContext *c)
00097 {
00098 char *psets, *p;
00099 uint8_t *r;
00100 const char *pset_string = "; sprop-parameter-sets=";
00101
00102 if (c->extradata_size > MAX_EXTRADATA_SIZE) {
00103 av_log(c, AV_LOG_ERROR, "Too many extra data!\n");
00104
00105 return NULL;
00106 }
00107
00108 psets = av_mallocz(MAX_PSET_SIZE);
00109 if (psets == NULL) {
00110 av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the parameter sets\n");
00111 return NULL;
00112 }
00113 memcpy(psets, pset_string, strlen(pset_string));
00114 p = psets + strlen(pset_string);
00115 r = ff_avc_find_startcode(c->extradata, c->extradata + c->extradata_size);
00116 while (r < c->extradata + c->extradata_size) {
00117 uint8_t *r1;
00118
00119 while (!*(r++));
00120 r1 = ff_avc_find_startcode(r, c->extradata + c->extradata_size);
00121 if (p != (psets + strlen(pset_string))) {
00122 *p = ',';
00123 p++;
00124 }
00125 if (av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r) == NULL) {
00126 av_log(c, AV_LOG_ERROR, "Cannot BASE64 encode %d %d!\n", MAX_PSET_SIZE - (p - psets), r1 - r);
00127 av_free(psets);
00128
00129 return NULL;
00130 }
00131 p += strlen(p);
00132 r = r1;
00133 }
00134
00135 return psets;
00136 }
00137
00138 static void digit_to_char(char *dst, uint8_t src)
00139 {
00140 if (src < 10) {
00141 *dst = '0' + src;
00142 } else {
00143 *dst = 'A' + src - 10;
00144 }
00145 }
00146
00147 static char *data_to_hex(char *buff, const uint8_t *src, int s)
00148 {
00149 int i;
00150
00151 for(i = 0; i < s; i++) {
00152 digit_to_char(buff + 2 * i, src[i] >> 4);
00153 digit_to_char(buff + 2 * i + 1, src[i] & 0xF);
00154 }
00155
00156 return buff;
00157 }
00158
00159 static char *extradata2config(AVCodecContext *c)
00160 {
00161 char *config;
00162
00163 if (c->extradata_size > MAX_EXTRADATA_SIZE) {
00164 av_log(c, AV_LOG_ERROR, "Too many extra data!\n");
00165
00166 return NULL;
00167 }
00168 config = av_malloc(10 + c->extradata_size * 2);
00169 if (config == NULL) {
00170 av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the config info\n");
00171 return NULL;
00172 }
00173 memcpy(config, "; config=", 9);
00174 data_to_hex(config + 9, c->extradata, c->extradata_size);
00175 config[9 + c->extradata_size * 2] = 0;
00176
00177 return config;
00178 }
00179
00180 static char *sdp_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type)
00181 {
00182 char *config = NULL;
00183
00184 switch (c->codec_id) {
00185 case CODEC_ID_H264:
00186 if (c->extradata_size) {
00187 config = extradata2psets(c);
00188 }
00189 av_strlcatf(buff, size, "a=rtpmap:%d H264/90000\r\n"
00190 "a=fmtp:%d packetization-mode=1%s\r\n",
00191 payload_type,
00192 payload_type, config ? config : "");
00193 break;
00194 case CODEC_ID_MPEG4:
00195 if (c->extradata_size) {
00196 config = extradata2config(c);
00197 }
00198 av_strlcatf(buff, size, "a=rtpmap:%d MP4V-ES/90000\r\n"
00199 "a=fmtp:%d profile-level-id=1%s\r\n",
00200 payload_type,
00201 payload_type, config ? config : "");
00202 break;
00203 case CODEC_ID_AAC:
00204 if (c->extradata_size) {
00205 config = extradata2config(c);
00206 } else {
00207
00208
00209
00210 av_log(c, AV_LOG_ERROR, "AAC with no global headers is currently not supported\n");
00211 return NULL;
00212 }
00213 if (config == NULL) {
00214 return NULL;
00215 }
00216 av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n"
00217 "a=fmtp:%d profile-level-id=1;"
00218 "mode=AAC-hbr;sizelength=13;indexlength=3;"
00219 "indexdeltalength=3%s\r\n",
00220 payload_type, c->sample_rate, c->channels,
00221 payload_type, config);
00222 break;
00223 case CODEC_ID_PCM_S16BE:
00224 if (payload_type >= 96)
00225 av_strlcatf(buff, size, "a=rtpmap:%d L16/%d/%d\r\n",
00226 payload_type,
00227 c->sample_rate, c->channels);
00228 break;
00229 case CODEC_ID_PCM_MULAW:
00230 if (payload_type >= 96)
00231 av_strlcatf(buff, size, "a=rtpmap:%d PCMU/%d/%d\r\n",
00232 payload_type,
00233 c->sample_rate, c->channels);
00234 break;
00235 case CODEC_ID_PCM_ALAW:
00236 if (payload_type >= 96)
00237 av_strlcatf(buff, size, "a=rtpmap:%d PCMA/%d/%d\r\n",
00238 payload_type,
00239 c->sample_rate, c->channels);
00240 break;
00241 default:
00242
00243 break;
00244 }
00245
00246 av_free(config);
00247
00248 return buff;
00249 }
00250
00251 static void sdp_write_media(char *buff, int size, AVCodecContext *c, const char *dest_addr, int port, int ttl)
00252 {
00253 const char *type;
00254 int payload_type;
00255
00256 payload_type = rtp_get_payload_type(c);
00257 if (payload_type < 0) {
00258 payload_type = 96;
00259 }
00260
00261 switch (c->codec_type) {
00262 case CODEC_TYPE_VIDEO : type = "video" ; break;
00263 case CODEC_TYPE_AUDIO : type = "audio" ; break;
00264 case CODEC_TYPE_SUBTITLE: type = "text" ; break;
00265 default : type = "application"; break;
00266 }
00267
00268 av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type);
00269 dest_write(buff, size, dest_addr, ttl);
00270
00271 sdp_media_attributes(buff, size, c, payload_type);
00272 }
00273
00274 int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size)
00275 {
00276 struct sdp_session_level s;
00277 int i, j, port, ttl;
00278 char dst[32];
00279
00280 memset(buff, 0, size);
00281 memset(&s, 0, sizeof(struct sdp_session_level));
00282 s.user = "-";
00283 s.src_addr = "127.0.0.1";
00284 s.name = ac[0]->title;
00285
00286 port = 0;
00287 ttl = 0;
00288 if (n_files == 1) {
00289 port = get_address(dst, sizeof(dst), &ttl, ac[0]->filename);
00290 if (port > 0) {
00291 s.dst_addr = dst;
00292 s.ttl = ttl;
00293 }
00294 }
00295 sdp_write_header(buff, size, &s);
00296
00297 dst[0] = 0;
00298 for (i = 0; i < n_files; i++) {
00299 if (n_files != 1) {
00300 port = get_address(dst, sizeof(dst), &ttl, ac[i]->filename);
00301 }
00302 for (j = 0; j < ac[i]->nb_streams; j++) {
00303 sdp_write_media(buff, size,
00304 ac[i]->streams[j]->codec, dst[0] ? dst : NULL,
00305 (port > 0) ? port + j * 2 : 0, ttl);
00306 if (port <= 0) {
00307 av_strlcatf(buff, size,
00308 "a=control:streamid=%d\r\n", i + j);
00309 }
00310 }
00311 }
00312
00313 return 0;
00314 }
00315 #else
00316 int avf_sdp_create(AVFormatContext *ac[], int n_files, char *buff, int size)
00317 {
00318 return AVERROR(ENOSYS);
00319 }
00320 #endif