Mercurial > mplayer.hg
annotate libmpdemux/demux_nemesi.c @ 26146:20a126aaa756
ve_vfw.c: #include aviheader.h instead of wine avifmt.h
Compilation was broken after libmpdemux/muxer.h started including
libmpdemux/aviheader.h. ve_vfw.c included both muxer.h and
loader/wine/avifmt.h, and the latter has definitions that conflict with
aviheader.h ones. Fix by removing the avifmt.h include.
I did not carefully check that changing the includes doesn't break
any ve_vfw.c code. However it at least fixes compilation, and if the
avifmt.h versions differ in some significant way then the code is
fundamentally broken anyway: ve_vfw cannot use different versions of
the avi struct definitions when it also uses shared muxer.h types
(those must use the standard definitions to keep the type compatible
with what's used in other files).
author | uau |
---|---|
date | Thu, 06 Mar 2008 01:57:26 +0000 |
parents | 4cd12675cfbb |
children | 0c1db5fd3f79 |
rev | line source |
---|---|
24564 | 1 /* |
2 * Copyright (C) 2007 Alessandro Molina <amol.wrk@gmail.com> | |
3 * | |
4 * MPlayer is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; either version 2 of the License, or | |
7 * (at your option) any later version. | |
8 * | |
9 * MPlayer is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with MPlayer; if not, write to the Free Software Foundation, | |
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
17 */ | |
25267 | 18 #include <stdlib.h> |
19 #include <stdio.h> | |
20 #include "stream/stream.h" | |
21 #include "demuxer.h" | |
24564 | 22 #include "stheader.h" |
23 #define HAVE_STRUCT_SOCKADDR_STORAGE | |
24 #include "nemesi/rtsp.h" | |
25 #include "nemesi/rtp.h" | |
24856 | 26 #include <sched.h> |
24564 | 27 |
28 int rtsp_transport_tcp = 0; | |
29 int rtsp_transport_sctp = 0; | |
25156 | 30 int rtsp_port = 0; |
24564 | 31 |
25103 | 32 typedef struct { |
33 char * mime; | |
34 unsigned int fourcc; | |
35 } MIMEto4CC; | |
36 | |
37 #define NMS_MAX_FORMATS 16 | |
38 | |
39 MIMEto4CC supported_audio[NMS_MAX_FORMATS] = { | |
40 {"MPA", 0x55}, | |
41 {"vorbis", mmioFOURCC('v','r','b','s')}, | |
25104 | 42 {"mpeg4-generic", mmioFOURCC('M','P','4','A')}, |
25103 | 43 {NULL, 0}, |
44 }; | |
45 | |
46 MIMEto4CC supported_video[NMS_MAX_FORMATS] = { | |
47 {"MPV", mmioFOURCC('M','P','E','G')}, | |
25116 | 48 {"theora",mmioFOURCC('t','h','e','o')}, |
25103 | 49 {"H264", mmioFOURCC('H','2','6','4')}, |
50 {"H263-1998", mmioFOURCC('H','2','6','3')}, | |
51 {"MP4V-ES", mmioFOURCC('M','P','4','V')}, | |
52 {NULL, 0}, | |
53 }; | |
54 | |
24564 | 55 typedef enum { NEMESI_SESSION_VIDEO, |
56 NEMESI_SESSION_AUDIO } Nemesi_SessionType; | |
57 | |
58 typedef struct { | |
59 rtsp_ctrl * rtsp; | |
60 rtp_session * session[2]; | |
61 rtp_frame first_pkt[2]; | |
62 double time[2]; | |
63 double seek; | |
64 } Nemesi_DemuxerStreamData; | |
65 | |
24997 | 66 |
67 #define STYPE_TO_DS(demuxer, stype) \ | |
68 ((stype) == NEMESI_SESSION_VIDEO ? (demuxer)->video : (demuxer)->audio) | |
69 | |
70 #define DS_TO_STYPE(demuxer, ds) \ | |
71 ((ds) == (demuxer)->video ? NEMESI_SESSION_VIDEO : NEMESI_SESSION_AUDIO) | |
72 | |
73 #define INVERT_STYPE(stype) ((stype + 1) % 2) | |
74 | |
25103 | 75 static unsigned int get4CC(MIMEto4CC * supported_formats, char const * format) |
76 { | |
77 unsigned i; | |
78 | |
79 for(i = 0; i < NMS_MAX_FORMATS; ++i) { | |
80 if (!supported_formats[i].mime) | |
81 return 0; | |
82 else if ( strcmp(supported_formats[i].mime, format) == 0 ) | |
83 return supported_formats[i].fourcc; | |
84 } | |
85 | |
86 return 0; | |
87 } | |
24997 | 88 |
89 static rtp_ssrc *wait_for_packets(Nemesi_DemuxerStreamData * ndsd, Nemesi_SessionType stype) | |
90 { | |
91 rtp_ssrc *ssrc = NULL; | |
92 | |
93 /* Wait for prebuffering (prebuffering must be enabled in nemesi) */ | |
94 int terminated = rtp_fill_buffers(rtsp_get_rtp_th(ndsd->rtsp)); | |
95 | |
96 /* Wait for the ssrc to be registered, if prebuffering is on in nemesi | |
97 this will just get immediatly the correct ssrc */ | |
98 if (!terminated) { | |
99 while ( !(ssrc = rtp_session_get_ssrc(ndsd->session[stype], ndsd->rtsp)) ) | |
100 sched_yield(); | |
101 } | |
102 | |
103 return ssrc; | |
104 } | |
105 | |
24564 | 106 static void link_session_and_fetch_conf(Nemesi_DemuxerStreamData * ndsd, |
107 Nemesi_SessionType stype, | |
108 rtp_session * sess, | |
109 rtp_buff * buff, unsigned int * fps) | |
110 { | |
25965 | 111 extern double force_fps; |
24855
2c790baff42c
Update to use newer libnemesi, should fix desync, fps guessing may fail now
lu_zero
parents:
24625
diff
changeset
|
112 rtp_ssrc *ssrc = NULL; |
24564 | 113 rtp_frame * fr = &ndsd->first_pkt[stype]; |
114 rtp_buff trash_buff; | |
25010 | 115 int must_prefetch = ((fps != NULL) || (buff != NULL)) ? 1 : 0; |
24564 | 116 |
117 ndsd->session[stype] = sess; | |
118 | |
24997 | 119 ssrc = wait_for_packets(ndsd, stype); |
24564 | 120 |
25010 | 121 if ( ((ssrc) && (must_prefetch)) ) { |
24997 | 122 if (buff == NULL) |
123 buff = &trash_buff; | |
24856 | 124 |
125 rtp_fill_buffer(ssrc, fr, buff); //Prefetch the first packet | |
24564 | 126 |
24998 | 127 /* Packet prefecthing must be done anyway or we won't be |
128 able to get the metadata, but fps calculation happens | |
129 only if the user didn't specify the FPS */ | |
25010 | 130 if ( ((!force_fps) && (fps != NULL)) ) { |
24998 | 131 while ( *fps <= 0 ) { |
132 //Wait more pkts to calculate FPS and try again | |
133 sched_yield(); | |
134 *fps = rtp_get_fps(ssrc); | |
135 } | |
136 } | |
24564 | 137 } |
138 } | |
139 | |
25267 | 140 static demuxer_t* demux_open_rtp(demuxer_t* demuxer) |
24564 | 141 { |
142 nms_rtsp_hints hints; | |
143 char * url = demuxer->stream->streaming_ctrl->url->url; | |
144 rtsp_ctrl * ctl; | |
145 RTSP_Error reply; | |
146 rtsp_medium * media; | |
147 Nemesi_DemuxerStreamData * ndsd = calloc(1, sizeof(Nemesi_DemuxerStreamData)); | |
148 | |
149 memset(&hints,0,sizeof(hints)); | |
25156 | 150 if (rtsp_port) hints.first_rtp_port = rtsp_port; |
24564 | 151 if (rtsp_transport_tcp) { |
152 hints.pref_rtsp_proto = TCP; | |
153 hints.pref_rtp_proto = TCP; | |
154 } | |
155 if (rtsp_transport_sctp) { | |
156 hints.pref_rtsp_proto = SCTP; | |
157 hints.pref_rtp_proto = SCTP; | |
158 } | |
159 | |
160 mp_msg(MSGT_DEMUX, MSGL_INFO, "Initializing libNemesi\n"); | |
161 if ((ctl = rtsp_init(&hints)) == NULL) { | |
162 free(ndsd); | |
163 return STREAM_ERROR; | |
164 } | |
165 | |
166 ndsd->rtsp = ctl; | |
167 demuxer->priv = ndsd; | |
168 //nms_verbosity_set(1); | |
169 | |
170 mp_msg(MSGT_DEMUX, MSGL_INFO, "Opening: %s\n", url); | |
171 if (rtsp_open(ctl, url)) { | |
172 mp_msg(MSGT_DEMUX, MSGL_ERR, "rtsp_open failed.\n"); | |
173 return demuxer; | |
174 } | |
175 | |
176 reply = rtsp_wait(ctl); | |
177 if (reply.got_error) { | |
178 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
179 "OPEN Error from the server: %s\n", | |
180 reply.message.reply_str); | |
181 return demuxer; | |
182 } | |
183 | |
184 rtsp_play(ctl, 0, 0); | |
185 reply = rtsp_wait(ctl); | |
186 if (reply.got_error) { | |
187 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
188 "PLAY Error from the server: %s\n", | |
189 reply.message.reply_str); | |
190 return demuxer; | |
191 } | |
192 | |
25490 | 193 if (!ctl->rtsp_queue) |
194 return demuxer; | |
195 | |
24564 | 196 media = ctl->rtsp_queue->media_queue; |
197 for (; media; media=media->next) { | |
198 sdp_medium_info * info = media->medium_info; | |
199 rtp_session * sess = media->rtp_sess; | |
25011 | 200 rtp_buff buff; |
24564 | 201 |
202 int media_format = atoi(info->fmts); | |
203 rtp_pt * ptinfo = rtp_get_pt_info(sess, media_format); | |
204 char const * format_name = ptinfo ? ptinfo->name : NULL; | |
205 | |
25011 | 206 memset(&buff, 0, sizeof(rtp_buff)); |
207 | |
24564 | 208 if (sess->parsers[media_format] == NULL) { |
209 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
210 "libNemesi unsupported media format: %s\n", | |
211 format_name ? format_name : info->fmts); | |
212 continue; | |
213 } | |
214 else { | |
215 mp_msg(MSGT_DEMUX, MSGL_INFO, | |
216 "libNemesi supported media: %s\n", | |
217 format_name); | |
218 } | |
219 | |
220 if (ptinfo->type == AU) { | |
221 if (ndsd->session[NEMESI_SESSION_AUDIO] == NULL) { | |
222 sh_audio_t* sh_audio = new_sh_audio(demuxer,0); | |
25011 | 223 WAVEFORMATEX* wf; |
24564 | 224 demux_stream_t* d_audio = demuxer->audio; |
225 | |
226 mp_msg(MSGT_DEMUX, MSGL_INFO, "Detected as AUDIO stream...\n"); | |
227 | |
228 link_session_and_fetch_conf(ndsd, NEMESI_SESSION_AUDIO, | |
25011 | 229 sess, &buff, NULL); |
230 | |
231 if (buff.len) { | |
232 wf = calloc(1,sizeof(WAVEFORMATEX)+buff.len); | |
233 wf->cbSize = buff.len; | |
234 memcpy(wf+1, buff.data, buff.len); | |
235 } else { | |
236 wf = calloc(1,sizeof(WAVEFORMATEX)); | |
237 } | |
24564 | 238 |
239 sh_audio->wf = wf; | |
240 d_audio->sh = sh_audio; | |
241 sh_audio->ds = d_audio; | |
242 wf->nSamplesPerSec = 0; | |
243 | |
25103 | 244 wf->wFormatTag = |
245 sh_audio->format = get4CC(supported_audio, format_name); | |
246 if ( !(wf->wFormatTag) ) | |
24564 | 247 mp_msg(MSGT_DEMUX, MSGL_WARN, |
248 "Unknown MPlayer format code for MIME" | |
249 " type \"audio/%s\"\n", format_name); | |
250 } else { | |
251 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
252 "There is already an audio session registered," | |
253 " ignoring...\n"); | |
254 } | |
255 } else if (ptinfo->type == VI) { | |
25012 | 256 if (ndsd->session[NEMESI_SESSION_VIDEO] == NULL) { |
24564 | 257 sh_video_t* sh_video; |
258 BITMAPINFOHEADER* bih; | |
259 demux_stream_t* d_video; | |
260 int fps = 0; | |
261 | |
262 mp_msg(MSGT_DEMUX, MSGL_INFO, "Detected as VIDEO stream...\n"); | |
263 | |
264 link_session_and_fetch_conf(ndsd, NEMESI_SESSION_VIDEO, | |
265 sess, &buff, &fps); | |
266 | |
267 if (buff.len) { | |
268 bih = calloc(1,sizeof(BITMAPINFOHEADER)+buff.len); | |
269 bih->biSize = sizeof(BITMAPINFOHEADER)+buff.len; | |
270 memcpy(bih+1, buff.data, buff.len); | |
271 } else { | |
272 bih = calloc(1,sizeof(BITMAPINFOHEADER)); | |
273 bih->biSize = sizeof(BITMAPINFOHEADER); | |
274 } | |
275 | |
276 sh_video = new_sh_video(demuxer,0); | |
277 sh_video->bih = bih; | |
278 d_video = demuxer->video; | |
279 d_video->sh = sh_video; | |
280 sh_video->ds = d_video; | |
281 | |
24856 | 282 if (fps) { |
24564 | 283 sh_video->fps = fps; |
24856 | 284 sh_video->frametime = 1.0/fps; |
285 } | |
24564 | 286 |
25103 | 287 bih->biCompression = |
288 sh_video->format = get4CC(supported_video, format_name); | |
289 if ( !(bih->biCompression) ) { | |
24564 | 290 mp_msg(MSGT_DEMUX, MSGL_WARN, |
291 "Unknown MPlayer format code for MIME" | |
292 " type \"video/%s\"\n", format_name); | |
293 } | |
294 } else { | |
295 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
296 "There is already a video session registered," | |
297 " ignoring...\n"); | |
298 } | |
299 } else { | |
300 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unsupported media type\n"); | |
301 } | |
302 } | |
303 | |
304 demuxer->stream->eof = 0; | |
305 | |
306 return demuxer; | |
307 } | |
308 | |
309 static int get_data_for_session(Nemesi_DemuxerStreamData * ndsd, | |
24997 | 310 Nemesi_SessionType stype, rtp_ssrc * ssrc, |
311 rtp_frame * fr) | |
24564 | 312 { |
24997 | 313 if (ndsd->first_pkt[stype].len != 0) { |
314 fr->data = ndsd->first_pkt[stype].data; | |
315 fr->time_sec = ndsd->first_pkt[stype].time_sec; | |
316 fr->len = ndsd->first_pkt[stype].len; | |
317 ndsd->first_pkt[stype].len = 0; | |
318 return RTP_FILL_OK; | |
319 } else { | |
320 rtp_buff buff; | |
321 return rtp_fill_buffer(ssrc, fr, &buff); | |
322 } | |
323 } | |
24564 | 324 |
24997 | 325 static void stream_add_packet(Nemesi_DemuxerStreamData * ndsd, |
326 Nemesi_SessionType stype, | |
327 demux_stream_t* ds, rtp_frame * fr) | |
328 { | |
329 demux_packet_t* dp = new_demux_packet(fr->len); | |
330 memcpy(dp->buffer, fr->data, fr->len); | |
24564 | 331 |
24997 | 332 fr->time_sec += ndsd->seek; |
333 ndsd->time[stype] = dp->pts = fr->time_sec; | |
334 | |
335 ds_add_packet(ds, dp); | |
24564 | 336 } |
337 | |
25267 | 338 static int demux_rtp_fill_buffer(demuxer_t* demuxer, demux_stream_t* ds) |
24564 | 339 { |
340 Nemesi_DemuxerStreamData * ndsd = demuxer->priv; | |
341 Nemesi_SessionType stype; | |
24997 | 342 rtp_ssrc * ssrc; |
24564 | 343 rtp_frame fr; |
344 | |
24997 | 345 if ( (!ndsd->rtsp->rtsp_queue) || (demuxer->stream->eof) ) { |
24564 | 346 mp_msg(MSGT_DEMUX, MSGL_INFO, "End of Stream...\n"); |
347 demuxer->stream->eof = 1; | |
348 return 0; | |
349 } | |
350 | |
25103 | 351 memset(&fr, 0, sizeof(fr)); |
352 | |
24997 | 353 stype = DS_TO_STYPE(demuxer, ds); |
354 if ( (ssrc = wait_for_packets(ndsd, stype)) == NULL ) { | |
355 mp_msg(MSGT_DEMUX, MSGL_INFO, "Bye...\n"); | |
356 demuxer->stream->eof = 1; | |
24564 | 357 return 0; |
358 } | |
359 | |
24997 | 360 if(!get_data_for_session(ndsd, stype, ssrc, &fr)) |
361 stream_add_packet(ndsd, stype, ds, &fr); | |
362 else { | |
363 stype = INVERT_STYPE(stype); | |
25013
dc6b7ad240db
Check for second stream presence, fixes single stream playback (from amol)
lu_zero
parents:
25012
diff
changeset
|
364 |
dc6b7ad240db
Check for second stream presence, fixes single stream playback (from amol)
lu_zero
parents:
25012
diff
changeset
|
365 //Must check if we actually have a stream of the other type |
dc6b7ad240db
Check for second stream presence, fixes single stream playback (from amol)
lu_zero
parents:
25012
diff
changeset
|
366 if (!ndsd->session[stype]) |
dc6b7ad240db
Check for second stream presence, fixes single stream playback (from amol)
lu_zero
parents:
25012
diff
changeset
|
367 return 1; |
dc6b7ad240db
Check for second stream presence, fixes single stream playback (from amol)
lu_zero
parents:
25012
diff
changeset
|
368 |
24997 | 369 ds = STYPE_TO_DS(demuxer, stype); |
370 ssrc = wait_for_packets(ndsd, stype); | |
371 | |
372 if(!get_data_for_session(ndsd, stype, ssrc, &fr)) | |
373 stream_add_packet(ndsd, stype, ds, &fr); | |
24564 | 374 } |
375 | |
376 return 1; | |
377 } | |
378 | |
379 | |
25267 | 380 static void demux_close_rtp(demuxer_t* demuxer) |
24564 | 381 { |
382 Nemesi_DemuxerStreamData * ndsd = demuxer->priv; | |
383 rtsp_ctrl * ctl = ndsd->rtsp; | |
384 RTSP_Error err; | |
385 | |
386 mp_msg(MSGT_DEMUX, MSGL_INFO, "Closing libNemesi RTSP Stream...\n"); | |
387 | |
388 if (ndsd == NULL) | |
389 return; | |
390 | |
391 free(ndsd); | |
392 | |
393 if (rtsp_close(ctl)) { | |
394 err = rtsp_wait(ctl); | |
395 if (err.got_error) | |
396 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
397 "Error Closing Stream: %s\n", | |
398 err.message.reply_str); | |
399 } | |
400 | |
401 rtsp_uninit(ctl); | |
402 } | |
403 | |
404 static void demux_seek_rtp(demuxer_t *demuxer, float rel_seek_secs, | |
405 float audio_delay, int flags) | |
406 { | |
407 Nemesi_DemuxerStreamData * ndsd = demuxer->priv; | |
408 rtsp_ctrl * ctl = ndsd->rtsp; | |
409 sdp_attr * r_attr = NULL; | |
410 sdp_range r = {0, 0}; | |
411 double time = ndsd->time[NEMESI_SESSION_VIDEO] ? | |
412 ndsd->time[NEMESI_SESSION_VIDEO] : | |
413 ndsd->time[NEMESI_SESSION_AUDIO]; | |
414 | |
415 if (!ctl->rtsp_queue) | |
416 return; | |
417 | |
418 r_attr = sdp_get_attr(ctl->rtsp_queue->info->attr_list, "range"); | |
419 if (r_attr) | |
420 r = sdp_parse_range(r_attr->value); | |
421 | |
422 //flags & 1 -> absolute seek | |
423 //flags & 2 -> percent seek | |
424 if (flags == 0) { | |
425 time += rel_seek_secs; | |
426 if (time < r.begin) | |
427 time = r.begin; | |
428 else if (time > r.end) | |
429 time = r.end; | |
430 ndsd->seek = time; | |
431 | |
432 mp_msg(MSGT_DEMUX,MSGL_WARN,"libNemesi SEEK %f on %f - %f)\n", | |
433 time, r.begin, r.end); | |
434 | |
435 if (!rtsp_seek(ctl, time, 0)) { | |
436 RTSP_Error err = rtsp_wait(ctl); | |
437 if (err.got_error) { | |
438 mp_msg(MSGT_DEMUX, MSGL_ERR, | |
439 "Error Performing Seek: %s\n", | |
440 err.message.reply_str); | |
441 demuxer->stream->eof = 1; | |
442 } | |
443 else | |
444 mp_msg(MSGT_DEMUX, MSGL_INFO, "Seek, performed\n"); | |
445 } | |
446 else { | |
447 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unable to pause stream to perform seek\n"); | |
448 demuxer->stream->eof = 1; | |
449 } | |
450 } | |
451 else | |
452 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unsupported seek type\n"); | |
453 } | |
454 | |
455 static int demux_rtp_control(struct demuxer_st *demuxer, int cmd, void *arg) | |
456 { | |
457 Nemesi_DemuxerStreamData * ndsd = demuxer->priv; | |
458 rtsp_ctrl * ctl = ndsd->rtsp; | |
459 sdp_attr * r_attr = NULL; | |
460 sdp_range r = {0, 0}; | |
461 double time = ndsd->time[NEMESI_SESSION_VIDEO] ? | |
462 ndsd->time[NEMESI_SESSION_VIDEO] : | |
463 ndsd->time[NEMESI_SESSION_AUDIO]; | |
464 | |
465 if (!ctl->rtsp_queue) | |
466 return DEMUXER_CTRL_DONTKNOW; | |
467 | |
468 r_attr = sdp_get_attr(ctl->rtsp_queue->info->attr_list, "range"); | |
469 if (r_attr) | |
470 r = sdp_parse_range(r_attr->value); | |
471 | |
472 switch (cmd) { | |
473 case DEMUXER_CTRL_GET_TIME_LENGTH: | |
474 if (r.end == 0) | |
475 return DEMUXER_CTRL_DONTKNOW; | |
476 | |
477 *((double *)arg) = ((double)r.end) - ((double)r.begin); | |
478 return DEMUXER_CTRL_OK; | |
479 | |
480 case DEMUXER_CTRL_GET_PERCENT_POS: | |
481 if (r.end == 0) | |
482 return DEMUXER_CTRL_DONTKNOW; | |
483 | |
484 *((int *)arg) = (int)( time * 100 / (r.end - r.begin) ); | |
485 return DEMUXER_CTRL_OK; | |
486 default: | |
487 return DEMUXER_CTRL_DONTKNOW; | |
488 } | |
489 } | |
490 | |
25707
d4fe6e23283e
Make all demuxer_desc_t const, thus moving them to .rodata
reimar
parents:
25490
diff
changeset
|
491 const demuxer_desc_t demuxer_desc_rtp_nemesi = { |
25270 | 492 "libnemesi RTP demuxer", |
493 "nemesi", | |
24564 | 494 "", |
495 "Alessandro Molina", | |
25270 | 496 "requires libnemesi", |
25266
239330301b33
Make libnemesi use specific struct and DEMUXER_TYPE
lu_zero
parents:
25156
diff
changeset
|
497 DEMUXER_TYPE_RTP_NEMESI, |
24564 | 498 0, // no autodetect |
499 NULL, | |
500 demux_rtp_fill_buffer, | |
501 demux_open_rtp, | |
502 demux_close_rtp, | |
503 demux_seek_rtp, | |
504 demux_rtp_control | |
505 }; |