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 <vorbis/codec.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 last_gp
;
72 unsigned int timestamp
;
75 int param_blockflag
[64];
78 static int rtp_ilog(unsigned int v
){
87 static int icount(unsigned int v
){
97 dump_packet_raw (unsigned char *data
, const int len
, FILE * out
)
104 fprintf (out
, " %04x ", i
);
105 n
= MIN (8, len
- i
);
106 for (j
= 0; j
< n
; j
++)
107 fprintf (out
, " %02x", data
[i
+ j
]);
109 n
= MIN (16, len
- i
);
110 for (j
= 8; j
< 16; j
++)
111 fprintf (out
, " %02x", data
[i
+ j
]);
113 for (j
= 0; j
< n
; j
++)
114 fprintf (out
, "%c", isprint (data
[i
+ j
]) ? data
[i
+ j
] : '.');
123 dump_packet_rtp (unsigned char *data
, const int len
, FILE * out
)
125 int V
, P
, X
, CC
, M
, PT
;
126 unsigned short sequence
;
127 unsigned int timestamp
, ssrc
;
130 int i
, offset
, length
;
132 /* parse RTP header */
133 V
= (data
[0] & 0xc0) >> 6;
134 P
= (data
[0] & 0x40) >> 5;
135 X
= (data
[0] & 0x20) >> 4;
136 CC
= (data
[0] & 0x0f);
137 M
= (data
[1] & 0x80) >> 7;
138 PT
= (data
[1] & 0x7F);
139 sequence
= ntohs (((unsigned short *) data
)[1]);
140 timestamp
= ntohl (((unsigned int *) data
)[1]);
141 ssrc
= ntohl (((unsigned int *) data
)[2]);
143 fprintf (out
, "RTP packet V:%d P:%d X:%d M:%d PT:%d", V
, P
, X
, M
, PT
);
144 fprintf (out
, " seq %d", sequence
);
145 fprintf (out
, " timestamp: %u\n", timestamp
);
146 fprintf (out
, " ssrc: 0x%08x", ssrc
);
148 for (i
= 0; i
< CC
; i
++)
149 fprintf (out
, " csrc: 0x%08x",
150 ntohl (((unsigned int *) data
)[3 + i
]));
152 fprintf (out
, " no csrc");
154 /* offset to payload header */
155 offset
= (3 + CC
) * 4;
157 /* parse Vorbis payload header */
158 ident
= data
[offset
++]<<16;
159 ident
+= data
[offset
++]<<8;
160 ident
+= data
[offset
++];
161 F
= (data
[offset
] & 0xc0) >> 6;
162 VDT
= (data
[offset
] & 0x30) >> 4;
163 pkts
= (data
[offset
] & 0x0F);
166 fprintf(out
,"ident %06x, frag type %d, data type %d, pkts %d, size %d\n",
167 ident
,F
,VDT
,pkts
,len
-4*(CC
+3));
169 for (i
= 0; i
< pkts
; i
++)
173 fprintf (stderr
, "payload length overflow. corrupt packet?\n");
176 length
= data
[offset
++]<<8;
177 length
+= data
[offset
++];
178 fprintf (out
, " data: %d bytes in block %d\n", length
, i
);
183 length
= data
[offset
++]<<8;
184 length
+= data
[offset
++];
185 fprintf (out
, " data: %d bytes in fragment\n", length
);
189 if (len
- offset
> 0)
190 fprintf (out
, " %d unused bytes at the end of the packet!\n",
197 maptype_quantvals(int entries
, int dim
)
199 long vals
=floor(pow((float)entries
,1.f
/dim
));
201 /* the above *should* be reliable, but we'll not assume that FP is
202 ever reliable when bitstream sync is at stake; verify via integer
203 means that vals really is the greatest value of dim for which
204 vals^b->bim <= b->entries */
205 /* treat the above as an initial guess */
214 if(acc
<=entries
&& acc1
>entries
){
226 int cfg_parse( ogg_context_t
*ogg
)
229 int num
, i
, j
, k
=-1, channels
;
232 ogg
->blocksizes
[0] = 1<<(ogg
->op
.packet
[28]&0x0f);
233 ogg
->blocksizes
[1] = 1<<(ogg
->op
.packet
[28]>>4);
234 channels
= ogg
->op
.packet
[11];
236 oggpack_readinit(&opb
, ogg
->op
.packet
+ 30, ogg
->op
.bytes
- 30);
237 if (5 != oggpack_read(&opb
,8)) exit(9);
238 for(i
=6;i
>0;i
--) { oggpack_read(&opb
,8);
241 num
= oggpack_read(&opb
,8)+1;
244 int entries
, quantvals
=-1, maptype
, q_quant
, dim
;
245 oggpack_read(&opb
,24); //magic
246 dim
= oggpack_read(&opb
,16); //dimensions
247 entries
= oggpack_read(&opb
,24); //entries
249 switch(oggpack_read(&opb
,1)){
251 if(oggpack_read(&opb
,1))
253 for(i
=0; i
<entries
; i
++)
254 if(oggpack_read(&opb
,1))
255 oggpack_read(&opb
,5);
258 for(i
=0; i
<entries
; i
++)
259 oggpack_read(&opb
,5);
262 oggpack_read(&opb
,5); //len
264 i
+=oggpack_read(&opb
,rtp_ilog(entries
-i
));
267 switch((maptype
=oggpack_read(&opb
,4))){
272 oggpack_read(&opb
,32);
273 oggpack_read(&opb
,32);
274 q_quant
= oggpack_read(&opb
,4) + 1;
275 oggpack_read(&opb
,1);
279 quantvals
= maptype_quantvals(entries
,dim
);
283 quantvals
= entries
*dim
;
287 for(i
=0;i
<quantvals
;i
++)
288 oggpack_read(&opb
,q_quant
);
295 num
= oggpack_read(&opb
,6)+1;
297 oggpack_read(&opb
,16);
300 num
= oggpack_read(&opb
,6)+1;
302 switch(oggpack_read(&opb
,16)){
305 oggpack_read(&opb
,16);
306 oggpack_read(&opb
,16);
307 oggpack_read(&opb
,16);
308 oggpack_read(&opb
,6);
309 oggpack_read(&opb
,8);
310 for(i
=oggpack_read(&opb
,4)+1;i
>0;i
--)
311 oggpack_read(&opb
,8);
315 int pclass
[31], pdim
[16],
317 partitions
=oggpack_read(&opb
,5);
318 for(i
=0;i
<partitions
;i
++)
320 pclass
[i
]=oggpack_read(&opb
,4);
321 if(k
<pclass
[i
])k
=pclass
[i
];
325 pdim
[j
] = oggpack_read(&opb
,3)+1; // dim
326 subs
=oggpack_read(&opb
,2);
327 if(subs
) oggpack_read(&opb
,8); // book
328 for(i
=0;i
<(1<<subs
);i
++)
329 oggpack_read(&opb
,8);
332 oggpack_read(&opb
,2);
333 rangebits
=oggpack_read(&opb
,4);
334 for(j
=0,k
=0;j
<partitions
;j
++)
336 oggpack_read(&opb
,rangebits
*pdim
[pclass
[j
]]);
342 num
= oggpack_read(&opb
,6)+1;
345 int partitions
,acc
=0;
346 oggpack_read(&opb
,16); // unpack is always the same
347 oggpack_read(&opb
,24); // begin
348 oggpack_read(&opb
,24); // end
349 oggpack_read(&opb
,24); // grouping
350 partitions
=oggpack_read(&opb
,6)+1;
351 oggpack_read(&opb
,8); // groupbook
352 for(j
=0;j
<partitions
;j
++){
353 int cascade
=oggpack_read(&opb
,3);
354 if(oggpack_read(&opb
,1))
355 cascade
|=(oggpack_read(&opb
,5)<<3);
356 acc
+=icount(cascade
);
359 oggpack_read(&opb
,8);
362 num
= oggpack_read(&opb
,6)+1;
366 oggpack_read(&opb
,16); // maps is just mapping0
367 if(oggpack_read(&opb
,1))
368 submaps
=oggpack_read(&opb
,4)+1;
369 if(oggpack_read(&opb
,1))
370 for(i
=oggpack_read(&opb
,8)+1;i
>0;i
--){
371 oggpack_read(&opb
,rtp_ilog(channels
-1));
372 oggpack_read(&opb
,rtp_ilog(channels
-1));
374 oggpack_read(&opb
,2);
376 for(i
=0;i
<channels
;i
++)
377 oggpack_read(&opb
,4);
378 for(i
=0;i
<submaps
;i
++){
379 oggpack_read(&opb
,8);
380 oggpack_read(&opb
,8);
381 oggpack_read(&opb
,8);
386 ogg
->modes
= oggpack_read(&opb
,6)+1;
387 for(i
=0;i
<ogg
->modes
;i
++)
389 ogg
->param_blockflag
[i
] = oggpack_read(&opb
,1); //blockflag
390 oggpack_read(&opb
,16);
391 oggpack_read(&opb
,16);
392 oggpack_read(&opb
,8);
395 return 0; //FIXME add some checks and return -1 on failure
399 long pkt_blocksize(ogg_context_t
*ogg
)
403 oggpack_readinit(&opb
,ogg
->op
.packet
,ogg
->op
.bytes
);
405 oggpack_read(&opb
,1);
407 mode
= oggpack_read(&opb
,rtp_ilog(ogg
->modes
));
409 return ogg
->blocksizes
[ogg
->param_blockflag
[mode
]];
414 cfg_repack(ogg_context_t
*ogg
, FILE* out
)
417 unsigned char comment
[] =
418 /*quite minimal comment*/
419 { 3,'v','o','r','b','i','s',
421 'v','o','r','b','i','s','-','r','t','p',
425 /* get the identification packet*/
426 id
.packet
= ogg
->op
.packet
;
433 /* get the comment packet*/
435 co
.bytes
= sizeof(comment
);
440 /* get the setup packet*/
441 cb
.packet
= ogg
->op
.packet
+ 30;
442 cb
.bytes
= ogg
->op
.bytes
- 30;
447 /* get the sample rate from the info packet */
448 ogg
->vi
.rate
=id
.packet
[12];
449 ogg
->vi
.rate
+=id
.packet
[12+1]<<8;
450 ogg
->vi
.rate
+=id
.packet
[12+2]<<8;
451 ogg
->vi
.rate
+=id
.packet
[12+3]<<24;
456 fprintf(stderr
,"parsed: rate %ld, blocksizes %ld %ld, blockflags ",
463 for (i
=0;i
<ogg
->modes
;i
++)
464 fprintf(stderr
," %d ", ogg
->param_blockflag
[i
]);
465 fprintf(stderr
,"\n");
468 vorbis_info_init(&ogg
->vi
);
469 vorbis_comment_init(&ogg
->vc
);
470 if(vorbis_synthesis_headerin(&ogg
->vi
,&ogg
->vc
,&id
)<0){
471 /* error case; not a vorbis header */
472 fprintf(stderr
,"Not valid identification\n");
473 } else fprintf(stderr
," Valid identification\n");
474 if(vorbis_synthesis_headerin(&ogg
->vi
,&ogg
->vc
,&co
)<0){
475 /* error case; not a vorbis header */
476 fprintf(stderr
,"Not valid comment\n");
477 } else fprintf(stderr
," Valid comment\n");
478 if(vorbis_synthesis_headerin(&ogg
->vi
,&ogg
->vc
,&cb
)<0){
479 /* error case; not a vorbis header */
480 fprintf(stderr
,"Not valid setup\n");
481 } else fprintf(stderr
," Valid setup\n");
482 fprintf(stderr
,"decoded: rate %ld\n",ogg
->vi
.rate
);
485 ogg_stream_init(&ogg
->os
,rand());
487 ogg_stream_packetin(&ogg
->os
,&id
);
488 ogg_stream_packetin(&ogg
->os
,&co
);
489 ogg_stream_packetin(&ogg
->os
,&cb
);
492 int result
=ogg_stream_flush(&ogg
->os
,&ogg
->og
);
494 fwrite(ogg
->og
.header
,1,ogg
->og
.header_len
,out
);
495 fwrite(ogg
->og
.body
,1,ogg
->og
.body_len
,out
);
496 }while(!ogg_page_eos(&ogg
->og
));
501 pkt_repack(ogg_context_t
*ogg
, FILE *out
){
502 ogg_stream_packetin(&ogg
->os
,&ogg
->op
);
504 int result
=ogg_stream_pageout(&ogg
->os
,&ogg
->og
);
506 fwrite(ogg
->og
.header
,1,ogg
->og
.header_len
,out
);
507 fwrite(ogg
->og
.body
,1,ogg
->og
.body_len
,out
);
508 }while(!ogg_page_eos(&ogg
->og
));
514 dump_packet_ogg (unsigned char *data
, const int len
, FILE * out
, ogg_context_t
*ogg
)
516 int V
, P
, X
, CC
, M
, PT
;
517 unsigned short sequence
;
518 unsigned int timestamp
, ssrc
;
521 int i
, offset
, length
, count
= 0;
522 ogg_packet
*op
= &ogg
->op
;
524 /* parse RTP header */
525 V
= (data
[0] & 0xc0) >> 6;
526 P
= (data
[0] & 0x40) >> 5;
527 X
= (data
[0] & 0x20) >> 4;
528 CC
= (data
[0] & 0x0f);
529 M
= (data
[1] & 0x80) >> 7;
530 PT
= (data
[1] & 0x7F);
531 sequence
= ntohs (((unsigned short *) data
)[1]);
532 //FIXME not exactly ideal
533 timestamp
= ntohl (((unsigned int *) data
)[1]) - ogg
->timestamp
;
534 ogg
->timestamp
= ntohl (((unsigned int *) data
)[1]);
535 ssrc
= ntohl (((unsigned int *) data
)[2]);
536 /* offset to payload header */
537 offset
= (3 + CC
) * 4;
539 /* parse Vorbis payload header */
540 ident
= data
[offset
++]<<16;
541 ident
+= data
[offset
++]<<8;
542 ident
+= data
[offset
++];
543 F
= (data
[offset
] & 0xc0) >> 6;
544 VDT
= (data
[offset
] & 0x30) >> 4;
545 pkts
= (data
[offset
] & 0x0F);
551 op
->bytes
= data
[offset
++]<<8;
552 op
->bytes
+= data
[offset
++];
553 op
->packet
= &data
[offset
];
561 length
= data
[offset
++] << 8;
562 length
+= data
[offset
++];
563 op
->packet
= realloc (op
->packet
, length
+op
->bytes
);
564 memcpy (op
->packet
+ op
->bytes
, data
+ offset
, length
);
568 length
= data
[offset
++] << 8;
569 length
+= data
[offset
++];
570 op
->packet
= realloc (op
->packet
, length
+op
->bytes
);
571 memcpy (op
->packet
+ op
->bytes
, data
+ offset
, length
);
576 fprintf (stderr
, " unknown frament?!\n");
583 ogg
->curr_bs
= pkt_blocksize(ogg
);
585 op
->granulepos
+= (ogg
->curr_bs
+ ogg
->prev_bs
)/4;
586 ogg
->prev_bs
= ogg
->curr_bs
;
587 count
+= pkt_repack(ogg
,out
);
588 for (i
= 1; i
< pkts
; i
++)
592 fprintf (stderr
, "payload length overflow. corrupt packet?\n");
595 op
->bytes
= data
[offset
++]<<8;
596 op
->bytes
+= data
[offset
++];
597 op
->packet
= &data
[offset
];
600 ogg
->curr_bs
= pkt_blocksize(ogg
);
602 op
->granulepos
+= (ogg
->curr_bs
+ ogg
->prev_bs
)/4;
604 fprintf(stderr
,"gp %lld, d_bs %ld, p_bs %ld, pno %lld\n", op
->granulepos
, ogg
->curr_bs
, pkt_blocksize(ogg
), op
->packetno
);
606 ogg
->prev_bs
= ogg
->curr_bs
;
607 count
+= pkt_repack(ogg
,out
);
613 count
= cfg_repack(ogg
, out
);
629 main (int argc
, char *argv
[])
633 int verbose
= 0, dump
= 0, opt
;
634 struct sockaddr_in us
, them
;
635 struct ip_mreq group
;
636 unsigned char data
[MAX_PACKET
];
637 char *hostname
= "227.0.0.1", *filename
= "out.ogg";
638 unsigned int port
= 4044;
641 memset(&ogg
,0,sizeof(ogg_context_t
));
643 "||---------------------------------------------------------------------------||\n");
645 fprintf (stderr
, "|| Vorbis RTP Client (draft-ietf-avt-rtp-vorbis-00)\n");
647 while ((opt
= getopt (argc
, argv
, "i:p:f:v")) != -1)
659 port
= atoi (optarg
);
673 fprintf (stderr
, "\n|| Unknown option `-%c'.\n", optopt
);
674 fprintf (stderr
, "|| Usage: vorbisrtp-client [-i ip address] [-p port] [-f filename]\n");
679 fprintf (stderr
, "Opening connection to %s port %d\n", hostname
, port
);
681 RTPSocket
= socket (AF_INET
, SOCK_DGRAM
, 0);
685 fprintf (stderr
, "Unable to create socket.\n");
689 us
.sin_family
= AF_INET
;
690 us
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
691 us
.sin_port
= htons (port
);
692 ret
= bind (RTPSocket
, (struct sockaddr
*) &us
, sizeof (us
));
695 fprintf (stderr
, "Unable to bind socket!\n");
699 them
.sin_family
= AF_INET
;
700 them
.sin_addr
.s_addr
= inet_addr (hostname
);
701 them
.sin_port
= htons (port
);
703 if (!IN_MULTICAST (ntohl (them
.sin_addr
.s_addr
)))
705 fprintf (stderr
, "not a multicast address\n");
709 fprintf (stderr
, "joining multicast group...\n");
710 group
.imr_multiaddr
.s_addr
= them
.sin_addr
.s_addr
;
711 group
.imr_interface
.s_addr
= htonl (INADDR_ANY
);
712 ret
= setsockopt (RTPSocket
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
713 (void *) &group
, sizeof (group
));
716 fprintf (stderr
, "cannot join multicast group!\n");
722 if (strcmp (filename
, "-"))
724 file
= fopen (filename
, "wb");
727 fprintf (stderr
, "Unable to open %s\n", filename
);
734 filename
= "Standard Output";
737 fprintf (stderr
, "Dumping the stream to %s\n", filename
);
742 ret
= recvfrom (RTPSocket
, data
, MAX_PACKET
, 0, NULL
, 0);
743 fprintf (stderr
, "read %d bytes of data\n", ret
);
745 if (verbose
) dump_packet_rtp (data
, ret
, stderr
);
747 dump_packet_ogg (data
, ret
, file
, &ogg
);