Copy the libogg bitpacker directly into libtheoradec.
[xiph/unicode.git] / xiph-rtp / theorartp-client.c
blobbd46bf50981873c02c99ab298f89658084b4674e
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 <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 {
59 ogg_packet op;
60 ogg_stream_state os;
61 ogg_page og;
62 long long int prev_gp;
63 long int prev_keyframe;
64 long int curr_frame;
66 theora_info ti;
67 theora_comment tc;
68 theora_state td;
70 long time_den;
71 long time_num;
73 int frag;
74 unsigned int timestamp;
76 int gp_shift;
78 } ogg_context_t;
80 int
81 dump_packet_raw (unsigned char *data, const int len, FILE * out)
83 int i, j, n;
85 i = 0;
86 while (i < len)
88 fprintf (out, " %04x ", i);
89 n = MIN (8, len - i);
90 for (j = 0; j < n; j++)
91 fprintf (out, " %02x", data[i + j]);
92 fprintf (out, " ");
93 n = MIN (16, len - i);
94 for (j = 8; j < 16; j++)
95 fprintf (out, " %02x", data[i + j]);
96 fprintf (out, " ");
97 for (j = 0; j < n; j++)
98 fprintf (out, "%c", isprint (data[i + j]) ? data[i + j] : '.');
99 fprintf (out, "\n");
100 i += 16;
103 return 0;
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;
112 unsigned int ident;
113 int F, VDT, pkts;
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);
131 if (CC)
132 for (i = 0; i < CC; i++)
133 fprintf (out, " csrc: 0x%08x",
134 ntohl (((unsigned int *) data)[3 + i]));
135 else
136 fprintf (out, " no csrc");
137 fprintf (out, "\n");
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);
148 offset++;
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++)
155 if (offset >= len)
157 fprintf (stderr, "payload length overflow. corrupt packet?\n");
158 return -1;
160 length = data[offset++]<<8;
161 length += data[offset++];
162 fprintf (out, " data: %d bytes in block %d\n", length, i);
163 offset += length;
165 if (pkts == 0)
167 length = data[offset++]<<8;
168 length += data [offset++];
169 fprintf (out, " data: %d bytes in fragment\n", length);
170 offset += length;
173 if (len - offset > 0)
174 fprintf (out, " %d unused bytes at the end of the packet!\n",
175 len - offset);
177 return 0;
180 int cfg_parse( ogg_context_t *ogg )
182 oggpack_buffer opb;
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);
193 ogg->time_den =
194 oggpackB_read(&opb,32);
195 ogg->time_num =
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)
211 ogg_packet id,co,cb;
212 unsigned char comment[] =
213 /*quite minimal comment*/
214 { 0x81,'t','h','e','o','r','a',
215 10,0,0,0,
216 't','h','e','o','r','a','-','r','t','p',
217 0,0,0,0};
219 /* get the identification packet*/
220 id.packet = ogg->op.packet;
221 id.bytes = 42;
222 id.b_o_s = 1;
223 id.e_o_s = 0;
224 id.granulepos = 0;
227 /* get the comment packet*/
228 co.packet = comment;
229 co.bytes = sizeof(comment);
230 co.b_o_s = 0;
231 co.e_o_s = 0;
232 co.granulepos = 0;
234 /* get the setup packet*/
235 cb.packet = ogg->op.packet + 42;
236 cb.bytes = ogg->op.bytes - 42;
237 cb.b_o_s = 0;
238 cb.e_o_s = 0;
239 cb.granulepos = 0;
241 cfg_parse(ogg);
243 #if CHECK
244 //FIXME add some checks
246 #endif
247 /* start the ogg*/
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);
256 if(result==0)break;
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));
260 return 3;
264 pkt_repack(ogg_context_t *ogg, unsigned int timestamp, FILE *out){
266 int frame_type;
267 ogg_packet *op = &ogg->op;
269 oggpack_buffer opb;
270 oggpack_readinit(&opb,op->packet,op->bytes);
271 oggpack_read(&opb,1); //video marker
273 frame_type = oggpackB_read(&opb,1);
274 timestamp/=90000;
275 timestamp*=ogg->time_num/ogg->time_den;
277 if(timestamp)
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;
287 else
289 if(ogg->prev_gp==-1)
291 op->granulepos = 0;
293 else
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;
303 else
304 op->granulepos++;
308 ogg->prev_gp = op->granulepos;
310 #ifdef DEBUG
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 );
312 #endif
315 ogg_stream_packetin(&ogg->os,&ogg->op);
317 int result=ogg_stream_pageout(&ogg->os,&ogg->og);
318 if(result==0)break;
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));
322 return 1;
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;
332 unsigned int ident;
333 int F, VDT, pkts;
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);
359 offset++;
361 switch (F) {
363 case 0:
364 op->bytes = data[offset++]<<8;
365 op->bytes += data[offset++];
366 op->packet = &data[offset];
367 op->packetno++;
368 offset += op->bytes;
369 break;
370 case 1:
371 op->bytes = 0;
372 //FIXME malloc checks
373 op->packet = NULL;
374 case 2:
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);
379 op->bytes += length;
380 return 0;
381 case 3:
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);
386 op->bytes += length;
387 break;
388 default:
389 fprintf (stderr, " unknown frament?!\n");
390 return 0;
393 switch (VDT) {
395 case 0:
397 count += pkt_repack(ogg, timestamp, out);
399 for (i = 1; i < pkts; i++)
401 if (offset >= len)
403 fprintf (stderr, "payload length overflow. corrupt packet?\n");
404 return -1;
406 op->bytes = data[offset++]<<8;
407 op->bytes += data[offset++];
408 op->packet = &data[offset];
409 op->packetno++;
410 count += pkt_repack(ogg, 0, out);
411 offset += op->bytes;
413 break;
414 case 1:
415 count = cfg_repack(ogg, out);
416 op->packetno+=3;
417 break;
418 default:
419 //ignore
420 break;
423 if ( F == 3 )
425 free(op->packet);
426 op->packet = NULL;
428 return count;
432 main (int argc, char *argv[])
434 int RTPSocket, ret;
435 FILE *file=stdout;
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;
443 ogg_context_t ogg;
444 memset(&ogg,0,sizeof(ogg_context_t));
445 ogg.prev_gp=-1;
448 fprintf (stderr, " Vorbis RTP Client (draft-barbato-avt-rtp-theora-00)\n");
450 while ((opt = getopt (argc, argv, "i:p:f:v")) != -1)
452 switch (opt)
455 /* Set IP address */
456 case 'i':
457 hostname = optarg;
458 break;
460 /* Set port */
461 case 'p':
462 port = atoi (optarg);
463 break;
465 /* Set TTL value */
466 case 'f':
467 filename = optarg;
468 dump = 1;
469 break;
470 case 'v':
471 verbose=1;
472 break;
474 /* Unknown option */
475 case '?':
476 fprintf (stderr, "\n|| Unknown option `-%c'.\n", optopt);
477 fprintf (stderr, "|| Usage: vorbisrtp-client [-i ip address] [-p port] [-f filename]\n");
478 return 1;
482 fprintf (stderr, "Opening connection to %s port %d\n", hostname, port);
484 RTPSocket = socket (AF_INET, SOCK_DGRAM, 0);
486 if (RTPSocket < 0)
488 fprintf (stderr, "Unable to create socket.\n");
489 exit (1);
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));
496 if (ret < 0)
498 fprintf (stderr, "Unable to bind socket!\n");
499 exit (1);
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");
510 else
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));
517 if (ret < 0)
519 fprintf (stderr, "cannot join multicast group!\n");
520 exit (1);
523 if (dump)
525 if (strcmp (filename, "-"))
527 file = fopen (filename, "wb");
528 if (file == NULL)
530 fprintf (stderr, "Unable to open %s\n", filename);
531 exit (1);
534 else
536 file = stdout;
537 filename = "Standard Output";
540 fprintf (stderr, "Dumping the stream to %s\n", filename);
543 while (1)
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);
549 if (dump){
550 dump_packet_ogg (data, ret, file, &ogg);
551 fflush(NULL);
555 return 0;