Recognizes if input is ogg or not.
[xiph.git] / xiph-rtp / vorbisrtp-client.c
blobdfc77e088eae7ba415ebc1ce41c0aca1a0c51796
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
5 are met:
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*/
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <unistd.h>
42 #include <time.h>
43 #include <math.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
50 #include <ogg/ogg.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 {
59 ogg_packet op;
60 ogg_stream_state os;
61 ogg_page og;
62 long long int last_gp;
63 long int prev_bs;
64 long int curr_bs;
66 vorbis_info vi;
67 vorbis_comment vc;
68 vorbis_dsp_state vd;
69 vorbis_block vb;
70 int frag;
71 float **pcm;
72 unsigned int timestamp;
73 int modes;
74 long blocksizes[2];
75 int param_blockflag[64];
76 } ogg_context_t;
78 static int rtp_ilog(unsigned int v){
79 int ret=0;
80 while(v){
81 ret++;
82 v>>=1;
84 return(ret);
87 static int icount(unsigned int v){
88 int ret=0;
89 while(v){
90 ret+=v&1;
91 v>>=1;
93 return(ret);
96 int
97 dump_packet_raw (unsigned char *data, const int len, FILE * out)
99 int i, j, n;
101 i = 0;
102 while (i < len)
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]);
108 fprintf (out, " ");
109 n = MIN (16, len - i);
110 for (j = 8; j < 16; j++)
111 fprintf (out, " %02x", data[i + j]);
112 fprintf (out, " ");
113 for (j = 0; j < n; j++)
114 fprintf (out, "%c", isprint (data[i + j]) ? data[i + j] : '.');
115 fprintf (out, "\n");
116 i += 16;
119 return 0;
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;
128 unsigned int ident;
129 int F, VDT, pkts;
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);
147 if (CC)
148 for (i = 0; i < CC; i++)
149 fprintf (out, " csrc: 0x%08x",
150 ntohl (((unsigned int *) data)[3 + i]));
151 else
152 fprintf (out, " no csrc");
153 fprintf (out, "\n");
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);
164 offset++;
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++)
171 if (offset >= len)
173 fprintf (stderr, "payload length overflow. corrupt packet?\n");
174 return -1;
176 length = data[offset++]<<8;
177 length += data[offset++];
178 fprintf (out, " data: %d bytes in block %d\n", length, i);
179 offset += length;
181 if (pkts == 0)
183 length = data[offset++]<<8;
184 length += data [offset++];
185 fprintf (out, " data: %d bytes in fragment\n", length);
186 offset += length;
189 if (len - offset > 0)
190 fprintf (out, " %d unused bytes at the end of the packet!\n",
191 len - offset);
193 return 0;
196 long
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 */
206 while(1){
207 long acc=1;
208 long acc1=1;
209 int i;
210 for(i=0;i<dim;i++){
211 acc*=vals;
212 acc1*=vals+1;
214 if(acc<=entries && acc1>entries){
215 return(vals);
216 }else{
217 if(acc>entries){
218 vals--;
219 }else{
220 vals++;
226 int cfg_parse( ogg_context_t *ogg )
228 oggpack_buffer opb;
229 int num, i, j, k=-1, channels;
231 //id packet
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];
235 //cb packet
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;
242 for(;num>0;num--)
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)){
250 case 0: //unordered
251 if(oggpack_read(&opb,1))
253 for(i=0; i<entries; i++)
254 if(oggpack_read(&opb,1))
255 oggpack_read(&opb,5);
257 else
258 for(i=0; i<entries; i++)
259 oggpack_read(&opb,5);
260 break;
261 case 1: //ordered
262 oggpack_read(&opb,5); //len
263 for(i=0;i<entries;)
264 i+=oggpack_read(&opb,rtp_ilog(entries-i));
265 break;
267 switch((maptype=oggpack_read(&opb,4))){
268 case 0:
269 break;
271 case 1: case 2:
272 oggpack_read(&opb,32);
273 oggpack_read(&opb,32);
274 q_quant = oggpack_read(&opb,4) + 1;
275 oggpack_read(&opb,1);
277 switch (maptype){
278 case 1:
279 quantvals = maptype_quantvals(entries,dim);
280 break;
282 case 2:
283 quantvals = entries*dim;
284 break;
287 for(i=0;i<quantvals;i++)
288 oggpack_read(&opb,q_quant);
289 break;
294 //times
295 num = oggpack_read(&opb,6)+1;
296 for(;num>0;num--)
297 oggpack_read(&opb,16);
299 //floors
300 num = oggpack_read(&opb,6)+1;
301 for(;num>0;num--)
302 switch(oggpack_read(&opb,16)){
304 case 0:
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);
312 break;
313 case 1:
315 int pclass[31], pdim[16],
316 rangebits,
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];
323 for(j=0;j<k+1;j++){
324 int subs;
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]]);
341 //residues
342 num = oggpack_read(&opb,6)+1;
343 for(;num>0;num--)
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);
358 for(j=0;j<acc;j++)
359 oggpack_read(&opb,8);
361 //maps
362 num = oggpack_read(&opb,6)+1;
363 for(;num>0;num--)
365 int submaps=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);
375 if(submaps>1)
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);
385 //modes
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)
401 int mode;
402 oggpack_buffer opb;
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)
416 ogg_packet id,co,cb;
417 unsigned char comment[] =
418 /*quite minimal comment*/
419 { 3,'v','o','r','b','i','s',
420 10,0,0,0,
421 'v','o','r','b','i','s','-','r','t','p',
422 0,0,0,0,
425 /* get the identification packet*/
426 id.packet = ogg->op.packet;
427 id.bytes = 30;
428 id.b_o_s = 1;
429 id.e_o_s = 0;
430 id.granulepos = 0;
433 /* get the comment packet*/
434 co.packet = comment;
435 co.bytes = sizeof(comment);
436 co.b_o_s = 0;
437 co.e_o_s = 0;
438 co.granulepos = 0;
440 /* get the setup packet*/
441 cb.packet = ogg->op.packet + 30;
442 cb.bytes = ogg->op.bytes - 30;
443 cb.b_o_s = 0;
444 cb.e_o_s = 0;
445 cb.granulepos = 0;
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;
453 cfg_parse(ogg);
455 #if CHECK
456 fprintf(stderr,"parsed: rate %ld, blocksizes %ld %ld, blockflags ",
457 ogg->vi.rate,
458 ogg->blocksizes[0],
459 ogg->blocksizes[1]
462 int i;
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);
483 #endif
484 /* start the ogg*/
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);
490 // ogg->op.b_o_s=1;
492 int result=ogg_stream_flush(&ogg->os,&ogg->og);
493 if(result==0)break;
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));
497 return 3;
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);
505 if(result==0)break;
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));
509 return 1;
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;
519 unsigned int ident;
520 int F, VDT, pkts;
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);
546 offset++;
548 switch (F) {
550 case 0:
551 op->bytes = data[offset++]<<8;
552 op->bytes += data[offset++];
553 op->packet = &data[offset];
554 op->packetno++;
555 offset += op->bytes;
556 break;
557 case 1:
558 op->bytes = 0;
559 op->packet = NULL;
560 case 2:
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);
565 op->bytes += length;
566 return 0;
567 case 3:
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);
572 op->bytes += length;
573 pkts=1;
574 break;
575 default:
576 fprintf (stderr, " unknown frament?!\n");
577 return 0;
580 switch (VDT) {
582 case 0:
583 ogg->curr_bs = pkt_blocksize(ogg);
584 if(ogg->prev_bs)
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++)
590 if (offset >= len)
592 fprintf (stderr, "payload length overflow. corrupt packet?\n");
593 return -1;
595 op->bytes = data[offset++]<<8;
596 op->bytes += data[offset++];
597 op->packet = &data[offset];
598 op->packetno++;
600 ogg->curr_bs = pkt_blocksize(ogg);
601 if(ogg->prev_bs)
602 op->granulepos += (ogg->curr_bs + ogg->prev_bs)/4;
603 #if DEBUG
604 fprintf(stderr,"gp %lld, d_bs %ld, p_bs %ld, pno %lld\n", op->granulepos, ogg->curr_bs, pkt_blocksize(ogg), op->packetno);
605 #endif
606 ogg->prev_bs = ogg->curr_bs;
607 count += pkt_repack(ogg,out);
608 offset += op->bytes;
609 op->b_o_s=0;
611 break;
612 case 1:
613 count = cfg_repack(ogg, out);
614 break;
615 default:
616 //ignore
617 break;
620 if ( F == 3 )
622 free(op->packet);
623 op->packet = NULL;
625 return count;
629 main (int argc, char *argv[])
631 int RTPSocket, ret;
632 FILE *file=stdout;
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;
640 ogg_context_t ogg;
641 memset(&ogg,0,sizeof(ogg_context_t));
642 fprintf (stderr,
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)
649 switch (opt)
652 /* Set IP address */
653 case 'i':
654 hostname = optarg;
655 break;
657 /* Set port */
658 case 'p':
659 port = atoi (optarg);
660 break;
662 /* Set TTL value */
663 case 'f':
664 filename = optarg;
665 dump = 1;
666 break;
667 case 'v':
668 verbose=1;
669 break;
671 /* Unknown option */
672 case '?':
673 fprintf (stderr, "\n|| Unknown option `-%c'.\n", optopt);
674 fprintf (stderr, "|| Usage: vorbisrtp-client [-i ip address] [-p port] [-f filename]\n");
675 return 1;
679 fprintf (stderr, "Opening connection to %s port %d\n", hostname, port);
681 RTPSocket = socket (AF_INET, SOCK_DGRAM, 0);
683 if (RTPSocket < 0)
685 fprintf (stderr, "Unable to create socket.\n");
686 exit (1);
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));
693 if (ret < 0)
695 fprintf (stderr, "Unable to bind socket!\n");
696 exit (1);
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");
707 else
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));
714 if (ret < 0)
716 fprintf (stderr, "cannot join multicast group!\n");
717 exit (1);
720 if (dump)
722 if (strcmp (filename, "-"))
724 file = fopen (filename, "wb");
725 if (file == NULL)
727 fprintf (stderr, "Unable to open %s\n", filename);
728 exit (1);
731 else
733 file = stdout;
734 filename = "Standard Output";
737 fprintf (stderr, "Dumping the stream to %s\n", filename);
740 while (1)
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);
746 if (dump){
747 dump_packet_ogg (data, ret, file, &ogg);
748 fflush(NULL);
752 return 0;