1 /* Copyright (C) 2005 Xiph.org Foundation
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
7 - Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
10 - Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
14 - Neither the name of the Xiph.org Foundation nor the names of its
15 contributors may be used to endorse or promote products derived from
16 this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
22 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /* sample RTP Vorbis client */
33 /* compile with: gcc -g -O2 -Wall -o vorbisrtp-client vorbisrtp-client.c -logg
34 * append -DCHECK -lvorbis to enable header checks*/
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
51 #include <theora/theora.h>
53 #define MAX_PACKET 1500
55 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
56 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
58 typedef struct ogg_context
{
62 long long int prev_gp
;
63 long int prev_keyframe
;
74 unsigned int timestamp
;
81 dump_packet_raw (unsigned char *data
, const int len
, FILE * out
)
88 fprintf (out
, " %04x ", i
);
90 for (j
= 0; j
< n
; j
++)
91 fprintf (out
, " %02x", data
[i
+ j
]);
93 n
= MIN (16, len
- i
);
94 for (j
= 8; j
< 16; j
++)
95 fprintf (out
, " %02x", data
[i
+ j
]);
97 for (j
= 0; j
< n
; j
++)
98 fprintf (out
, "%c", isprint (data
[i
+ j
]) ? data
[i
+ j
] : '.');
107 dump_packet_rtp (unsigned char *data
, const int len
, FILE * out
)
109 int V
, P
, X
, CC
, M
, PT
;
110 unsigned short sequence
;
111 unsigned int timestamp
, ssrc
;
114 int i
, offset
, length
;
116 /* parse RTP header */
117 V
= (data
[0] & 0xc0) >> 6;
118 P
= (data
[0] & 0x40) >> 5;
119 X
= (data
[0] & 0x20) >> 4;
120 CC
= (data
[0] & 0x0f);
121 M
= (data
[1] & 0x80) >> 7;
122 PT
= (data
[1] & 0x7F);
123 sequence
= ntohs (((unsigned short *) data
)[1]);
124 timestamp
= ntohl (((unsigned int *) data
)[1]);
125 ssrc
= ntohl (((unsigned int *) data
)[2]);
127 fprintf (out
, "RTP packet V:%d P:%d X:%d M:%d PT:%d", V
, P
, X
, M
, PT
);
128 fprintf (out
, " seq %d", sequence
);
129 fprintf (out
, " timestamp: %u\n", timestamp
);
130 fprintf (out
, " ssrc: 0x%08x", ssrc
);
132 for (i
= 0; i
< CC
; i
++)
133 fprintf (out
, " csrc: 0x%08x",
134 ntohl (((unsigned int *) data
)[3 + i
]));
136 fprintf (out
, " no csrc");
138 /* offset to payload header */
139 offset
= (3 + CC
) * 4;
141 /* parse payload header */
142 ident
= data
[offset
++]<<16;
143 ident
+= data
[offset
++]<<8;
144 ident
+= data
[offset
++];
145 F
= (data
[offset
] & 0xc0) >> 6;
146 VDT
= (data
[offset
] & 0x30) >> 4;
147 pkts
= (data
[offset
] & 0x0F);
150 fprintf(out
,"ident %06x, frag type %d, data type %d, pkts %d, size %d\n",
151 ident
,F
,VDT
,pkts
,len
-4*(CC
+3));
153 for (i
= 0; i
< pkts
; i
++)
157 fprintf (stderr
, "payload length overflow. corrupt packet?\n");
160 length
= data
[offset
++]<<8;
161 length
+= data
[offset
++];
162 fprintf (out
, " data: %d bytes in block %d\n", length
, i
);
167 length
= data
[offset
++]<<8;
168 length
+= data
[offset
++];
169 fprintf (out
, " data: %d bytes in fragment\n", length
);
173 if (len
- offset
> 0)
174 fprintf (out
, " %d unused bytes at the end of the packet!\n",
180 int cfg_parse( ogg_context_t
*ogg
)
184 oggpackB_readinit(&opb
,ogg
->op
.packet
, ogg
->op
.bytes
);
186 oggpackB_read(&opb
,8*7);
187 oggpackB_read(&opb
,8*3);
188 oggpackB_read(&opb
,16);
189 oggpackB_read(&opb
,16);
191 oggpackB_read(&opb
,64);
194 oggpackB_read(&opb
,32);
196 oggpackB_read(&opb
,32);
198 oggpackB_read(&opb
,24);
199 oggpackB_read(&opb
,24);
201 oggpackB_read(&opb
,38);
203 ogg
->gp_shift
= oggpackB_read(&opb
,5);
205 return 0; //FIXME add some checks and return -1 on failure
209 cfg_repack(ogg_context_t
*ogg
, FILE* out
)
212 unsigned char comment
[] =
213 /*quite minimal comment*/
214 { 0x81,'t','h','e','o','r','a',
216 't','h','e','o','r','a','-','r','t','p',
219 /* get the identification packet*/
220 id
.packet
= ogg
->op
.packet
;
227 /* get the comment packet*/
229 co
.bytes
= sizeof(comment
);
234 /* get the setup packet*/
235 cb
.packet
= ogg
->op
.packet
+ 42;
236 cb
.bytes
= ogg
->op
.bytes
- 42;
244 //FIXME add some checks
248 ogg_stream_init(&ogg
->os
,rand());
250 ogg_stream_packetin(&ogg
->os
,&id
);
251 ogg_stream_packetin(&ogg
->os
,&co
);
252 ogg_stream_packetin(&ogg
->os
,&cb
);
255 int result
=ogg_stream_flush(&ogg
->os
,&ogg
->og
);
257 fwrite(ogg
->og
.header
,1,ogg
->og
.header_len
,out
);
258 fwrite(ogg
->og
.body
,1,ogg
->og
.body_len
,out
);
259 }while(!ogg_page_eos(&ogg
->og
));
264 pkt_repack(ogg_context_t
*ogg
, unsigned int timestamp
, FILE *out
){
267 ogg_packet
*op
= &ogg
->op
;
270 oggpack_readinit(&opb
,op
->packet
,op
->bytes
);
271 oggpack_read(&opb
,1); //video marker
273 frame_type
= oggpackB_read(&opb
,1);
275 timestamp
*=ogg
->time_num
/ogg
->time_den
;
279 if (frame_type
== 0) //KEY_FRAME
280 ogg
->prev_keyframe
= timestamp
;
282 ogg
->curr_frame
= timestamp
;
284 op
->granulepos
= (ogg
->curr_frame
- ogg
->prev_keyframe
)
285 | ogg
->prev_keyframe
<< ogg
->gp_shift
;
295 if(frame_type
== 0) //KEY_FRAME
297 ogg
->prev_keyframe
= op
->granulepos
&
298 ((1<<ogg
->gp_shift
)-1);
299 op
->granulepos
>>= ogg
->gp_shift
;
300 op
->granulepos
+= ++ogg
->prev_keyframe
;
301 op
->granulepos
<<= ogg
->gp_shift
;
308 ogg
->prev_gp
= op
->granulepos
;
311 fprintf(stderr
," type %d, timestamp %d, bytes %ld bos %ld eos %ld gp %lld pno %lld\n",frame_type
, timestamp
, op
->bytes
, op
->b_o_s
, op
->e_o_s
, op
->granulepos
, op
->packetno
);
315 ogg_stream_packetin(&ogg
->os
,&ogg
->op
);
317 int result
=ogg_stream_pageout(&ogg
->os
,&ogg
->og
);
319 fwrite(ogg
->og
.header
,1,ogg
->og
.header_len
,out
);
320 fwrite(ogg
->og
.body
,1,ogg
->og
.body_len
,out
);
321 }while(!ogg_page_eos(&ogg
->og
));
327 dump_packet_ogg (unsigned char *data
, const int len
, FILE * out
, ogg_context_t
*ogg
)
329 int V
, P
, X
, CC
, M
, PT
;
330 unsigned short sequence
;
331 unsigned int timestamp
, ssrc
;
334 int i
, offset
, length
, count
= 0;
335 ogg_packet
*op
= &ogg
->op
;
337 /* parse RTP header */
338 V
= (data
[0] & 0xc0) >> 6;
339 P
= (data
[0] & 0x40) >> 5;
340 X
= (data
[0] & 0x20) >> 4;
341 CC
= (data
[0] & 0x0f);
342 M
= (data
[1] & 0x80) >> 7;
343 PT
= (data
[1] & 0x7F);
344 sequence
= ntohs (((unsigned short *) data
)[1]);
345 //FIXME not exactly ideal
346 timestamp
= ntohl (((unsigned int *) data
)[1]) - ogg
->timestamp
;
347 ogg
->timestamp
= ntohl (((unsigned int *) data
)[1]);
348 ssrc
= ntohl (((unsigned int *) data
)[2]);
349 /* offset to payload header */
350 offset
= (3 + CC
) * 4;
352 /* parse Vorbis payload header */
353 ident
= data
[offset
++]<<16;
354 ident
+= data
[offset
++]<<8;
355 ident
+= data
[offset
++];
356 F
= (data
[offset
] & 0xc0) >> 6;
357 VDT
= (data
[offset
] & 0x30) >> 4;
358 pkts
= (data
[offset
] & 0x0F);
364 op
->bytes
= data
[offset
++]<<8;
365 op
->bytes
+= data
[offset
++];
366 op
->packet
= &data
[offset
];
372 //FIXME malloc checks
375 length
= data
[offset
++] << 8;
376 length
+= data
[offset
++];
377 op
->packet
= realloc (op
->packet
, length
+op
->bytes
);
378 memcpy (op
->packet
+ op
->bytes
, data
+ offset
, length
);
382 length
= data
[offset
++] << 8;
383 length
+= data
[offset
++];
384 op
->packet
= realloc (op
->packet
, length
+op
->bytes
);
385 memcpy (op
->packet
+ op
->bytes
, data
+ offset
, length
);
389 fprintf (stderr
, " unknown frament?!\n");
397 count
+= pkt_repack(ogg
, timestamp
, out
);
399 for (i
= 1; i
< pkts
; i
++)
403 fprintf (stderr
, "payload length overflow. corrupt packet?\n");
406 op
->bytes
= data
[offset
++]<<8;
407 op
->bytes
+= data
[offset
++];
408 op
->packet
= &data
[offset
];
410 count
+= pkt_repack(ogg
, 0, out
);
415 count
= cfg_repack(ogg
, out
);
432 main (int argc
, char *argv
[])
436 int verbose
= 0, dump
= 0, opt
;
437 struct sockaddr_in us
, them
;
438 struct ip_mreq group
;
439 unsigned char data
[MAX_PACKET
];
440 char *hostname
= "227.0.0.1", *filename
= "out.ogg";
441 unsigned int port
= 4044;
444 memset(&ogg
,0,sizeof(ogg_context_t
));
448 fprintf (stderr
, " Vorbis RTP Client (draft-barbato-avt-rtp-theora-00)\n");
450 while ((opt
= getopt (argc
, argv
, "i:p:f:v")) != -1)
462 port
= atoi (optarg
);
476 fprintf (stderr
, "\n|| Unknown option `-%c'.\n", optopt
);
477 fprintf (stderr
, "|| Usage: vorbisrtp-client [-i ip address] [-p port] [-f filename]\n");
482 fprintf (stderr
, "Opening connection to %s port %d\n", hostname
, port
);
484 RTPSocket
= socket (AF_INET
, SOCK_DGRAM
, 0);
488 fprintf (stderr
, "Unable to create socket.\n");
492 us
.sin_family
= AF_INET
;
493 us
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
494 us
.sin_port
= htons (port
);
495 ret
= bind (RTPSocket
, (struct sockaddr
*) &us
, sizeof (us
));
498 fprintf (stderr
, "Unable to bind socket!\n");
502 them
.sin_family
= AF_INET
;
503 them
.sin_addr
.s_addr
= inet_addr (hostname
);
504 them
.sin_port
= htons (port
);
506 if (!IN_MULTICAST (ntohl (them
.sin_addr
.s_addr
)))
508 fprintf (stderr
, "not a multicast address\n");
512 fprintf (stderr
, "joining multicast group...\n");
513 group
.imr_multiaddr
.s_addr
= them
.sin_addr
.s_addr
;
514 group
.imr_interface
.s_addr
= htonl (INADDR_ANY
);
515 ret
= setsockopt (RTPSocket
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
516 (void *) &group
, sizeof (group
));
519 fprintf (stderr
, "cannot join multicast group!\n");
525 if (strcmp (filename
, "-"))
527 file
= fopen (filename
, "wb");
530 fprintf (stderr
, "Unable to open %s\n", filename
);
537 filename
= "Standard Output";
540 fprintf (stderr
, "Dumping the stream to %s\n", filename
);
545 ret
= recvfrom (RTPSocket
, data
, MAX_PACKET
, 0, NULL
, 0);
546 fprintf (stderr
, "read %d bytes of data\n", ret
);
548 if (verbose
) dump_packet_rtp (data
, ret
, stderr
);
550 dump_packet_ogg (data
, ret
, file
, &ogg
);