getstream2-20100616/0000755000372000037200000000000011406140513012645 5ustar florfcorggetstream2-20100616/output_http.c0000664000372000037200000000607411406140020015412 0ustar florfcorg #include #include #include #include #include #include "output.h" #include "simplebuffer.h" #include "libhttp.h" extern struct http_server *hserver; static void output_remove_receiver(struct http_receiver_s *hr) { struct output_s *o=hr->output; struct http_connection *hc=hr->hc; o->http_receiver=g_list_remove(o->http_receiver, hr); o->receiver--; logwrite(LOG_INFO, "stream_http: dropping connection to %s for %s", inet_ntoa(hc->sin.sin_addr), hc->url); http_drop_connection(hc); free(hr); } static int output_cb_http(struct http_connection *hc, int cbtype, void *arg) { switch(cbtype) { case(HCB_QUERY): { struct http_receiver_s *hr; struct output_s *o=arg; /* HCB_QUERY returns http_url args */ hr=calloc(1, sizeof(struct http_receiver_s)); hr->hc=hc; hr->output=o; /* Put into stream output list */ o->http_receiver=g_list_append(o->http_receiver, hr); o->receiver++; /* Store http_receiver into http_connection structure */ hc->arg=hr; /* Return head */ http_header_start(hc, "200 OK", "application/octet-stream"); http_header_nocache(hc); http_header_clength(hc, -1); http_header_end(hc); logwrite(LOG_INFO, "stream_http: connection from %s for %s", inet_ntoa(hc->sin.sin_addr), hc->url); break; } case(HCB_ERROR): { output_remove_receiver(hc->arg); break; } } return 1; } #define HTTP_MAX_TS (20000/TS_PACKET_SIZE) int output_init_http(struct output_s *o) { o->buffer=sb_init(HTTP_MAX_TS, TS_PACKET_SIZE, 0); if (o->buffer == NULL) return 0; o->hurl=calloc(2, sizeof(struct http_url)); o->hurl->url=o->url; o->hurl->cb=output_cb_http; o->hurl->arg=(void *) o; http_register_url(hserver, o->hurl); return 0; } #define HTTP_MAX_QUEUED (200*1024) #define HTTP_MAX_OVERFLOW 20 void output_send_http_one(gpointer data, gpointer user_data) { struct http_receiver_s *hr=data; struct output_s *o=user_data; /* * Check how many bytes we already have queued on this * HTTP connection. Users might connect from low bandwidth * links not beeing able to transmit the full feed. We must * avoid consuming all memory. * * If the situation persists too long we drop the connection * */ if (http_get_queue(hr->hc) > HTTP_MAX_QUEUED) { hr->overflow++; if (hr->overflow > HTTP_MAX_OVERFLOW) output_remove_receiver(hr); return; } hr->overflow=0; /* * We cant reuse evbuffer as they are empty after * passing the the buffevent layer - Thus we recreate * the buffer every time we send it out. This involes * a little more memcpy as really necessary but for now * its enough * */ http_return_stream(hr->hc, sb_bufptr(o->buffer), sb_buflen(o->buffer)); } void output_send_http(struct output_s *o, uint8_t *tsp) { sb_add_atoms(o->buffer, tsp, 1); if (!sb_free_atoms(o->buffer)) { /* * If the output buffer is full - loop on all http sesssions * and send out the buffer as a http chunk */ g_list_foreach(o->http_receiver, output_send_http_one, (gpointer) o); sb_zap(o->buffer); } } getstream2-20100616/configs/0000775000372000037200000000000011406140020014270 5ustar florfcorggetstream2-20100616/configs/config-astra-transponder-11-hdtv0000664000372000037200000000153211406140020022310 0ustar florfcorg http { port 8001; }; adapter 0 { packet-buffer 50; stat-interval 120; dvb-s2 { lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 11362000; polarisation h; symbol-rate 22000000; }; }; stream { name "Das Erste HD"; input { pnr 11100; }; output-udp { remote-address 239.0.99.1; remote-port 3000; sap { scope global; ttl 4; playgroup "HD Test"; }; }; }; stream { name "ZDF HD"; input { pnr 11110; }; output-udp { remote-address 239.0.99.2; remote-port 3000; sap { scope global; ttl 4; playgroup "HD Test"; }; }; }; stream { name "ARTE HD"; input { pnr 11120; }; output-udp { remote-address 239.0.99.3; remote-port 3000; sap { scope global; ttl 4; playgroup "HD Test"; }; }; }; }; getstream2-20100616/configs/config-multiadapter0000664000372000037200000000125011406140020020147 0ustar florfcorg http { port 8001; }; adapter 0 { packet-buffer 50; stat-interval 120; dvb-s { lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 11836500; polarisation h; symbol-rate 27500000; }; }; stream { name "Das Erste"; input { pnr 28106; }; output-http { url /tv/daserste; }; }; }; adapter 1 { packet-buffer 50; stat-interval 120; dvb-s { lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 12266000; polarisation h; symbol-rate 27500000; }; }; stream { name 88acht; input { pnr 0x6f27; }; output-http { url /radio/88acht; }; }; }; getstream2-20100616/configs/config-astra-transponder-930000664000372000037200000001572511406140020021370 0ustar florfcorg http { port 8001; }; adapter 1 { packet-buffer 50; stat-interval 120; stuck-interval 200; dvb-s { lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 12266000; polarisation h; symbol-rate 27500000; }; }; # stream { # name epg; # input { # pid 0x10; # pid 0x11; # pid 0x12; # }; # output-pipe { # filename "/tmp/epg"; # }; # }; stream { name 88acht; input { pnr 0x6f27; }; output-http { url /radio/88acht; }; # output-pipe { # filename "/tmp/88acht"; # }; }; stream { name "Antenne Brandenburg"; input { pnr 28454; }; output-http { url /radio/antennebrandenburg; }; }; stream { name "B5 Aktuell"; input { pnr 28404; }; output-http { url "/radio/b5aktuell"; }; }; stream { name "Bayern 1"; input { pnr 28400; }; output-http { url "/radio/bayern1"; }; }; stream { name "Bayern 2"; input { pnr 28401; }; output-http { url "/radio/bayern2"; }; }; stream { name "Bayern 3"; input { pnr 28402; }; output-http { url "/radio/bayern3"; }; }; stream { name "Bayern 4 Klassik"; input { pnr 28403; }; output-http { url "/radio/bayern4klassik"; }; }; stream { name "Bayern Mobil"; input { pnr 28405; }; output-http { url "/radio/bayernmobil"; }; }; stream { name "Bremen Eins"; input { pnr 28448; }; output-http { url "/radio/bremeneins"; }; }; stream { name "Bremen Vier"; input { pnr 28450; }; output-http { url "/radio/bremenvier"; }; }; stream { name "BR Verkehr"; input { pnr 28407; }; output-http { url "/radio/brverkehr"; }; }; stream { name "Das Ding"; input { pnr 28471; }; output-http { url "/radio/dasding"; }; }; stream { name "Das Modul"; input { pnr 28406; }; output-http { url "/radio/dasmodul"; }; }; stream { name "Deutsche Welle 1"; input { pnr 28416; }; output-http { url "/radio/deutschewelle1"; }; }; stream { name "Deutsche Welle 4"; input { pnr 28417; }; output-http { url "/radio/deutschewelle4"; }; }; stream { name "Eins Live"; input { pnr 28475; }; output-http { url "/radio/einslive"; }; output-udp { remote-address 239.0.71.1; remote-port 3000; sap { scope global; ttl 4; playgroup "ARD Radio"; }; }; }; stream { name "Eins Live diggi"; input { pnr 0x6f41; }; output-http { url "/radio/einslivediggi"; }; }; stream { name "Fritz!"; input { pnr 28457; }; output-http { url "/radio/fritz"; }; }; stream { name "hr1"; input { pnr 28419; }; output-http { url "/radio/hr1"; }; }; stream { name "hr2"; input { pnr 28420; }; output-http { url "/radio/hr2"; }; }; stream { name "hr3"; input { pnr 28421; }; output-http { url "/radio/hr3"; }; }; stream { name "hr4"; input { pnr 28422; }; output-http { url "/radio/hr4"; }; }; stream { name "hr info am"; input { pnr 28425; }; output-http { url "/radio/hrinfoam"; }; }; stream { name "hr info fm"; input { pnr 28424; }; output-http { url "/radio/hrinfofm"; }; }; stream { name "Info Radio"; input { pnr 28452; }; output-http { url "/radio/inforadio"; }; }; stream { name "Jump"; input { pnr 28432; }; output-http { url "/radio/jump"; }; }; stream { name "MDR 1"; input { pnr 28428; }; output-http { url "/radio/mdr1"; }; }; # More MDR1 inbetween stream { name "MDR Figaro"; input { pnr 28431; }; output-http { url "/radio/mdrfigaro"; }; }; stream { name "MDR Info"; input { pnr 28434; }; output-http { url "/radio/mdrinfo"; }; }; stream { name "NDR 1"; input { pnr 28444; }; output-http { url "/radio/ndr1"; }; }; stream { name "NDR 2"; input { pnr 28437; }; output-http { url "/radio/ndr2"; }; }; stream { name "NDR 90,3"; input { pnr 28441; }; output-http { url "/radio/ndr903"; }; }; stream { name "NDR Info"; input { pnr 28439; }; output-http { url "/radio/ndrinfo"; }; }; stream { name "NDR Info Spezial"; input { pnr 28445; }; output-http { url "/radio/ndrinfospezial"; }; }; stream { name "NDR Kultur"; input { pnr 28438; }; output-http { url "/radio/ndrkultur"; }; }; stream { name "NordWestRadio"; input { pnr 28449; }; output-http { url "/radio/nordwestradio"; }; }; stream { name "N-Joy"; input { pnr 28440; }; output-http { url "/radio/njoy"; }; }; stream { name "Radio Eins"; input { pnr 28456; }; output-http { url "/radio/radioeins"; }; }; stream { name "RBB Kulturradio"; input { pnr 28453; }; output-http { url "/radio/rbbkulturradio"; }; }; stream { name "RBB RadioMultiKulti"; input { pnr 28458; }; output-http { url "/radio/rbbradiomultikulti"; }; }; stream { name "Sputnik"; input { pnr 28433; }; output-http { url "/radio/sputnik"; }; }; stream { name "SR 1 Europawelle"; input { pnr 28461; }; output-http { url "/radio/sr1europawelle"; }; }; stream { name "SR 2 KulturRadio"; input { pnr 28462; }; output-http { url "/radio/sr2kulturradio"; }; }; stream { name "SR 3 Saarlandwelle"; input { pnr 28463; }; output-http { url "/radio/sr3saarlandwelle"; }; }; stream { name "SWR 1 BW"; input { pnr 28465; }; output-http { url "/radio/swr1bw"; }; }; # More SWR1 stream { name "SWR 2"; input { pnr 28467; }; output-http { url "/radio/swr2"; }; }; stream { name "SWR 3"; input { pnr 28468; }; output-http { url "/radio/swr3"; }; }; stream { name "SWR 4 BW"; input { pnr 28469; }; output-http { url "/radio/swr4bw"; }; }; # More SWR stream { name "WDR 2"; input { pnr 28476; }; output-http { url "/radio/wdr2"; }; }; stream { name "WDR 2 Klassik"; input { pnr 28482; }; output-http { url "/radio/wdr2klassik"; }; }; stream { name "WDR 3"; input { pnr 28477; }; output-http { url "/radio/wdr3"; }; }; stream { name "WDR 4"; input { pnr 28478; }; output-http { url "/radio/wdr4"; }; }; stream { name "WDR 5"; input { pnr 28479; }; output-http { url "/radio/wdr5"; }; }; stream { name "WDR Event"; input { pnr 28473; }; output-http { url "/radio/wdrevent"; }; }; stream { name "WDR Funkhaus Europa"; input { pnr 28480; }; output-http { url "/radio/wdrfunkhauseuropa"; }; }; stream { name "You fm"; input { pnr 28423; }; output-http { url "/radio/youfm"; }; }; }; getstream2-20100616/configs/config-dvbt-germany-owl0000664000372000037200000000147311406140020020661 0ustar florfcorg http { port 8000; }; adapter 0 { # ZDF:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_2_3:FEC_1_2:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:545:546:514 # Info/3sat:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_2_3:FEC_1_2:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:561:562:515 # Doku/KiKa:570000000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_2_3:FEC_1_2:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:593:594:517 dvb-t { frequency 570000000; bandwidth 8; transmission-mode auto; guard-interval auto; hierarchy none; modulation auto; }; channel 0x205 { name "Doku/Kika"; stream-http { url /205; }; }; channel 0x203 { name "Info/3Sat"; stream-http { url /203; }; }; channel 0x202 { name "ZDF"; stream-http { url /202; }; }; }; getstream2-20100616/configs/config-astra-transponder-710000664000372000037200000000266011406140020021356 0ustar florfcorg http { port 8001; }; adapter 1 { packet-buffer 50; stat-interval 120; dvb-s { #lnb-sharing yes; lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 11836500; polarisation h; symbol-rate 27500000; }; }; stream { name "transponder-71-management"; input { pid 0x0; pid 0x10; pid 0x11; pid 0x12; pid 0x13; pid 0x14; }; output-pipe { filename "/tmp/transponder-71"; }; }; stream { name "Das Erste"; input { pnr 28106; }; output-http { url /tv/daserste; }; output-udp { remote-address 239.0.71.1; remote-port 3000; sap { scope global; ttl 4; playgroup "FIFA WM"; }; }; }; stream { name "arte"; input { pnr 28109; }; output-http { url /tv/arte; }; }; stream { name "Bayerisches Fernsehen"; input { pnr 28107; }; output-http { url /tv/br; }; }; stream { name "Bayern alpha"; input { pnr 28112; }; output-http { url /tv/bayernalpha; }; }; stream { name "hr-fernsehen"; input { pnr 28108; }; output-http { url /tv/hr; }; }; stream { name "Phoenix"; input { pnr 28114; }; output-http { url /tv/phoenix; }; }; stream { name "Suedwest Fernsehen SW"; input { pnr 28113; }; output-http { url /tv/sw; }; }; stream { name "WDR Fernsehen"; input { pnr 28111; }; output-http { url /tv/wdr; }; }; }; getstream2-20100616/crc32.h0000664000372000037200000000023211406140020013722 0ustar florfcorg#define CRC32_LEN 4 uint32_t crc32_le(uint32_t crc, unsigned char const *p, int len); uint32_t crc32_be(uint32_t crc, unsigned char const *p, int len); getstream2-20100616/stream.c0000664000372000037200000000414611406140020014304 0ustar florfcorg #include "getstream.h" #include "output.h" void stream_send(void *data, void *arg) { struct stream_s *stream=arg; GList *ol=g_list_first(stream->output); while(ol) { struct output_s *o=ol->data; output_send(o, data); ol=g_list_next(ol); } } static void stream_init_pat(struct stream_s *stream); static void stream_send_pat(int fd, short event, void *arg) { struct stream_s *stream=arg; struct pat_s *pat; int pkts; GList *il; pat=pat_new(); for(il=g_list_first(stream->input);il;il=g_list_next(il)) { struct input_s *input=il->data; if (input->type == INPUT_PNR) { unsigned int pmtpid=pmt_get_pmtpid(input->pnr.program); pat_add_program(pat, input->pnr.pnr, pmtpid); } } /* FIXME - we should take care on the PAT version and Transport ID */ pkts=pat_send(pat, stream->patcc, 0, 0, stream_send, stream); pat_free(pat); stream->patcc=(stream->patcc+pkts)&TS_CC_MASK; stream_init_pat(stream); } static void stream_init_pat(struct stream_s *stream) { struct timeval tv; #define PAT_INTERVAL 500 tv.tv_usec=PAT_INTERVAL*1000; tv.tv_sec=0; evtimer_set(&stream->patevent, stream_send_pat, stream); evtimer_add(&stream->patevent, &tv); } /* * Called initially on programm start to initialize all input * filter and outputs before the first TS packets get forwarded */ void stream_init(struct stream_s *stream) { GList *il=g_list_first(stream->input); GList *ol=g_list_first(stream->output); while(ol) { struct output_s *output=ol->data; output_init(output); ol=g_list_next(ol); } /* * FIXME - In case we dont have filters we might want to hand * out the output_??? function onto the input functions to pass * onto the demux or PMT parsing. This would eliminate the * stream_send function and save CPU cycles. */ while(il) { struct input_s *input=il->data; input_init(input); il=g_list_next(il); } /* * If we not only have static content in our stream we might need to * send out PATs on a regular basis - Initialize the PAT timer for this stream */ if (stream->psineeded) stream_init_pat(stream); /* FIXME SAP init ? */ /* FIXME filter init ? */ } getstream2-20100616/output_rtp.c0000664000372000037200000001522111406140020015232 0ustar florfcorg #include #include #include #include #include #include #include #include "output.h" #include "simplebuffer.h" #include "socket.h" #if 0 static inline void sout_send_tsp_rtp(struct stream_s *s, uint8_t *tsp) { } static void sout_sock_read(int fd, short event, void *arg) { struct stream_s *s=arg; ssize_t size; struct sockaddr_in sin; socklen_t sinlen=sizeof(struct sockaddr_in); size=recvfrom(fd, s->ctlbuf, MAX_CTL_MSG_SIZE, 0, (struct sockaddr *) &sin, &sinlen); sout_parse_rtcp(s, s->ctlbuf, size, &sin); } static int sout_init_socket(struct stream_s *s) { struct sockaddr_in sin; memset(&sin, 0, sizeof(struct sockaddr_in)); s->sockfd=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); sin.sin_port=htons(s->port); sin.sin_family=AF_INET; sin.sin_addr.s_addr=s->groupinaddr.s_addr; if (s->dist == STREAM_DIST_LISTEN) { /* Open control aka RTCP socket. I reverse engineered * that this is group socket + 1 */ s->ctlfd=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); sin.sin_port=htons(s->port+1); bind(s->ctlfd, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)); /* Add callback for packet receiption on control socket e.g. RTCP */ event_set(&s->ctlevent, s->ctlfd, EV_READ|EV_PERSIST, sout_sock_read, s); event_add(&s->ctlevent, NULL); } else { /* MCAST - Set destination */ connect(s->sockfd, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)); if (s->dist == STREAM_DIST_MCAST) { /* * Set socket TTL - Be warned - I DoSed a Cisco 5500 RSM by sending * a 60MBit/s stream in 14 Groups all with TTL of 1 and the switch * went to lala land. It seems dropping MCAST traffic is very expensive * in IOS 12.1 land and its even dropped in Layer 3 instead of Layer 2 although * nobody expects ICMP "TTL expired" for MCAST traffic * */ setsockopt(s->sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)); } } return 1; } #endif int output_rtp_new_receiver(struct output_s *o, char *addr, int port, uint32_t ssrc) { struct rtp_receiver_s *r; r=calloc(1, sizeof(struct rtp_receiver_s)); /* Copy address and port */ r->addr=strdup(addr); r->port=port; r->ssrc=ssrc; r->lastrr=time(NULL); /* Create sockaddr_in struct for later sendmsg */ r->sin.sin_family=AF_INET; inet_aton(r->addr, &r->sin.sin_addr); r->sin.sin_port=htons(r->port); r->sinlen=sizeof(struct sockaddr_in); /* Prepend receiver to receiver list */ r->next=o->rtpreceiver; o->rtpreceiver=r; /* We want to receive packets */ o->receiver++; return 0; } #if 0 static struct rtp_receiver_s *output_rtp_find_rtpr(struct output_s *o, uint32_t ssrc) { struct rtp_receiver_s *r; for(r=o->rtpreceiver;r;r=r->next) if (r->ssrc == ssrc) return r; return NULL; } static void output_rtp_free_rtpr(struct output_s *o, uint32_t ssrc) { struct rtp_receiver_s *r, *lr; for(r=o->rtpreceiver,lr=NULL;r;r=r->next) { if (r->ssrc == ssrc) { if (lr) lr->next=r->next; free(r); return; } lr=r; } } static void output_rtp_parse_rtcp(struct output_s *o, uint8_t *b, int len, struct sockaddr_in *sin) { struct rtp_receiver_s *r; uint32_t ssrc; logwrite(LOG_DEBUG, "streamrtp: got rtcp packet version %d\n", RTCP_VERSION(b)); /* Version 2 ? */ if (RTCP_VERSION(b) != 2) return; /* Get SSRC from RTCP packet */ ssrc=b[3]<<24 | b[4]<<16 | b[5]<<8 | b[6]; switch(RTCP_PT(b)) { case(RTP_PT_RR): logwrite(LOG_DEBUG, "streamrtp: Got Receiver Report size %d from %s ssrc %08x\n", len, inet_ntoa(sin->sin_addr), ssrc); r=output_rtp_find_rtpr(o, ssrc); if (!r) { logwrite(LOG_INFO, "streamrtp: Createing new RTP Receiver %08x\n", ssrc); output_rtp_new_receiver(o, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)-1, ssrc); } else { /* Store last RR timestamp */ r->lastrr=time(NULL); } break; case(RTP_PT_BYE): logwrite(LOG_INFO, "streamrtp: Got Bye size %d from %s ssrc %08x\n", len, inet_ntoa(sin->sin_addr), ssrc); /* Find receiver struct */ output_rtp_free_rtpr(o, ssrc); break; } } static void output_rtp_read_rtcp(int fd, short event, void *arg) { struct output_s *o=arg; ssize_t size; struct sockaddr_in sin; socklen_t sinlen=sizeof(struct sockaddr_in); size=recvfrom(fd, o->rtcpbuffer, RTCP_BUFFER_SIZE, 0, (struct sockaddr *) &sin, &sinlen); logwrite(LOG_DEBUG, "streamrtp: got packet size %d\n", size); output_rtp_parse_rtcp(o, o->rtcpbuffer, size, &sin); } static void output_init_rtp_rtcp(struct output_s *o) { struct sockaddr_in sin; /* Allocate the RTCP incoming packet buffer */ o->rtcpbuffer=malloc(RTCP_BUFFER_SIZE); memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family=AF_INET; sin.sin_addr.s_addr=INADDR_ANY; sin.sin_port=htons(o->rtcpport); /* Create a socket and bind */ o->rtcpfd=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); bind(o->rtcpfd, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)); /* Install rtcp callback */ event_set(&o->rtcpevent, o->rtcpfd, EV_READ|EV_PERSIST, output_rtp_read_rtcp, o); event_add(&o->rtcpevent, NULL); } #endif int output_init_rtp(struct output_s *o) { o->sockfd=socket_open(o->localaddr, 0); if (o->sockfd < 0) return -1; socket_set_nonblock(o->sockfd); socket_set_ttl(o->sockfd, o->ttl); /* Join multicast group if its a multicast address */ socket_join_multicast(o->sockfd, o->remoteaddr), /* Do you have a better idea ? */ o->rtpssrc=(uint32_t) random(); o->buffer=sb_init(RTP_MAX_TS, TS_PACKET_SIZE, RTP_HEADROOM); output_rtp_new_receiver(o, o->remoteaddr, o->remoteport, 0); return 0; } void output_send_rtp(struct output_s *o, uint8_t *tsp) { sb_add_atoms(o->buffer, tsp, 1); /* check whether another packet would fit ? */ if (!sb_free_atoms(o->buffer)) { struct rtp_receiver_s *r; struct timeval tv; long msec; uint8_t *b=sb_bufptr(o->buffer); gettimeofday(&tv, (struct timezone *) NULL); msec=(tv.tv_sec%1000000)*1000 + tv.tv_usec/1000; b[RTP_VERSION_OFF] = 0x80; b[RTP_PT_OFF] = RTP_PT_MP2T; /* RFC 2250 */ b[RTP_SEQ_OFF] = o->rtpseq >> 8 & 0xff; b[RTP_SEQ_OFF+1] = o->rtpseq & 0xff; b[RTP_TSTAMP_OFF] = msec>>24 & 0xff; b[RTP_TSTAMP_OFF+1] = msec>>16 & 0xff; b[RTP_TSTAMP_OFF+2] = msec>>8 & 0xff; b[RTP_TSTAMP_OFF+3] = msec & 0xff; b[RTP_SSRC_OFF] = o->rtpssrc>>24 & 0xff; b[RTP_SSRC_OFF+1] = o->rtpssrc>>16 & 0xff; b[RTP_SSRC_OFF+2] = o->rtpssrc>>8 & 0xff; b[RTP_SSRC_OFF+3] = o->rtpssrc & 0xff; for(r=o->rtpreceiver;r;r=r->next) { int len; len=sendto(o->sockfd, sb_bufptr(o->buffer), sb_buflen(o->buffer), MSG_DONTWAIT, (struct sockaddr *) &r->sin, r->sinlen); } sb_zap(o->buffer); o->rtpseq++; } } getstream2-20100616/libhttp.h0000664000372000037200000000441111406140020014457 0ustar florfcorg#ifndef LIBHTTP_H #define LIBHTTP_H #include #include #include #include #include #include #include #define MAX_HEADER_SIZE 3000 enum { HC_STATUS_HEAD, HC_STATUS_BODY, HC_STATUS_END }; enum { HC_CMD_UNKNOWN, HC_CMD_GET, HC_CMD_POST, HC_CMD_HEAD, }; enum { HP_UNKNOWN, HP_HTTP, HP_HTTP10, HP_HTTP11 }; /* Callback types - Why are we calling back */ enum { HCB_QUERY, /* Check METHOD for validity */ HCB_WRITE, /* Socket is writeable - send more */ HCB_READ, /* Data arrived - parse it */ HCB_ERROR, /* We got an error from underneath */ HCB_END, /* User said - we want to end - give em a chance to clean up */ }; struct http_attrib { char *token; char *value; }; struct http_connection { struct http_server *server; struct sockaddr_in sin; int fd, status, cid, request; struct bufferevent *bev; struct evbuffer *evb; int keepalive; /* Request information */ int cmd; char *url; int proto; GList *attrib; /* url handler - set after request received */ int (*url_handler)(struct http_connection *hc, int cbtype, void *arg); /* Application */ void *arg; /* Header */ int hsize; char hdr[MAX_HEADER_SIZE]; }; struct http_server { int port; struct sockaddr_in sin; int fd; struct event ev; int cid; GList *conn; GHashTable *urls; }; struct http_url { char *url; int (*cb)(struct http_connection *hc, int cbtype, void *arg); void *arg; }; struct http_server *http_init(int port); int http_register_url(struct http_server *hs, struct http_url *hu); void http_drop_connection(struct http_connection *hc); int http_return_simple(struct http_connection *hc, char *result, char *type, void *data, size_t datalen); int http_return_stream(struct http_connection *hc, void *data, size_t datalen); size_t http_get_queue(struct http_connection *hc); int http_header_add(struct http_connection *hc, char *fmt, ...); int http_header_end(struct http_connection *hc); int http_header_clength(struct http_connection *hc, ssize_t length); int http_header_nocache(struct http_connection *hc); int http_header_start(struct http_connection *hc, char *result, char *type); void http_request_end(struct http_connection *hc); #endif getstream2-20100616/README0000664000372000037200000001336111406140020013524 0ustar florfcorg Building: - Install libevent and glib - Probably adapt paths in Makefile - type "make" Configuration: Adapter: -------- As getstream is supposed to support multiple adapters with the same program instance the configuration starts with an adapter clause: adapter 1 { budget-mode 1; packet-buffer 50; stat-interval 120; stuck-interval 200; }; - budget-mode Set to "0" disabled the budget mode where getstream request a seperate PID filter from the kernels filter. As the filters are limited on the full featured cards this is only a workaround and may work for 2-3 TV Channels. Streaming a full transponder on a full featured card is most certainly not possible. Getstream automatically disables budget mode when setting the filter for bit 0x2000 (Illegal value - tells kernel to stream full transponder aka "budget-mode") and setting the filter returns an error. - packet-buffer Set the amount of packets getstream trys to get from the kernels dvr interface. I see typically ~44 Packets/s per round read from the kernel. Setting this too high just wastes memory (a packet is 188 Bytes) setting it to low creates more context-switches and will reduce performance. - stat-interval The interval in seconds you will see the statistics on in the log e.g. 2007-10-25 08:27:02.323 dvr: inputstats: 149 pids 23259 pkt/s 4372702 byte/s - stuck-interval Interval to fire the stuck check timer for e.g. FlexCop cards. Default 5 seconds, 0 disables. Tuning or Card type: -------------------- As getstream supports DVB-C, DVB-T and DVB-S and all need different parameters for tuning in on the right transponder here is a short introduction on how to enter the right parameters. DVB-T: ----- There are 6 possible options to list for a DVB-T transponder: frequency - Frequency of transponder in Hz bandwidth - auto, 6 (6Mhz), 7 (7Mhz), 8 (8Mhz) transmission-mode - auto, 2 (2Khz), 8 (8Khz) guard-interval - auto, 4 (1/4), 8 (1/8), 16 (1/16), 32 (1/32) hierarchy - none, auto, 1, 2, 4 modulation - auto, 16, 32, 64, 128, 256 Example: dvb-t { frequency 570000000; bandwidth 8; transmission-mode auto; guard-interval auto; hierarchy none; modulation auto; }; DVB-S / DVB-S2: ------ DVB-S consists of 2 parts - the LNB and the Transponder config: lnb-sharing - Boolean When set getstream trys to not send an 22Khz Pilot tone or set high voltage for High/Low band or Polarisation but instead trusts a different card to do all necessary things. Some cards can not disable their power voltage completely so look out for problems. lnb The config needs to match your LNB (Low Block converter) on your Dish - If you have a Ku Band LNB (Normal for most of the World) you need these informations: lof1 - Local osscilator frequency 1 (Low Band) lof2 - Local osscilator frequency 2 (High Band) slof - Local osscilator frequency cut off If the transponder frequency is above the slof getstream enables the 22Khz Pilot tone to switch the LNB to High Band. If you have a C Band LNB you dont have a slof so dont configure it. If you have a multipoint C Band LNB you have lof1 and lof2 in case you dont just configure the lof1. Transponder: frequency - Frequency of the Transponder in hz. polarisation - Either H or V for Horizontal or Vertical symbol-rate - Symbol Rate - Most European transponders use 27500000 diseqc - LNB Diseq code dvb-s { lnb-sharing yes; lnb { lof1 9750000; lof2 10600000; slof 11700000; }; transponder { frequency 12266000; polarisation h; symbol-rate 27500000; diseqc 4; }; }; For "DVB-S2" the type obviously has to be changed to "dvb-s2". Also getstream need to be compiled against the multiproto header files and you need to be using multiproto enabled cards. Input: ------ The input section in every stream defines which parts of the Transponder needs to be forwarded into this stream. Currently there are 3 options. pid - A static pid to forward. Sometimes people want to have the encryption stuff forwarded e.g. pid 10 and 11 pnr - Program number e.g. a full Programm including all pids. full - A full transponder e.g. the same as pid 0x2000. Example: -------- input { full; }; SAP: ------ Can be used in the output-rtp and output-udp sections to send SAP announcements. scope - Multicast group which is used for the SAP announcemens. Possible values: global - Global SAP address (224.2.127.254:9875) (default) org - Organization-local SAP address (239.195.255.255:9875) local - Local SAP address (239.255.255.255:9875) link - Link-local SAP address (224.0.0.255:9875) sap-group - Multicast group address which is used for the SAP announcemens (overrides scope). sap-port - Port which is used for the SAP announcemens (overrides scope). announce-host - unused announce-port - unused ttl - TTL for the SAP announcemens. By default the output stream's TTL is used. interval - Announcement interval (in seconds) (default=1). playgroup - SAP/SDP group name. uri - An URI with additional information. description - Stream description. email - EMail contact information (can be used multiple times). phone - Phone contact information (can be used multiple times). attribute - Additional SDP attribute, see RFC2327 for details (can be used multiple times). Example: -------- sap { scope global; interval 5; playgroup "TV"; description "Example TV stream"; uri "http://example.com"; email " bob@example.com"; phone "+49 555 555-555"; attribute "tool:getstream"; attribute "recvonly"; }; getstream2-20100616/simplebuffer.c0000664000372000037200000000301211406140020015463 0ustar florfcorg#include #include #include #include #include struct simplebuffer_s { uint8_t *buffer; int atomsize; int atoms; int fill; int headroom; }; void *sb_init(int atoms, int atomsize, int headroom) { struct simplebuffer_s *sb; sb=calloc(1, sizeof(struct simplebuffer_s)); if (!sb) return NULL; sb->buffer=malloc(atoms*atomsize+headroom); if (!sb->buffer) { free(sb); return NULL; } sb->atoms=atoms; sb->atomsize=atomsize; sb->headroom=headroom; return sb; } void sb_free(void *sbv) { struct simplebuffer_s *sb=sbv; free(sb->buffer); free(sb); } int sb_used_atoms(void *sbv) { struct simplebuffer_s *sb=sbv; return sb->fill; } int sb_free_atoms(void *sbv) { struct simplebuffer_s *sb=sbv; return (sb->atoms-sb->fill); } int sb_add_atoms(void *sbv, uint8_t *atom, int atoms) { struct simplebuffer_s *sb=sbv; int copy; copy=MIN(atoms, sb_free_atoms(sbv)); memcpy(&sb->buffer[sb->fill*sb->atomsize+sb->headroom], atom, copy*sb->atomsize); sb->fill+=copy; return copy; } uint8_t *sb_bufptr(void *sbv) { struct simplebuffer_s *sb=sbv; return sb->buffer; } int sb_buflen(void *sbv) { struct simplebuffer_s *sb=sbv; return sb->fill*sb->atomsize+sb->headroom; } void sb_zap(void *sbv) { struct simplebuffer_s *sb=sbv; sb->fill=0; } void sb_drop_atoms(void *sbv, int atoms) { struct simplebuffer_s *sb=sbv; memmove(sb->buffer+sb->headroom, sb->buffer+sb->headroom+atoms*sb->atomsize, (sb->fill-atoms)*sb->atomsize); sb->fill-=atoms; } getstream2-20100616/pmt.c0000664000372000037200000002621111406140020013606 0ustar florfcorg#include #include #include #include "getstream.h" #include "psi.h" #include "crc32.h" struct programcb_s { void (*callback)(void *data, void *arg); void *arg; }; struct pmtes_s { uint8_t streamtype; uint16_t espid; }; struct pmt_s { uint16_t pnr; uint16_t pcrpid; GList *es; }; struct program_s { struct adapter_s *adapter; unsigned int pnr; uint16_t pmtpid; void *pmtpidcb; /* pid callback cookie (PMT) */ struct { GList *cb; } pidtable[PID_MAX]; struct psi_s psi; struct pmt_s *pmtlast; struct pmt_s *pmtcurrent; GList *progcbl; }; #if 0 /* Callback for program pids (not pmt) from the demux */ static void pmt_dvr_cb(void *data, void *arg) { struct program_s *prog=arg; GList *progcbl=g_list_first(prog->progcbl); while(progcbl) { struct programcb_s *pcb=progcbl->data; /* Issue callback - typically a input callback */ pcb->callback(data, pcb->arg); progcbl=g_list_next(progcbl); } } #endif #define pmt_prog_gets_pid(prog, pid) \ ((prog)->pidtable[(pid)].cb != NULL) /* * This program wants a specific PID - So register it with the DVR demux. As we * might habe multiple streams receiving this program we register all streams * with the dvr demux. * * As we need to leave pid in case the PMT changes we need to remember the DVR demux * call back cookies. For this we append the cookies to a GList in the programs pidtable. * */ static void pmt_prog_join_pid(struct program_s *prog, uint16_t pid, unsigned int type) { GList *pcbl=g_list_first(prog->progcbl); while(pcbl) { struct programcb_s *pcb=pcbl->data; void *cbc; cbc=dvr_add_pcb(prog->adapter, pid, DVRCB_TS, type, pcb->callback, pcb->arg); prog->pidtable[pid].cb=g_list_append(prog->pidtable[pid].cb, cbc); pcbl=g_list_next(pcbl); } } /* * The programs leaves a certain pid. For this we walk the DVR callback cookie * list in the programs pidtable and pass them onto the dvr_del_pcb. * If we removed all streams from the dvr callback we delete the list and * set the GList pointer in the pidtable to NULL which is the signal that * the program is not receiving that pid. */ static void pmt_prog_leave_pid(struct program_s *prog, uint16_t pid) { GList *cbcl=g_list_first(prog->pidtable[pid].cb); while(cbcl) { void *cbc=cbcl->data; dvr_del_pcb(prog->adapter, pid, cbc); cbcl=g_list_next(cbcl); } g_list_free(prog->pidtable[pid].cb); prog->pidtable[pid].cb=NULL; } static uint16_t pmt_pnr(struct psisec_s *section) { return (section->data[PMT_PNR_OFF1] << 8 | section->data[PMT_PNR_OFF2]); } static int pmt_mapstreamtype(uint8_t type) { switch (type) { case 1: /* ISO/IEC 11172 Video */ case 2: /* ITU-T Rec. H.262 */ /* ISO/IEC 13818-2 Video */ /* ISO/IEC 11172-2 */ case 27: /* ITU-T Rec. H.264 */ /* ISO/IEC 14496-10 Video */ return PID_VIDEO; break; case 3: /* ISO/IEC 11172 Audio */ case 4: /* ISO/IEC 13818-3 Audio */ return PID_AUDIO; break; case 5: /* ISO/IEC 13818-1 Page 160 - Private Sections */ case 6: /* ITU-T Rec. H.222.0 ISO/IEC 13818-1 PES - Private Data */ /* Stephen Gardner sent dvbsnoop output showing AC3 Audio * in here encapsulated in the private data. As we dont * treat different pid types differently we don't care for * now */ return PID_PRIVATE; break; case 7: /* ISO/IEC 13522 MHEG */ case 8: /* ITU-T Rec. H.220.0 / ISO/IEC 13818-1 Annex A DSM CC */ case 9: /* ITU-T Rec. H.220.1 */ case 10: /* ISO/IEC 13818-6 Type A */ case 11: /* ISO/IEC 13818-6 Type B */ case 12: /* ISO/IEC 13818-6 Type C */ case 13: /* ISO/IEC 13818-6 Type D */ case 14: /* ISO/IEC 13818-1 auxiliary */ return PID_OTHER; break; default: if (type & 0x80) return PID_USER; } return PID_OTHER; } static uint16_t pmt_pcr(struct psisec_s *section) { return (section->data[PMT_PCR_OFF1] << 8 | section->data[PMT_PCR_OFF2]) & PID_MASK; } static unsigned int pmt_pinfo_len(struct psisec_s *section) { return (section->data[PMT_PILEN_OFF1] << 8 | section->data[PMT_PILEN_OFF2]) & PMT_PILEN_MASK; } #define ESINFO_LEN_OFF 3 #define ESINFO_LEN_MASK 0x0fff #define ESINFO_MIN_LEN 5 static int pmt_es_infolen(struct psisec_s *section, int es) { return (section->data[es+ESINFO_LEN_OFF]<<8 | section->data[es+ESINFO_LEN_OFF+1]) & ESINFO_LEN_MASK; } static int pmt_next_es(struct psisec_s *section, int es) { int nes; nes=es+pmt_es_infolen(section, es)+ESINFO_MIN_LEN; /* Next offset beyond PMT end ? */ if (nes >= psi_len(section)-CRC32_LEN) return 0; return nes; } static int pmt_first_es(struct psisec_s *section) { return PMT_PI_OFF+pmt_pinfo_len(section); } static uint8_t pmt_es_type(struct psisec_s *section, int es) { return section->data[es]; } #define ES_PID_OFF 1 static unsigned int pmt_es_pid(struct psisec_s *section, int es) { return ((section->data[es+ES_PID_OFF]<<8 | section->data[es+ES_PID_OFF+1]) & PID_MASK); } unsigned int pmt_get_pmtpid(void *pvoid) { struct program_s *prog=pvoid; return prog->pmtpid; } struct pmt_s *pmt_new(void ) { return calloc(1, sizeof(struct pmt_s)); } void pmt_free(struct pmt_s *pmt) { GList *es=g_list_first(pmt->es); while(es) { g_free(es->data); es=g_list_next(es); } g_list_free(pmt->es); free(pmt); } void pmt_add_es(struct pmt_s *pmt, uint8_t type, uint16_t pid) { struct pmtes_s *es=g_new(struct pmtes_s, 1); es->streamtype=type; es->espid=pid; pmt->es=g_list_append(pmt->es, es); } static struct pmt_s *pmt_parse(struct program_s *prog) { struct pmt_s *pmt; struct psisec_s *s; int lastsecnum; int secnum; int esoff; int es=0; if (!prog->psi.section[0]) return NULL; pmt=pmt_new(); lastsecnum=psi_last_section_number(prog->psi.section[0]); for(secnum=0;secnum<=lastsecnum;secnum++) { s=prog->psi.section[secnum]; if (!s) continue; /* * FIXME - Multiple section PMTs might be inconsistent * concerning the PCR pid */ if (pmt_pcr(s) != PID_MAX) pmt->pcrpid=pmt_pcr(s); /* Walk PMT and add ES PIDS as necessary */ esoff=pmt_first_es(s); do { uint8_t type=pmt_es_type(s, esoff); uint16_t pid=pmt_es_pid(s, esoff); pmt_add_es(pmt, type, pid); es++; } while((esoff=pmt_next_es(s, esoff)) != 0); } logwrite(LOG_DEBUG, "pmt: parse_pmt found %d elementary streams for pnr %04x", es, prog->pnr); return pmt; } static struct pmtes_s *pmt_find_es(struct pmt_s *pmt, uint16_t pid) { GList *esl; struct pmtes_s *es; for(esl=g_list_first(pmt->es);esl;esl=g_list_next(esl)) { es=esl->data; if (es->espid == pid) return es; } return NULL; } static void pmt_update(struct program_s *prog) { struct pmt_s *current=prog->pmtcurrent; struct pmt_s *last=prog->pmtlast; GList *esl; struct pmtes_s *escur, *eslast; int i; logwrite(LOG_DEBUG, "pmt: pmt_update running for program %04x", prog->pnr); for(esl=g_list_first(current->es);esl;esl=g_list_next(esl)) { escur=esl->data; if (last) { eslast=pmt_find_es(last, escur->espid); if (eslast) continue; } if (!pmt_prog_gets_pid(prog, escur->espid)) pmt_prog_join_pid(prog, escur->espid, pmt_mapstreamtype(escur->streamtype)); } /* * We check if we get the PCR pid already. It seems most programs * multiplex the PCR informations into the Audio PID instead * a seperate PID so there would be no need to add a seperate callback * */ if (current->pcrpid != PID_MAX) { if (!pmt_prog_gets_pid(prog, current->pcrpid)) pmt_prog_join_pid(prog, current->pcrpid, PID_PCR); } /* * Walk all joined pids and check whether they are still active. * If they are not active - leave */ for(i=0;ipcrpid == i) continue; if (prog->pmtpid == i) continue; if (pmt_find_es(current, i)) continue; pmt_prog_leave_pid(prog, i); } } } static void pmt_dvr_pmt_cb(void *data, void *arg) { struct psisec_s *section=data; struct program_s *prog=arg; struct pmt_s *pmt; logwrite(LOG_XTREME, "pmt: dvr section callback"); dump_hex(LOG_XTREME, "pmt: PMT ", section->data, section->valid); if (!psi_currentnext(section)) return; if (psi_tableid(section) != PMT_TABLE_ID) { logwrite(LOG_INFO, "pmt: received PMT with broken table id on pid %d", prog->pmtpid); return; } if (pmt_pnr(section) != prog->pnr) { logwrite(LOG_DEBUG, "pmt: received PMT section for pnr %04x expected %04x", pmt_pnr(section), prog->pnr); return; } if (!psi_update_table(&prog->psi, section)) return; pmt=pmt_parse(prog); if (prog->pmtlast) pmt_free(prog->pmtlast); prog->pmtlast=prog->pmtcurrent; prog->pmtcurrent=pmt; pmt_update(prog); } /* Find program structure for a given PNR (Program Number) */ static struct program_s *pmt_prog_from_pnr(struct adapter_s *a, unsigned int pnr) { struct program_s *prog; GList *pl=g_list_first(a->pmt.pnrlist); /* Find pnr struct for this pnr */ while(pl) { prog=pl->data; if (prog->pnr == pnr) return prog; pl=g_list_next(pl); } return NULL; } /* * Callback from PAT parsing - Passes in adapter, pnr and pmtpid - If * we want this PNR and we didnt already join the PMT do so - In case * of a PMT pid change - leave old pid and join new one. * * In case of a PMTPID of 0 the PAT did not contain our program anymore. * */ void pmt_pidfrompat(struct adapter_s *a, unsigned int pnr, unsigned int pmtpid) { struct program_s *prog; prog=pmt_prog_from_pnr(a, pnr); if (!prog) return; logwrite(LOG_XTREME, "pmt: pidfrompat found pnr %04x", pnr); if (prog->pmtpid == pmtpid) return; logwrite(LOG_XTREME, "pmt: pidfrompat pmt pid changed from %04x to %04x", prog->pmtpid, pmtpid); /* If we have an old one - leave it */ if (prog->pmtpid) { dvr_del_pcb(a, prog->pmtpid, prog->pmtpidcb); pmt_prog_leave_pid(prog, prog->pmtpid); } if (pmtpid) { /* Add a callback for the new pmt pid */ prog->pmtpidcb=dvr_add_pcb(a, pmtpid, DVRCB_SECTION, PID_PMT, pmt_dvr_pmt_cb, prog); pmt_prog_join_pid(prog, pmtpid, PID_PMT); } else { /* FIXME - The Program disappeared - we should leave all pids */ } prog->pmtpid=pmtpid; } static struct program_s *pmt_prog_new(struct adapter_s *a, unsigned int pnr) { struct program_s *prog; prog=calloc(1, sizeof(struct program_s)); if (!prog) return NULL; prog->pnr=pnr; prog->pmtpid=0; prog->adapter=a; a->pmt.pnrlist=g_list_append(a->pmt.pnrlist, prog); return prog; } static void pmt_prog_add_cb(struct program_s *prog, void (*callback)(void *data, void *arg), void *arg) { struct programcb_s *pcb; pcb=malloc(sizeof(struct programcb_s)); pcb->callback=callback; pcb->arg=arg; prog->progcbl=g_list_append(prog->progcbl, pcb); } /* * Called from input_pnr to join a program number by supplying an * callback which will be put into the demux table by the PMT parser * */ void *pmt_join_pnr(struct adapter_s *a, unsigned int pnr, void (*callback)(void *data, void *arg), void *arg) { struct program_s *prog; /* * Find this program - might be multiple output * streams for the same program */ prog=pmt_prog_from_pnr(a, pnr); /* If we havent got one - create it */ if (!prog) prog=pmt_prog_new(a, pnr); /* Add callback to this stream to program list */ if (prog) pmt_prog_add_cb(prog, callback, arg); pat_init(a); return prog; } getstream2-20100616/README.multiproto0000664000372000037200000000100311406140020015727 0ustar florfcorg Multiproto is still a moving target and incompatible API changes have been introduced on a regular basis. Getstream has been known to work with multiproto at specific releases. To compile against multiproto copy the include directory named "linux" to the getstream build directory and name it "linux". The getstream build should then build against the multiproto header. flo@stream:~/multiproto$ cp -ra linux/include/linux ~/getstream2/ Be sure to make clean before building a new multiproto enabled getstream. getstream2-20100616/socket.c0000664000372000037200000000365711406140020014307 0ustar florfcorg #include #include #include #include #include #include #include #include #include #include "output.h" #include "simplebuffer.h" void socket_close(int sock) { close(sock); } int socket_open(char *laddr, int port) { struct sockaddr_in lsin; int sock; memset(&lsin, 0, sizeof(struct sockaddr_in)); sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock<0) return sock; lsin.sin_family=AF_INET; lsin.sin_addr.s_addr=INADDR_ANY; if (laddr) inet_aton(laddr, &lsin.sin_addr); lsin.sin_port=htons(port); if (bind(sock, (struct sockaddr *) &lsin, sizeof(struct sockaddr_in)) != 0) { close(sock); return -1; } return sock; } int socket_set_nonblock(int sock) { unsigned int flags; flags=fcntl(sock, F_GETFL); return fcntl(sock, F_SETFL, flags | O_NONBLOCK); } int socket_set_ttl(int sock, int ttl) { if (ttl) return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); return 0; } /* Join the socket on a multicats group e.g. tell the kernel * to send out IGMP join messages ... * * Returns 0 on success and != 0 in failure * */ int socket_join_multicast(int sock, char *addr) { struct ip_mreq mreq; memset(&mreq, 0, sizeof(struct ip_mreq)); /* Its not an ip address ? */ if (!inet_aton(addr, &mreq.imr_multiaddr)) return -1; if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) return 0; mreq.imr_interface.s_addr=INADDR_ANY; return setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); } int socket_connect(int sock, char *addr, int port) { struct sockaddr_in rsin; memset(&rsin, 0, sizeof(struct sockaddr_in)); /* Create remote end sockaddr_in */ rsin.sin_family=AF_INET; rsin.sin_port=htons(port); rsin.sin_addr.s_addr=INADDR_ANY; if (addr) inet_aton(addr, &rsin.sin_addr); return connect(sock, (struct sockaddr *) &rsin, sizeof(struct sockaddr_in)); } getstream2-20100616/dvr.c0000664000372000037200000001735411406140020013611 0ustar florfcorg#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getstream.h" const char *pidtnames[]={ "None", "PAT", "PMT", "PCR", "Video", "Audio", "Privat", "User", "Static", "Reassemble", "Other" }; struct pidcallback_s { void (*callback)(void *data, void *arg); void *arg; uint16_t pid; unsigned int type; unsigned int pidt; }; static inline char *dvrname(int adapter) { static char dvrname[128]; sprintf(dvrname, "/dev/dvb/adapter%d/dvr0", adapter); return dvrname; } static inline void dvr_input_ts(struct adapter_s *a, uint8_t *ts) { uint16_t pid; GList *pcbl; /* TS (Transport Stream) packets start with 0x47 */ if (ts[TS_SYNC_OFF] != TS_SYNC) { logwrite(LOG_XTREME, "dvr: Non TS Stream packet (!0x47) received on dvr0"); dump_hex(LOG_XTREME, "dvr:", ts, TS_PACKET_SIZE); return; } /* Full stream callbacks - pseudo pid 0x2000 */ for(pcbl=g_list_first(a->dvr.fullcb);pcbl!=NULL;pcbl=g_list_next(pcbl)) { struct pidcallback_s *pcb=pcbl->data; pcb->callback(ts, pcb->arg); } pid=ts_pid(ts); a->dvr.pidtable[pid].packets++; for(pcbl=g_list_first(a->dvr.pidtable[pid].callback);pcbl!=NULL;pcbl=g_list_next(pcbl)) { struct pidcallback_s *pcb=pcbl->data; pcb->callback(ts, pcb->arg); } } void dvr_del_pcb(struct adapter_s *a, unsigned int pid, void *vpcb) { struct pidcallback_s *pcb=vpcb; logwrite(LOG_DEBUG, "dvr: Del callback for PID %4d (0x%04x) type %d (%s)", pid, pid, pcb->pidt, pidtnames[pcb->pidt]); switch(pcb->type) { case(DVRCB_SECTION): a->dvr.pidtable[pid].secuser--; if (!a->dvr.pidtable[pid].secuser) { /* RECURSION - We want the section so we need the TS */ dvr_del_pcb(a, pid, a->dvr.pidtable[pid].seccb); a->dvr.pidtable[pid].seccb=NULL; } a->dvr.pidtable[pid].sectioncallback= g_list_remove(a->dvr.pidtable[pid].sectioncallback, pcb); break; case(DVRCB_TS): a->dvr.pidtable[pid].callback= g_list_remove(a->dvr.pidtable[pid].callback, pcb); if (!a->dvr.pidtable[pid].callback) dmx_leave_pid(a, pid); break; } free(pcb); } void dvr_section_reassemble(void *ts, void *arg) { struct dvrpt_s *pidentry=arg; int off=0; GList *cbl; while(off < TS_PACKET_SIZE) { off=psi_reassemble(pidentry->section, ts, off); if (off<0) break; for(cbl=g_list_first(pidentry->sectioncallback);cbl!=NULL;cbl=g_list_next(cbl)) { struct pidcallback_s *pcb=cbl->data; pcb->callback(pidentry->section, pcb->arg); } } } void *dvr_add_pcb(struct adapter_s *a, unsigned int pid, unsigned int type, unsigned int pidt, void (*callback)(void *data, void *arg), void *arg) { struct pidcallback_s *pcb; logwrite(LOG_DEBUG, "dvr: Add %s callback for PID %4d (0x%04x) type %d (%s)", ((type == DVRCB_TS) ? "TS" : "Section"), pid, pid, pidt, pidtnames[pidt]); /* FIXME - check for error of malloc */ pcb=malloc(sizeof(struct pidcallback_s)); pcb->pid=pid; pcb->callback=callback; pcb->arg=arg; pcb->pidt=pidt; pcb->type=type; /* Joined pseudo pid 0x2000 e.g. input full */ if (pid == PID_MAX+1) { /* FIXME: How to detect we need to join 0x2000? */ a->dvr.fullcb=g_list_append(a->dvr.fullcb, pcb); return pcb; } switch(type) { case(DVRCB_SECTION): if (!a->dvr.pidtable[pid].secuser) { a->dvr.pidtable[pid].section=psi_section_new(); /* Recursion - SECTION needs the ts packets */ a->dvr.pidtable[pid].seccb= dvr_add_pcb(a, pid, DVRCB_TS, PID_REASSEMBLE, dvr_section_reassemble, &a->dvr.pidtable[pid]); } a->dvr.pidtable[pid].secuser++; a->dvr.pidtable[pid].sectioncallback= g_list_append(a->dvr.pidtable[pid].sectioncallback, pcb); break; case(DVRCB_TS): if (!a->dvr.pidtable[pid].callback) dmx_join_pid(a, pid, DMX_PES_OTHER); a->dvr.pidtable[pid].callback= g_list_append(a->dvr.pidtable[pid].callback, pcb); break; } return pcb; } /* * Read TS packet chunks from dvr0 device and fill them one after * another into dvr_input_ts */ static void dvr_read(int fd, short event, void *arg) { int len, i; struct adapter_s *adapter=arg; uint8_t *db=adapter->dvr.buffer.ptr; do { len=read(fd, db, adapter->dvr.buffer.size*TS_PACKET_SIZE); /* * Increase readcounter - we need to detect flexcop not delivering * TS packets anymore e.g. the IRQ Stop bug. A timer will then * try to issue a frontend tune which will hopefully reset the * card to deliver packets * */ adapter->dvr.stat.reads++; /* EOF aka no more TS Packets ? */ if (len == 0) break; /* Read returned error ? */ if (len < 0) { if (errno != EAGAIN) logwrite(LOG_ERROR, "demux: read in dvr_read returned with errno %d / %s", errno, strerror(errno)); break; } /* Loop on TS packets and fill them into dvr_input_ts */ for(i=0;idvr.buffer.size*TS_PACKET_SIZE); } static void dvr_init_stat_timer(struct adapter_s *a); #define DVR_STAT_INTERVAL 60 static void dvr_stat_timer(int fd, short event, void *arg) { struct adapter_s *a=arg; int i; unsigned long total=0, pids=0; for(i=0;idvr.pidtable[i].packets) { pids++; total+=a->dvr.pidtable[i].packets; } } logwrite(LOG_INFO, "dvr: inputstats: %d pids %u pkt/s %u byte/s", pids, total/a->dvr.stat.interval, total*TS_PACKET_SIZE/a->dvr.stat.interval); for(i=0;idvr.pidtable[i].packets=0; } dvr_init_stat_timer(a); } static void dvr_init_stat_timer(struct adapter_s *a) { struct timeval tv; if (!a->dvr.stat.interval) return; a->dvr.stat.last=time(NULL); tv.tv_sec=a->dvr.stat.interval; tv.tv_usec=0; evtimer_set(&a->dvr.stat.event, dvr_stat_timer, a); evtimer_add(&a->dvr.stat.event, &tv); } static void dvr_stuck_init(struct adapter_s *a); static void dvr_stuck_timer(int fd, short event, void *arg) { struct adapter_s *adapter=arg; /* * Flexcop is known to randomly lockup. A workaround in the kernel * driver is to reset some registers known to reanimate the flexcop. * * See: http://lkml.org/lkml/2005/6/27/135 * * The patch went into 2.6.13-rc3 so in case you are running an FlexCop based * card (SkyStar2, AirStar) you better upgrade to 2.6.13 or better. * * First try was to retune which itself is not enough. One needs to bring * down the number of received pids to 0 as the transition from 0 -> 1 resets * the board. So let dmx bounce all filters. * */ if (adapter->dvr.stat.reads == 0) { logwrite(LOG_ERROR, "dvr: lockup of DVB card detected - trying to reanimate via bouncing filter"); fe_retune(adapter); dmx_bounce_filter(adapter); } adapter->dvr.stat.reads=0; dvr_stuck_init(adapter); } static void dvr_stuck_init(struct adapter_s *a) { struct timeval tv; /* stuck-interval 0; disables the stuck check timer */ if (!a->dvr.stuckinterval) return; tv.tv_sec=a->dvr.stuckinterval/1000; tv.tv_usec=a->dvr.stuckinterval%1000*1000; evtimer_set(&a->dvr.stucktimer, dvr_stuck_timer, a); evtimer_add(&a->dvr.stucktimer, &tv); } int dvr_init(struct adapter_s *a) { int dvrfd; a->dvr.buffer.ptr=malloc(a->dvr.buffer.size*TS_PACKET_SIZE); dvrfd=open(dvrname(a->no), O_RDONLY|O_NONBLOCK); if (dvrfd < 0) return 0; event_set(&a->dvr.dvrevent, dvrfd, EV_READ|EV_PERSIST, dvr_read, a); event_add(&a->dvr.dvrevent, NULL); if (a->budgetmode) { if (!dmx_join_pid(a, 0x2000, DMX_PES_OTHER)) { logwrite(LOG_INFO, "demux: Setting budget filter failed - switching off budget mode"); a->budgetmode=0; } } a->dvr.fd=dvrfd; dvr_init_stat_timer(a); dvr_stuck_init(a); return 1; } getstream2-20100616/libconf.h0000664000372000037200000000220111406140020014420 0ustar florfcorg enum { LCV_NONE, LCV_NUM, LCV_BOOL, LCV_HEX, LCV_STRING, LCV_IPV4ADDR, LCV_IPV6ADDR, LCV_IPADDR }; #define LCO_OPTIONAL (1<<0) /* Value is optional */ #define LCO_UNIQ (1<<1) /* Value must be locally unique */ #define LCO_LATECB (1<<2) /* Call childrens callback first */ struct lc_value { union { long num; /* NUM/HEX */ char *string; /* String and Addresses*/ }; }; struct lc_centry; struct lc_ventry { char *name; /* token name */ int min, max; /* min max occurance */ int type; /* type int/string/ipaddr */ int opt; /* options */ struct lc_ventry *child; /* child structures */ int (*cback)(struct lc_centry *ce, struct lc_value *val); }; struct lc_centry { struct lc_centry *prev,*next, *child,*parent; char *token, *value; int closed, tline, /* Line# of token */ vline, /* Line# of value */ noce; /* # of confentrys */ struct lc_ventry *ventry; /* Pre parsed values */ struct lc_value cbvalue; }; struct lc_centry *libconf_parse(char *c, off_t len); int libconf_validate(struct lc_centry *ce, struct lc_ventry *ve); void libconf_free(struct lc_centry *ce); getstream2-20100616/tsdecode.c0000664000372000037200000001062211406140020014577 0ustar florfcorg#include #include #include #include #include #include #include #include #include "getstream.h" #include "psi.h" #define TS_PID_OFF1 1 #define TS_PID_OFF2 2 #define TS_CC_OFF 3 #define TS_CC_MASK 0xf #define TS_AFC_OFF 3 #define TS_AFC_MASK 0x30 #define TS_AFC_SHIFT 4 #define TS_AFC_LEN 4 #define TS_HEAD_MIN 4 void _dump_hex(char *prefix, uint8_t *buf, int size) { int i; unsigned char ch; char sascii[17]; char linebuffer[16*4+1]; sascii[16]=0x0; for(i=0;i= ' ' && ch <= '}') sascii[i%16]=ch; else sascii[i%16]='.'; if (i%16 == 15) printf("%s %s %s\n", prefix, linebuffer, sascii); } /* i++ after loop */ if (i%16 != 0) { for(;i%16 != 0;i++) { sprintf(&linebuffer[(i%16)*3], " "); sascii[i%16]=' '; } printf("%s %s %s\n", prefix, linebuffer, sascii); } } static int pktno; void decodebits(unsigned int bits, unsigned int mask, unsigned int len, char *prefix, char *name) { char line[128]; int val=0, i, j=0; for(i=len-1;i>=0;i--) { if (mask & (1<data; unsigned int bits; unsigned int off; decodebits(pat[0], 0xff, 8, " ", "table_id"); bits=pat[1]<<8|pat[2]; decodebits(bits, 0x8000, 16, " ", "section syntax indicator"); decodebits(bits, 0x4000, 16, " ", "0"); decodebits(bits, 0x3000, 16, " ", "reserved"); decodebits(bits, 0x0fff, 16, " ", "section length"); bits=pat[3]<<8|pat[4]; printf(" 0x%04x transport stream id\n", bits); bits=pat[5]; decodebits(bits, 0xc0, 8, " ", "reserved"); decodebits(bits, 0x3e, 8, " ", "version"); decodebits(bits, 0x01, 8, " ", "current next indicator"); printf(" 0x%02x section number\n", pat[PAT_SECTION_OFF]); printf(" 0x%02x last section number\n", pat[PAT_LAST_SECTION_OFF]); off=PAT_HDR_LEN; printf(" Program_number program_map_pid\n"); while(off < _psi_len(pat)-4) { uint16_t pnr; uint16_t pid; pnr=pat[off]<<8|pat[off+1]; pid=(pat[off+2]<<8|pat[off+3])&PID_MASK; printf(" %04x %04x\n", pnr, pid); off+=4; } } void tsd_pat(uint8_t *ts, uint16_t pid) { int off=0; while(off < TS_PACKET_SIZE) { off=psi_reassemble(&patsec, ts, off); if (off < 0) break; printf("%u pid %04x new pat section complete\n", pktno, pid); tsd_pat_section_dump(&patsec); psi_update_table(&pat, &patsec); } } void tsd_packetin(uint8_t *ts) { uint16_t pid; if (!ts_sync(ts)) { fprintf(stderr, "%06d Missing sync\n", pktno); return; } if (ts_tei(ts)) fprintf(stderr, "%06d Packet has set TEI\n", pktno); pid=ts_pid(ts); switch(pid) { case(0): tsd_pat(ts, pid); break; } } int main(void ) { uint8_t tsbuf[TS_PACKET_SIZE]; int len, valid=0, toread, no; fd_set fdin; while(1) { toread=TS_PACKET_SIZE-valid; FD_ZERO(&fdin); FD_SET(fileno(stdin), &fdin); no=select(1, &fdin, NULL, NULL, NULL); if (!no) continue; len=read(fileno(stdin), &tsbuf, toread); if (len == 0 || len < 0) { printf("Aborting - short read %d/%d\n", len, toread); exit(0); } valid+=len; if (valid != TS_PACKET_SIZE) continue; pktno++; tsd_packetin(tsbuf); valid=0; } } getstream2-20100616/fe.c0000664000372000037200000004461711406140020013412 0ustar florfcorg#include #include #include #include #include #include #include #include #include #include #include #include "getstream.h" struct diseqc_cmd { struct dvb_diseqc_master_cmd cmd; uint32_t wait; }; struct diseqc_cmd switch_cmds[] = { { { { 0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf2, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf1, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf3, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf4, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf6, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf5, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf7, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf8, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xfa, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xf9, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xfb, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xfc, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xfe, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xfd, 0x00, 0x00 }, 4 }, 0 }, { { { 0xe0, 0x10, 0x38, 0xff, 0x00, 0x00 }, 4 }, 0 } }; static inline void msleep(uint32_t msec) { struct timespec req = { msec / 1000, 1000000 * (msec % 1000) }; while (nanosleep(&req, &req)) ; } int diseqc_send_msg (int fd, fe_sec_voltage_t v, struct diseqc_cmd **cmd, fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b) { int err; if ((err = ioctl(fd, FE_SET_TONE, SEC_TONE_OFF))) return err; if ((err = ioctl(fd, FE_SET_VOLTAGE, v))) return err; msleep(15); while (*cmd) { if ((err = ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &(*cmd)->cmd))) return err; msleep((*cmd)->wait); cmd++; } msleep(15); if ((err = ioctl(fd, FE_DISEQC_SEND_BURST, b))) return err; msleep(15); return ioctl(fd, FE_SET_TONE, t); } int setup_switch (int frontend_fd, int switch_pos, int voltage_18, int hiband) { struct diseqc_cmd *cmd[2] = { NULL, NULL }; int i = 4 * switch_pos + 2 * hiband + (voltage_18 ? 1 : 0); if (i < 0 || i >= (int) (sizeof(switch_cmds)/sizeof(struct diseqc_cmd))) return -EINVAL; cmd[0] = &switch_cmds[i]; return diseqc_send_msg (frontend_fd, i % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, cmd, (i/2) % 2 ? SEC_TONE_ON : SEC_TONE_OFF, (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A); } /* * Dump FrontEnd Status byte as clear text returned * by GET_STATUS or GET_EVENT ioctl * */ char *fe_decode_status(int status) { static char str[256]; str[0]=0x0; if (status & FE_HAS_SIGNAL) strcat(str, "HAS_SIGNAL "); if (status & FE_HAS_CARRIER) strcat(str, "HAS_CARRIER "); if (status & FE_HAS_VITERBI) strcat(str, "HAS_VITERBI "); if (status & FE_HAS_SYNC) strcat(str, "HAS_SYNC "); if (status & FE_HAS_LOCK) strcat(str, "HAS_LOCK "); if (status & FE_TIMEDOUT) strcat(str, "TIMEDOUT "); if (status & FE_REINIT) strcat(str, "REINIT "); /* Eliminate last space */ if (strlen(str) > 0) str[strlen(str)-1]=0x0; return str; }; static int fe_get_freqoffset(struct adapter_s *adapter) { int freqoffset; if (adapter->fe.dvbs.t_freq <= 2200000) return adapter->fe.dvbs.t_freq; if (adapter->fe.dvbs.lnb_slof) { /* Ku Band LNB */ if (adapter->fe.dvbs.t_freq < adapter->fe.dvbs.lnb_slof) { freqoffset=(adapter->fe.dvbs.t_freq- adapter->fe.dvbs.lnb_lof1); } else { freqoffset=(adapter->fe.dvbs.t_freq- adapter->fe.dvbs.lnb_lof2); } } else { /* C Band LNB */ if (adapter->fe.dvbs.lnb_lof2) { if (adapter->fe.dvbs.t_pol == POL_H) { freqoffset=(adapter->fe.dvbs.lnb_lof2- adapter->fe.dvbs.t_freq); } else { freqoffset=(adapter->fe.dvbs.lnb_lof1- adapter->fe.dvbs.t_freq); } } else { freqoffset=adapter->fe.dvbs.lnb_lof1-adapter->fe.dvbs.t_freq; } } return freqoffset; } static int fe_get_voltage(struct adapter_s *adapter) { return (adapter->fe.dvbs.t_pol == POL_H) ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13; } static int fe_is_highband(struct adapter_s *adapter) { return (adapter->fe.dvbs.t_freq > adapter->fe.dvbs.lnb_slof); } static int fe_get_tone(struct adapter_s *adapter) { return (fe_is_highband(adapter) ? SEC_TONE_ON : SEC_TONE_OFF); } static int fe_tune_dvbs(struct adapter_s *adapter) { struct dvb_frontend_parameters feparams; int voltage, tone=SEC_TONE_OFF; memset(&feparams, 0, sizeof(struct dvb_frontend_parameters)); voltage=fe_get_voltage(adapter); if (adapter->fe.dvbs.t_freq > 2200000) { if (adapter->fe.dvbs.lnb_slof) { /* Ku Band LNB */ if (adapter->fe.dvbs.t_freq < adapter->fe.dvbs.lnb_slof) { feparams.frequency=(adapter->fe.dvbs.t_freq- adapter->fe.dvbs.lnb_lof1); tone = SEC_TONE_OFF; } else { feparams.frequency=(adapter->fe.dvbs.t_freq- adapter->fe.dvbs.lnb_lof2); tone = SEC_TONE_ON; } } else { /* C Band LNB */ if (adapter->fe.dvbs.lnb_lof2) { if (adapter->fe.dvbs.t_pol == POL_H) { feparams.frequency=(adapter->fe.dvbs.lnb_lof2- adapter->fe.dvbs.t_freq); } else { feparams.frequency=(adapter->fe.dvbs.lnb_lof1- adapter->fe.dvbs.t_freq); } } else { feparams.frequency=adapter->fe.dvbs.lnb_lof1-adapter->fe.dvbs.t_freq; } } } else { feparams.frequency=adapter->fe.dvbs.t_freq; } feparams.inversion=INVERSION_AUTO; DVBS_SET_SYMBOLRATE(&feparams, adapter->fe.dvbs.t_srate); DVBS_SET_FEC(&feparams, FEC_AUTO); if (adapter->fe.dvbs.lnbsharing) { int value=SEC_TONE_OFF; logwrite(LOG_DEBUG, "fe: Adapter %d lnb-sharing active - trying to turn off", adapter->no); if (ioctl(adapter->fe.fd, FE_SET_TONE, value) < 0) { logwrite(LOG_ERROR, "fe: Adapter %d ioctl FE_SET_TONE failed - %s", adapter->no, strerror(errno)); } value=SEC_VOLTAGE_OFF; if (ioctl(adapter->fe.fd, FE_SET_VOLTAGE, value) < 0) { if (errno == EINVAL) { logwrite(LOG_DEBUG, "fe: Adapter %d SEC_VOLTAGE_OFF not possible", adapter->no); if (ioctl(adapter->fe.fd, FE_SET_VOLTAGE, voltage) < 0) { logwrite(LOG_ERROR, "fe: Adapter %d ioctl FE_SET_VOLTAGE failed - %s", adapter->no, strerror(errno)); } } } } else if (adapter->fe.dvbs.t_diseqc) { if (setup_switch(adapter->fe.fd, adapter->fe.dvbs.t_diseqc-1, voltage, (tone == SEC_TONE_ON))) { logwrite(LOG_ERROR, "fe: diseqc failed to send"); exit(-1); } logwrite(LOG_DEBUG, "fe: diseqc send successful"); sleep(1); } else { if (ioctl(adapter->fe.fd, FE_SET_VOLTAGE, voltage) < 0) { logwrite(LOG_ERROR, "fe: ioctl FE_SET_VOLTAGE failed - %s", strerror(errno)); } if (ioctl(adapter->fe.fd, FE_SET_TONE, tone) < 0) { logwrite(LOG_ERROR, "fe: ioctl FE_SET_TONE failed - %s", strerror(errno)); } } logwrite(LOG_INFO, "fe: DVB-S tone = %d", tone); logwrite(LOG_INFO, "fe: DVB-S voltage = %d", voltage); logwrite(LOG_INFO, "fe: DVB-S diseqc = %d", adapter->fe.dvbs.t_diseqc); logwrite(LOG_INFO, "fe: DVB-S freq = %lu", adapter->fe.dvbs.t_freq); logwrite(LOG_INFO, "fe: DVB-S lof1 = %lu", adapter->fe.dvbs.lnb_lof1); logwrite(LOG_INFO, "fe: DVB-S lof2 = %lu", adapter->fe.dvbs.lnb_lof2); logwrite(LOG_INFO, "fe: DVB-S slof = %lu", adapter->fe.dvbs.lnb_slof); logwrite(LOG_INFO, "fe: DVB-S feparams.frequency = %d", feparams.frequency); logwrite(LOG_INFO, "fe: DVB-S feparams.inversion = %d", feparams.inversion); logwrite(LOG_INFO, "fe: DVB-S feparams.u.qpsk.symbol_rate = %d", adapter->fe.dvbs.t_srate); if (ioctl(adapter->fe.fd, FE_SET_FRONTEND, &feparams) < 0) { logwrite(LOG_ERROR, "fe: ioctl FE_SET_FRONTEND failed - %s", strerror(errno)); exit(-1); } return 0; } int fe_tune_dvbt(struct adapter_s *adapter) { struct dvb_frontend_parameters feparams; memset(&feparams, 0, sizeof(struct dvb_frontend_parameters)); feparams.frequency = adapter->fe.dvbt.freq; feparams.inversion = INVERSION_AUTO; DVBT_SET_CODERATE_HP(&feparams, FEC_AUTO); DVBT_SET_CODERATE_LP(&feparams, FEC_AUTO); switch(adapter->fe.dvbt.bandwidth) { case(0): DVBT_SET_BANDWIDTH(&feparams, BANDWIDTH_AUTO); break; case(6): DVBT_SET_BANDWIDTH(&feparams, BANDWIDTH_6_MHZ); break; case(7): DVBT_SET_BANDWIDTH(&feparams, BANDWIDTH_7_MHZ); break; case(8): DVBT_SET_BANDWIDTH(&feparams, BANDWIDTH_8_MHZ); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T bandwidth %d", adapter->fe.dvbt.bandwidth); exit(-1); } switch(adapter->fe.dvbt.modulation) { case(0): DVBT_SET_MODULATION(&feparams, QAM_AUTO); break; case(16):DVBT_SET_MODULATION(&feparams, QAM_16); break; case(32):DVBT_SET_MODULATION(&feparams, QAM_32); break; case(64):DVBT_SET_MODULATION(&feparams, QAM_64); break; case(128):DVBT_SET_MODULATION(&feparams, QAM_128); break; case(256):DVBT_SET_MODULATION(&feparams, QAM_256); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T modulation %d", adapter->fe.dvbt.modulation); exit(-1); } switch(adapter->fe.dvbt.tmode) { case(0):DVBT_SET_TMODE(&feparams, TRANSMISSION_MODE_AUTO); break; case(2):DVBT_SET_TMODE(&feparams, TRANSMISSION_MODE_2K); break; case(8):DVBT_SET_TMODE(&feparams, TRANSMISSION_MODE_8K); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T transmission mode %d", adapter->fe.dvbt.tmode); exit(-1); } switch(adapter->fe.dvbt.guard) { case(0):DVBT_SET_GUARD(&feparams, GUARD_INTERVAL_AUTO); break; case(4):DVBT_SET_GUARD(&feparams, GUARD_INTERVAL_1_4); break; case(8):DVBT_SET_GUARD(&feparams, GUARD_INTERVAL_1_8); break; case(16):DVBT_SET_GUARD(&feparams, GUARD_INTERVAL_1_16); break; case(32):DVBT_SET_GUARD(&feparams, GUARD_INTERVAL_1_32); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T guard interval %d", adapter->fe.dvbt.guard); exit(-1); } switch(adapter->fe.dvbt.hierarchy) { case(-1):DVBT_SET_HIERARCHY(&feparams, HIERARCHY_NONE); break; case(0):DVBT_SET_HIERARCHY(&feparams, HIERARCHY_AUTO); break; case(1):DVBT_SET_HIERARCHY(&feparams, HIERARCHY_1); break; case(2):DVBT_SET_HIERARCHY(&feparams, HIERARCHY_2); break; case(4):DVBT_SET_HIERARCHY(&feparams, HIERARCHY_4); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T hierarchy %d", adapter->fe.dvbt.hierarchy); exit(-1); } if (ioctl(adapter->fe.fd, FE_SET_FRONTEND, &feparams) < 0) { logwrite(LOG_ERROR, "ioctl FE_SET_FRONTEND failed"); exit(-1); } return 0; } #if (DVB_API_VERSION>=5) static int fe_tune_dvbs2(struct adapter_s *adapter) { struct dtv_property p[DTV_IOCTL_MAX_MSGS]; struct dtv_properties cmds; p[0].cmd = DTV_CLEAR; p[1].cmd = DTV_DELIVERY_SYSTEM; p[1].u.data = SYS_DVBS2; p[2].cmd = DTV_SYMBOL_RATE; p[2].u.data = adapter->fe.dvbs.t_srate; p[3].cmd = DTV_INNER_FEC; p[3].u.data = FEC_AUTO; p[4].cmd = DTV_INVERSION; p[4].u.data = INVERSION_AUTO; p[5].cmd = DTV_FREQUENCY; p[5].u.data = fe_get_freqoffset(adapter); p[6].cmd = DTV_VOLTAGE; p[6].u.data = fe_get_voltage(adapter); p[7].cmd = DTV_TONE; p[7].u.data = fe_get_tone(adapter); p[8].cmd = DTV_TUNE; p[8].u.data = 0; cmds.num=9; cmds.props=p; if (ioctl(adapter->fe.fd, FE_SET_PROPERTY, &cmds) < 0) { logwrite(LOG_ERROR, "fe: ioctl FE_SET_PROPERTY failed - %s", strerror(errno)); exit(-1); } return 0; } #else static int fe_tune_dvbs2(struct adapter_s *adapter) { logwrite(LOG_ERROR, "fe: not compiled against DVB Api 5 - no DVB-S2 support"); exit(-1); } #endif static int fe_tune_dvbc(struct adapter_s *adapter) { struct dvb_frontend_parameters feparams; memset(&feparams, 0, sizeof(struct dvb_frontend_parameters)); feparams.frequency = adapter->fe.dvbc.freq; DVBC_SET_SYMBOLRATE(&feparams, adapter->fe.dvbc.srate); feparams.inversion=INVERSION_AUTO; switch(adapter->fe.dvbc.modulation) { case -1: DVBC_SET_MODULATION(&feparams, QPSK); break; case 0: DVBC_SET_MODULATION(&feparams, QAM_AUTO); break; case 16: DVBC_SET_MODULATION(&feparams, QAM_16); break; case 32: DVBC_SET_MODULATION(&feparams, QAM_32); break; case 64: DVBC_SET_MODULATION(&feparams, QAM_64); break; case 128: DVBC_SET_MODULATION(&feparams, QAM_128); break; case 256: DVBC_SET_MODULATION(&feparams, QAM_256); break; default: logwrite(LOG_ERROR, "Unknown modulation %d", adapter->fe.dvbc.modulation); exit(-1); } switch(adapter->fe.dvbc.fec) { case 0: DVBC_SET_FEC(&feparams, FEC_NONE); break; case 1: DVBC_SET_FEC(&feparams, FEC_1_2); break; case 2: DVBC_SET_FEC(&feparams, FEC_2_3); break; case 3: DVBC_SET_FEC(&feparams, FEC_3_4); break; case 4: DVBC_SET_FEC(&feparams, FEC_4_5); break; case 5: DVBC_SET_FEC(&feparams, FEC_5_6); break; case 6: DVBC_SET_FEC(&feparams, FEC_6_7); break; case 7: DVBC_SET_FEC(&feparams, FEC_7_8); break; case 8: DVBC_SET_FEC(&feparams, FEC_8_9); break; case 9: DVBC_SET_FEC(&feparams, FEC_AUTO); break; default: logwrite(LOG_ERROR, "Unknown fec %d", adapter->fe.dvbc.fec); exit(-1); } if (ioctl(adapter->fe.fd, FE_SET_FRONTEND, &feparams) < 0) { logwrite(LOG_ERROR, "ioctl FE_SET_FRONTEND failed"); exit(-1); } return 0; } static int fe_tune(struct adapter_s *adapter) { switch(adapter->type) { case(AT_DVBS2): fe_tune_dvbs2(adapter); break; case(AT_DVBS): fe_tune_dvbs(adapter); break; case(AT_DVBT): fe_tune_dvbt(adapter); break; case(AT_DVBC): fe_tune_dvbc(adapter); break; } adapter->fe.tunelast=time(NULL); return 0; } #define FE_TUNE_MINDELAY 5 void fe_retune(struct adapter_s *adapter) { time_t now; now=time(NULL); /* Debounce the retuning */ if (adapter->fe.tunelast + FE_TUNE_MINDELAY > now) return; fe_tune(adapter); } static void fe_timer_init(struct adapter_s *adapter); static void fe_check_status(int fd, short event, void *arg) { struct adapter_s *adapter=arg; fe_status_t status; int res; res=ioctl(adapter->fe.fd, FE_READ_STATUS, &status); if (res == 0) { if (!(status & FE_HAS_LOCK)) { logwrite(LOG_INFO, "fe: Adapter %d Status: 0x%02x (%s)", adapter->no, status, fe_decode_status(status)); fe_retune(adapter); } } fe_timer_init(adapter); } #define FE_CHECKSTATUS_INTERVAL 5 static void fe_timer_init(struct adapter_s *adapter) { struct timeval tv; tv.tv_sec=FE_CHECKSTATUS_INTERVAL; tv.tv_usec=0; evtimer_set(&adapter->fe.timer, fe_check_status, adapter); evtimer_add(&adapter->fe.timer, &tv); } /* * We had an event on the frontend filedescriptor - poll the event * and dump the status * */ static void fe_event(int fd, short ev, void *arg) { struct adapter_s *adapter=arg; int res, status; struct dvb_frontend_event event; res=ioctl(adapter->fe.fd, FE_GET_EVENT, &event); if (res < 0 && errno != EOVERFLOW) { logwrite(LOG_ERROR, "fe: Adapter %d Status event overflow %d", adapter->no, errno); return; } status=event.status; if (res >= 0 && status) { if (!(status & FE_TIMEDOUT)) { logwrite(LOG_INFO, "fe: Adapter %d Status: 0x%02x (%s)", adapter->no, status, fe_decode_status(status)); if (!(status & FE_HAS_LOCK)) { fe_retune(adapter); } } } } #if (DVB_API_VERSION>=5) static int fe_api5_checkcap(struct adapter_s *adapter) { struct dtv_property p[1]; struct dtv_properties cmds; p[0].cmd = DTV_DELIVERY_SYSTEM; cmds.props = p; cmds.num = 1; if (ioctl(adapter->fe.fd, FE_GET_PROPERTY, &cmds)) { logwrite(LOG_DEBUG, "fe: ioctl(FE_GET_PROPERTY) failed - no DVBS2 aka API 5 support?"); return 0; } switch (p[0].u.data) { case(SYS_DVBS): if (adapter->type == AT_DVBS) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-S card - config is not for DVB-S", adapter->no); exit(-1); case(SYS_DVBS2): if (adapter->type == AT_DVBS || adapter->type == AT_DVBS2) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-S2 card - config is not DVB-S or S2", adapter->no); exit(-1); case(SYS_DVBT): if (adapter->type == AT_DVBT) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-T card - config is not for DVB-T", adapter->no); exit(-1); case(SYS_DVBC_ANNEX_B): case(SYS_DVBC_ANNEX_AC): if (adapter->type == AT_DVBC) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-C card - config is not for DVB-C", adapter->no); exit(-1); } return 1; } #else static int fe_api5_checkcap(struct adapter_s *adapter) { return 0; } #endif static void fe_api3_checkcap(struct adapter_s *adapter) { char *type="unknown"; if (ioctl(adapter->fe.fd, FE_GET_INFO, &adapter->fe.feinfo)) { logwrite(LOG_ERROR, "fe: ioctl(FE_GET_INFO...) failed"); exit(-1); } switch(adapter->fe.feinfo.type) { case(FE_QPSK): type="QPSK"; if (adapter->type == AT_DVBS) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-S card - config is not for DVB-S", adapter->no); break; //exit(-1); case(FE_OFDM): type="OFDM"; if (adapter->type == AT_DVBT) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-T card - config is not for DVB-T", adapter->no); exit(-1); case(FE_QAM): type="QAM"; if (adapter->type == AT_DVBC) break; logwrite(LOG_ERROR, "fe: Adapter %d is an DVB-C card - config is not for DVB-C", adapter->no); exit(-1); default: logwrite(LOG_ERROR, "fe: Adapter %d is an unknown card type %d", adapter->no, adapter->fe.feinfo.type); break; } if (adapter->fe.feinfo.type == FE_QPSK && !(adapter->fe.feinfo.caps & FE_CAN_FEC_AUTO)) { logwrite(LOG_ERROR, "fe: adapter %d is incapable of handling FEC_AUTO - please report to flo@rfc822.org", adapter->no); } logwrite(LOG_DEBUG, "fe: adapter %d type %s name \"%s\"", adapter->no, type, adapter->fe.feinfo.name); logwrite(LOG_DEBUG, "fe: adapter %d frequency min %d max %d step %d tolerance %d", adapter->no, adapter->fe.feinfo.frequency_min, adapter->fe.feinfo.frequency_max, adapter->fe.feinfo.frequency_stepsize, adapter->fe.feinfo.frequency_tolerance); logwrite(LOG_DEBUG, "fe: adapter %d symbol rate min %d max %d tolerance %d", adapter->no, adapter->fe.feinfo.symbol_rate_min, adapter->fe.feinfo.symbol_rate_max, adapter->fe.feinfo.symbol_rate_tolerance); } static void fe_checkcap(struct adapter_s *adapter) { if (fe_api5_checkcap(adapter)) return; fe_api3_checkcap(adapter); return; } int fe_tune_init(struct adapter_s *adapter) { char fename[128]; sprintf(fename, "/dev/dvb/adapter%d/frontend0", adapter->no); adapter->fe.fd=open(fename, O_RDWR|O_NONBLOCK); if (adapter->fe.fd < 0) { logwrite(LOG_ERROR, "Error opening dvb frontend %s", fename); exit(-1); } logwrite(LOG_INFO, "fe: Adapter %d Setting up frontend tuner", adapter->no); fe_checkcap(adapter); /* Single shot - try to tune */ fe_tune(adapter); /* Watch the filedescriptor for frontend events */ event_set(&adapter->fe.event, adapter->fe.fd, EV_READ|EV_PERSIST, fe_event, adapter); event_add(&adapter->fe.event, NULL); /* Create a timer to regular poll the status */ fe_timer_init(adapter); return 0; } getstream2-20100616/input.c0000664000372000037200000000204511406140020014144 0ustar florfcorg #include "getstream.h" static void input_init_pnr(struct input_s *input) { input->pnr.program=pmt_join_pnr(input->stream->adapter, input->pnr.pnr, stream_send, input->stream); } /* * Static PID input - Just add a callback to the dvr demux (e.g. adapter pidtable) * and direct it to the stream input callback * */ static void input_init_pid(struct input_s *input) { input->pid.cbkey=dvr_add_pcb(input->stream->adapter, input->pid.pid, DVRCB_TS, PID_STATIC, stream_send, input->stream); } static void input_init_full(struct input_s *input) { input->pid.cbkey=dvr_add_pcb(input->stream->adapter, PID_MAX+1, DVRCB_TS, PID_STATIC, stream_send, input->stream); } void input_init(struct input_s *input) { switch(input->type) { case(INPUT_PNR): input_init_pnr(input); break; case(INPUT_PID): input_init_pid(input); break; case(INPUT_FULL): input_init_full(input); break; default: logwrite(LOG_ERROR, "input: Unknown input type %d", input->type); break; } } getstream2-20100616/getstream.h0000664000372000037200000001751111406140020015011 0ustar florfcorg#ifndef GETSTREAM_H #define GETSTREAM_H #include #include #include #include #include #include #include #include #include "sap.h" #include "psi.h" enum { AT_DVBS, AT_DVBS2, AT_DVBT, AT_DVBC }; enum { POL_H, POL_V }; #define DVBC_SET_SYMBOLRATE(a, b) (a)->u.qam.symbol_rate=(b) #define DVBC_SET_MODULATION(a, b) (a)->u.qam.modulation=(b) #define DVBC_SET_FEC(a, b) (a)->u.qam.fec_inner=(b) #define DVBT_SET_BANDWIDTH(a, b) (a)->u.ofdm.bandwidth=(b) #define DVBT_SET_MODULATION(a, b) (a)->u.ofdm.constellation=(b) #define DVBT_SET_TMODE(a, b) (a)->u.ofdm.transmission_mode=(b) #define DVBT_SET_GUARD(a, b) (a)->u.ofdm.guard_interval=(b) #define DVBT_SET_HIERARCHY(a, b) (a)->u.ofdm.hierarchy_information=(b) #define DVBT_SET_CODERATE_HP(a, b) (a)->u.ofdm.code_rate_HP=(b) #define DVBT_SET_CODERATE_LP(a, b) (a)->u.ofdm.code_rate_LP=(b) #define DVBS_SET_SYMBOLRATE(a, b) (a)->u.qpsk.symbol_rate=(b) #define DVBS_SET_FEC(a, b) (a)->u.qpsk.fec_inner=(b) #define DEBUG #ifdef DEBUG #define dprintf printf #else #define dprintf( a... ) #endif #define MAX_CTL_MSG_SIZE 1500 /* * * * getstream.c * * */ extern int loglevel; #define MAX_MCAST_PAYLOAD (1500-40) #define TS_PACKET_SIZE 188 #define DVR_BUFFER_DEFAULT 50 /* I have seen an average of 44 Packets per read */ #define PID_MAX 0x1fff #define DVR_DEFAULT_STUCKINT 5000 /* 5 seconds stuck check interval */ enum { PID_NONE, PID_PAT, PID_PMT, PID_PCR, PID_VIDEO, PID_AUDIO, PID_PRIVATE, PID_USER, PID_STATIC, PID_REASSEMBLE, PID_OTHER, }; enum { DVRCB_TS, DVRCB_SECTION, }; extern const char *pidtnames[]; struct pat_s { uint16_t tid; unsigned int progcount; GList *program; }; enum { INPUT_NONE, INPUT_PID, INPUT_PNR, INPUT_FULL }; struct input_s { struct stream_s *stream; int type; struct { uint16_t pid; void *cbkey; } pid; struct { uint16_t pnr; void *program; } pnr; }; struct stream_s { char *name; struct adapter_s *adapter; GList *output; GList *input; int psineeded; /* True if PNR inputs - we need to generate PAT & PMT(s) */ uint8_t patcc; struct event patevent; }; struct dvrpt_s { GList *callback; GList *sectioncallback; unsigned long packets; struct psisec_s *section; unsigned int secuser; void *seccb; }; struct adapter_s { int no; /* Adapter Number */ int type; /* Adapter Type - DVB-S/DVB-T/DVB-C */ int budgetmode; GList *streams; /* fe.c */ struct { int fd; struct event timer; struct event event; time_t tunelast; struct dvb_frontend_info feinfo; union { struct { /* Tuning information DVB-S */ int lnbsharing; unsigned long lnb_lof1; unsigned long lnb_lof2; unsigned long lnb_slof; unsigned long t_freq; int t_pol; /* POL_H || POL_V */ unsigned long t_srate; int t_diseqc; } dvbs; struct { /* Tuning information DVB-T */ unsigned long freq; int bandwidth; /* 0 (Auto) 6 (6Mhz), 7 (7Mhz), 8 (8Mhz) */ int tmode; /* 0 (Auto) 2 (2Khz), 8 (8Khz) */ int modulation; /* 0, 16, 32, 64, 128, 256 QUAM */ int guard; /* 0, 4, 8, 16, 32 1/x Guard */ int hierarchy; /* 0, -1, 1, 2, 4 - 0 Auto, -1 None */ } dvbt; struct { /* Tuning information DVB-C */ unsigned long freq; unsigned long srate; int modulation; /* -1 (QPSK), 0, 16, 32, 64, 128, 256 QAM */ int fec; /* 0 (Auto) - 9 */ } dvbc; }; } fe; /* dvr */ struct { int fd; struct event dvrevent; struct event stucktimer; time_t lastinput; int stuckinterval; /* List of full stream callbacks */ GList *fullcb; struct dvrpt_s pidtable[PID_MAX+1]; struct { int size; /* Config option */ uint8_t *ptr; } buffer; uint8_t pidcc[PID_MAX+1]; struct { unsigned int reads; /* Reads in the stats interval */ int interval; /* Config option */ time_t last; struct event event; } stat; } dvr; struct { struct event dmxevent; /* dmx */ struct { int fd; int type; } pidtable[PID_MAX+1+1]; /* Space for 0x2000 pid structures */ } dmx; /* PAT */ struct { void *cbc; struct psi_s psi; struct pat_s *last; struct pat_s *current; } pat; struct { GList *pnrlist; } pmt; }; /* * * * demux.c * * * */ void demux_init(struct adapter_s *adapter); void *demux_add_pid(struct adapter_s *a, uint16_t pid, void (*demuxcb)(uint8_t *ts, void *arg), void *arg); void *demux_add_pnr(struct adapter_s *a, uint16_t pnr, void (*demuxcb)(uint8_t *ts, void *arg), void *arg); /* * * * fe.c * * */ int fe_tune_init(struct adapter_s *adapter); void fe_retune(struct adapter_s *adapter); /* * * * util.c * * */ int addr_is_mcast(char *addr); void dump_hex(int level, const char *prefix, uint8_t *buf, int size); void ts_packet_decode(uint8_t *ts); /* * * * stream.c * * * */ void stream_init(struct stream_s *stream); void stream_send(void *data, void *arg); /* * * * input.c * * */ void input_init(struct input_s *input); /* * * logging * * */ void logwrite_inc_level(void ); void logwrite(int level, const char *format, ...); enum { LOG_ERROR, LOG_INFO, LOG_DEBUG, LOG_EVENT, LOG_XTREME, }; /* * * * dvr.c * * * */ int dvr_init(struct adapter_s *adapter); void *dvr_add_pcb(struct adapter_s *a, unsigned int pid, unsigned int type, unsigned int pidt, void (*callback)(void *data, void *arg), void *arg); void dvr_del_pcb(struct adapter_s *a, unsigned int pid, void *cbs); /* * * * dmx.c * * * */ int dmx_init(struct adapter_s *adapter); int dmx_join_pid(struct adapter_s *a, unsigned int pid, int type); void dmx_leave_pid(struct adapter_s *a, int pid); void dmx_bounce_filter(struct adapter_s *adapter); /* * * pat.c * */ void pat_section_add(struct adapter_s *a, struct psisec_s *section); void pat_addpmtpid(struct pat_s *pat, uint16_t pnr, uint16_t pid); void pat_free(struct pat_s *pat); struct pat_s *pat_new(); void pat_add_program(struct pat_s *pat, uint16_t pnr, uint16_t pid); unsigned int pat_send(struct pat_s *pat, uint8_t cc, uint8_t version, uint16_t tid, void (*callback)(void *data, void *arg), void *arg); void pat_init(struct adapter_s *adapter); /* * * pmt.c * */ void pmt_pidfrompat(struct adapter_s *a, unsigned int pnr, unsigned int pmtpid); void *pmt_join_pnr(struct adapter_s *a, unsigned int pnr, \ void (*callback)(void *data, void *arg), void *arg); unsigned int pmt_get_pmtpid(void *program); #define PID_MASK 0x1fff #define TS_SYNC_OFF 0 #define TS_SYNC 0x47 #define TS_PID_OFF1 1 #define TS_PID_OFF2 2 #define TS_PUSI_OFF 1 #define TS_PUSI_MASK 0x40 #define TS_AFC_OFF 3 #define TS_AFC_MASK 0x30 #define TS_AFC_SHIFT 4 #define TS_HEAD_MIN 4 #define TS_AFC_LEN 4 #define TS_CC_OFF 3 #define TS_CC_MASK 0xf #define TS_AF_OFF 4 #define TS_PAYLOAD_OFF 4 static inline unsigned int ts_has_payload(uint8_t *tsp) { int afc; afc=(tsp[TS_AFC_OFF] & TS_AFC_MASK) >> TS_AFC_SHIFT; return(afc & 0x1); } static inline unsigned int ts_afc(uint8_t *tsp) { return ((tsp[TS_AFC_OFF] & TS_AFC_MASK) >> TS_AFC_SHIFT); } static inline unsigned int ts_has_af(uint8_t *tsp) { return (ts_afc(tsp) & 0x2); } static inline unsigned int ts_af_len(uint8_t *tsp) { return (ts_has_af(tsp) ? tsp[TS_AFC_LEN]+1 : 0); } static inline unsigned int ts_payload_start(uint8_t *tsp) { return ts_af_len(tsp)+TS_HEAD_MIN; } static inline unsigned int ts_pusi(uint8_t *tsp) { return tsp[TS_PUSI_OFF] & TS_PUSI_MASK; } #define ts_pid(ts) \ (((ts)[TS_PID_OFF1]<<8|(ts)[TS_PID_OFF2])&PID_MASK) #if 0 static inline unsigned int ts_pid(uint8_t *tsp) { return (tsp[TS_PID_OFF1]<<8|tsp[TS_PID_OFF2])&PID_MASK; } #endif static inline unsigned int ts_tei(uint8_t *tsp) { return (tsp[TS_PID_OFF1] & 0x80); } static inline unsigned int ts_cc(uint8_t *tsp) { return (tsp[TS_CC_OFF] & TS_CC_MASK); } #endif getstream2-20100616/crc32.c0000664000372000037200000006045611406140020013733 0ustar florfcorg/* * Oct 15, 2000 Matt Domsch * Nicer crc32 functions/docs submitted by linux@horizon.com. Thanks! * Code was from the public domain, copyright abandoned. Code was * subsequently included in the kernel, thus was re-licensed under the * GNU GPL v2. * * Oct 12, 2000 Matt Domsch * Same crc32 function was used in 5 other places in the kernel. * I made one version, and deleted the others. * There are various incantations of crc32(). Some use a seed of 0 or ~0. * Some xor at the end with ~0. The generic crc32() function takes * seed as an argument, and doesn't xor at the end. Then individual * users can do whatever they need. * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0. * fs/jffs2 uses seed 0, doesn't xor with ~0. * fs/partitions/efi.c uses seed ~0, xor's with ~0. * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #include #include /* * There are multiple 16-bit CRC polynomials in common use, but this is * *the* standard CRC-32 polynomial, first popularized by Ethernet. * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */ #define CRCPOLY_LE 0xedb88320 #define CRCPOLY_BE 0x04c11db7 /* How many bits at a time to use. Requires a table of 4< 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 # error CRC_LE_BITS must be a power of 2 between 1 and 8 #endif /* * Big-endian CRC computation. Used with serial bit streams sent * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. */ #if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 # error CRC_BE_BITS must be a power of 2 between 1 and 8 #endif #define __swab32(x) \ __extension__ ({ \ uint32_t __x = (x); \ ((uint32_t)( \ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ }) #define ___constant_swab32(x) \ ((uint32_t)( \ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) #ifdef __LITTLE_ENDIAN #define __constant_cpu_to_le32(x) ((uint32_t)(x)) #define __constant_cpu_to_be32(x) (___constant_swab32((x))) #define __cpu_to_le32(x) ((uint32_t)(x)) #define __cpu_to_be32(x) (__swab32((uint32_t)(x))) #define __le32_to_cpu(x) ((uint32_t)(x)) #define __be32_to_cpu(x) __swab32((uint32_t)(x)) #else #define __constant_cpu_to_le32(x) (___constant_swab32((x))) #define __constant_cpu_to_be32(x) ((uint32_t)(x)) #define __cpu_to_le32(x) (__swab32((uint32_t)(x))) #define __cpu_to_be32(x) ((uint32_t)(x)) #define __le32_to_cpu(x) __swab32((uint32_t)(x)) #define __be32_to_cpu(x) ((uint32_t)(x)) #endif #if CRC_LE_BITS == 8 #define tole(x) __constant_cpu_to_le32(x) #define tobe(x) __constant_cpu_to_be32(x) #else #define tole(x) (x) #define tobe(x) (x) #endif static const uint32_t crc32table_le[] = { tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL), tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L), tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L), tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L), tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL), tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L), tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL), tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L), tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L), tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL), tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L), tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L), tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L), tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL), tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L), tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL), tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL), tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L), tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L), tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L), tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL), tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L), tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL), tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L), tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L), tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL), tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L), tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L), tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L), tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL), tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L), tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL), tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL), tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L), tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L), tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L), tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL), tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L), tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL), tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L), tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L), tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL), tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L), tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L), tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L), tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL), tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L), tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL), tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL), tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L), tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L), tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L), tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL), tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L), tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL), tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L), tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L), tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL), tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L), tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L), tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L), tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL), tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L), tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL) }; static const uint32_t crc32table_be[] = { tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L), tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L), tobe(0x2608edb8L), tobe(0x22c9f00fL), tobe(0x2f8ad6d6L), tobe(0x2b4bcb61L), tobe(0x350c9b64L), tobe(0x31cd86d3L), tobe(0x3c8ea00aL), tobe(0x384fbdbdL), tobe(0x4c11db70L), tobe(0x48d0c6c7L), tobe(0x4593e01eL), tobe(0x4152fda9L), tobe(0x5f15adacL), tobe(0x5bd4b01bL), tobe(0x569796c2L), tobe(0x52568b75L), tobe(0x6a1936c8L), tobe(0x6ed82b7fL), tobe(0x639b0da6L), tobe(0x675a1011L), tobe(0x791d4014L), tobe(0x7ddc5da3L), tobe(0x709f7b7aL), tobe(0x745e66cdL), tobe(0x9823b6e0L), tobe(0x9ce2ab57L), tobe(0x91a18d8eL), tobe(0x95609039L), tobe(0x8b27c03cL), tobe(0x8fe6dd8bL), tobe(0x82a5fb52L), tobe(0x8664e6e5L), tobe(0xbe2b5b58L), tobe(0xbaea46efL), tobe(0xb7a96036L), tobe(0xb3687d81L), tobe(0xad2f2d84L), tobe(0xa9ee3033L), tobe(0xa4ad16eaL), tobe(0xa06c0b5dL), tobe(0xd4326d90L), tobe(0xd0f37027L), tobe(0xddb056feL), tobe(0xd9714b49L), tobe(0xc7361b4cL), tobe(0xc3f706fbL), tobe(0xceb42022L), tobe(0xca753d95L), tobe(0xf23a8028L), tobe(0xf6fb9d9fL), tobe(0xfbb8bb46L), tobe(0xff79a6f1L), tobe(0xe13ef6f4L), tobe(0xe5ffeb43L), tobe(0xe8bccd9aL), tobe(0xec7dd02dL), tobe(0x34867077L), tobe(0x30476dc0L), tobe(0x3d044b19L), tobe(0x39c556aeL), tobe(0x278206abL), tobe(0x23431b1cL), tobe(0x2e003dc5L), tobe(0x2ac12072L), tobe(0x128e9dcfL), tobe(0x164f8078L), tobe(0x1b0ca6a1L), tobe(0x1fcdbb16L), tobe(0x018aeb13L), tobe(0x054bf6a4L), tobe(0x0808d07dL), tobe(0x0cc9cdcaL), tobe(0x7897ab07L), tobe(0x7c56b6b0L), tobe(0x71159069L), tobe(0x75d48ddeL), tobe(0x6b93dddbL), tobe(0x6f52c06cL), tobe(0x6211e6b5L), tobe(0x66d0fb02L), tobe(0x5e9f46bfL), tobe(0x5a5e5b08L), tobe(0x571d7dd1L), tobe(0x53dc6066L), tobe(0x4d9b3063L), tobe(0x495a2dd4L), tobe(0x44190b0dL), tobe(0x40d816baL), tobe(0xaca5c697L), tobe(0xa864db20L), tobe(0xa527fdf9L), tobe(0xa1e6e04eL), tobe(0xbfa1b04bL), tobe(0xbb60adfcL), tobe(0xb6238b25L), tobe(0xb2e29692L), tobe(0x8aad2b2fL), tobe(0x8e6c3698L), tobe(0x832f1041L), tobe(0x87ee0df6L), tobe(0x99a95df3L), tobe(0x9d684044L), tobe(0x902b669dL), tobe(0x94ea7b2aL), tobe(0xe0b41de7L), tobe(0xe4750050L), tobe(0xe9362689L), tobe(0xedf73b3eL), tobe(0xf3b06b3bL), tobe(0xf771768cL), tobe(0xfa325055L), tobe(0xfef34de2L), tobe(0xc6bcf05fL), tobe(0xc27dede8L), tobe(0xcf3ecb31L), tobe(0xcbffd686L), tobe(0xd5b88683L), tobe(0xd1799b34L), tobe(0xdc3abdedL), tobe(0xd8fba05aL), tobe(0x690ce0eeL), tobe(0x6dcdfd59L), tobe(0x608edb80L), tobe(0x644fc637L), tobe(0x7a089632L), tobe(0x7ec98b85L), tobe(0x738aad5cL), tobe(0x774bb0ebL), tobe(0x4f040d56L), tobe(0x4bc510e1L), tobe(0x46863638L), tobe(0x42472b8fL), tobe(0x5c007b8aL), tobe(0x58c1663dL), tobe(0x558240e4L), tobe(0x51435d53L), tobe(0x251d3b9eL), tobe(0x21dc2629L), tobe(0x2c9f00f0L), tobe(0x285e1d47L), tobe(0x36194d42L), tobe(0x32d850f5L), tobe(0x3f9b762cL), tobe(0x3b5a6b9bL), tobe(0x0315d626L), tobe(0x07d4cb91L), tobe(0x0a97ed48L), tobe(0x0e56f0ffL), tobe(0x1011a0faL), tobe(0x14d0bd4dL), tobe(0x19939b94L), tobe(0x1d528623L), tobe(0xf12f560eL), tobe(0xf5ee4bb9L), tobe(0xf8ad6d60L), tobe(0xfc6c70d7L), tobe(0xe22b20d2L), tobe(0xe6ea3d65L), tobe(0xeba91bbcL), tobe(0xef68060bL), tobe(0xd727bbb6L), tobe(0xd3e6a601L), tobe(0xdea580d8L), tobe(0xda649d6fL), tobe(0xc423cd6aL), tobe(0xc0e2d0ddL), tobe(0xcda1f604L), tobe(0xc960ebb3L), tobe(0xbd3e8d7eL), tobe(0xb9ff90c9L), tobe(0xb4bcb610L), tobe(0xb07daba7L), tobe(0xae3afba2L), tobe(0xaafbe615L), tobe(0xa7b8c0ccL), tobe(0xa379dd7bL), tobe(0x9b3660c6L), tobe(0x9ff77d71L), tobe(0x92b45ba8L), tobe(0x9675461fL), tobe(0x8832161aL), tobe(0x8cf30badL), tobe(0x81b02d74L), tobe(0x857130c3L), tobe(0x5d8a9099L), tobe(0x594b8d2eL), tobe(0x5408abf7L), tobe(0x50c9b640L), tobe(0x4e8ee645L), tobe(0x4a4ffbf2L), tobe(0x470cdd2bL), tobe(0x43cdc09cL), tobe(0x7b827d21L), tobe(0x7f436096L), tobe(0x7200464fL), tobe(0x76c15bf8L), tobe(0x68860bfdL), tobe(0x6c47164aL), tobe(0x61043093L), tobe(0x65c52d24L), tobe(0x119b4be9L), tobe(0x155a565eL), tobe(0x18197087L), tobe(0x1cd86d30L), tobe(0x029f3d35L), tobe(0x065e2082L), tobe(0x0b1d065bL), tobe(0x0fdc1becL), tobe(0x3793a651L), tobe(0x3352bbe6L), tobe(0x3e119d3fL), tobe(0x3ad08088L), tobe(0x2497d08dL), tobe(0x2056cd3aL), tobe(0x2d15ebe3L), tobe(0x29d4f654L), tobe(0xc5a92679L), tobe(0xc1683bceL), tobe(0xcc2b1d17L), tobe(0xc8ea00a0L), tobe(0xd6ad50a5L), tobe(0xd26c4d12L), tobe(0xdf2f6bcbL), tobe(0xdbee767cL), tobe(0xe3a1cbc1L), tobe(0xe760d676L), tobe(0xea23f0afL), tobe(0xeee2ed18L), tobe(0xf0a5bd1dL), tobe(0xf464a0aaL), tobe(0xf9278673L), tobe(0xfde69bc4L), tobe(0x89b8fd09L), tobe(0x8d79e0beL), tobe(0x803ac667L), tobe(0x84fbdbd0L), tobe(0x9abc8bd5L), tobe(0x9e7d9662L), tobe(0x933eb0bbL), tobe(0x97ffad0cL), tobe(0xafb010b1L), tobe(0xab710d06L), tobe(0xa6322bdfL), tobe(0xa2f33668L), tobe(0xbcb4666dL), tobe(0xb8757bdaL), tobe(0xb5365d03L), tobe(0xb1f740b4L) }; #if CRC_LE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t crc32_le(uint32_t crc, unsigned char const *p, int len) { int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); } return crc; } #else /* Table-based approach */ /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t crc32_le(uint32_t crc, unsigned char const *p, int len) { # if CRC_LE_BITS == 8 const uint32_t *b =(uint32_t *)p; const uint32_t *tab = crc32table_le; # ifdef __LITTLE_ENDIAN # define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) # else # define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) # endif crc = __cpu_to_le32(crc); /* Align it */ if(((long)b)&3 && len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (void *)p; } while ((--len) && ((long)b)&3 ); } if(len >= 4){ /* load data 32 bits wide, xor data 32 bits wide. */ int save_len = len & 3; len = len >> 2; --b; /* use pre increment below(*++b) for speed */ do { crc ^= *++b; DO_CRC(0); DO_CRC(0); DO_CRC(0); DO_CRC(0); } while (--len); b++; /* point to next byte(s) */ len = save_len; } /* And the last few bytes */ if(len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (void *)p; } while (--len); } return __le32_to_cpu(crc); #undef ENDIAN_SHIFT #undef DO_CRC # elif CRC_LE_BITS == 4 while (len--) { crc ^= *p++; crc = (crc >> 4) ^ crc32table_le[crc & 15]; crc = (crc >> 4) ^ crc32table_le[crc & 15]; } return crc; # elif CRC_LE_BITS == 2 while (len--) { crc ^= *p++; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; } return crc; # endif } #endif #if CRC_BE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t crc32_be(uint32_t crc, unsigned char const *p, int len) { int i; while (len--) { crc ^= *p++ << 24; for (i = 0; i < 8; i++) crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); } return crc; } #else /* Table-based approach */ /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t crc32_be(uint32_t crc, unsigned char const *p, int len) { # if CRC_BE_BITS == 8 const uint32_t *b =(uint32_t *)p; const uint32_t *tab = crc32table_be; # ifdef __LITTLE_ENDIAN # define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8) # else # define DO_CRC(x) crc = tab[ ((crc >> 24) ^ (x)) & 255] ^ (crc<<8) # endif crc = __cpu_to_be32(crc); /* Align it */ if(((long)b)&3 && len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (uint32_t *)p; } while ((--len) && ((long)b)&3 ); } if(len >= 4){ /* load data 32 bits wide, xor data 32 bits wide. */ int save_len = len & 3; len = len >> 2; --b; /* use pre increment below(*++b) for speed */ do { crc ^= *++b; DO_CRC(0); DO_CRC(0); DO_CRC(0); DO_CRC(0); } while (--len); b++; /* point to next byte(s) */ len = save_len; } /* And the last few bytes */ if(len){ do { uint8_t *p = (uint8_t *)b; DO_CRC(*p++); b = (void *)p; } while (--len); } return __be32_to_cpu(crc); #undef ENDIAN_SHIFT #undef DO_CRC # elif CRC_BE_BITS == 4 while (len--) { crc ^= *p++ << 24; crc = (crc << 4) ^ crc32table_be[crc >> 28]; crc = (crc << 4) ^ crc32table_be[crc >> 28]; } return crc; # elif CRC_BE_BITS == 2 while (len--) { crc ^= *p++ << 24; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; } return crc; # endif } #endif uint32_t bitreverse(uint32_t x) { x = (x >> 16) | (x << 16); x = (x >> 8 & 0x00ff00ff) | (x << 8 & 0xff00ff00); x = (x >> 4 & 0x0f0f0f0f) | (x << 4 & 0xf0f0f0f0); x = (x >> 2 & 0x33333333) | (x << 2 & 0xcccccccc); x = (x >> 1 & 0x55555555) | (x << 1 & 0xaaaaaaaa); return x; } /* * A brief CRC tutorial. * * A CRC is a long-division remainder. You add the CRC to the message, * and the whole thing (message+CRC) is a multiple of the given * CRC polynomial. To check the CRC, you can either check that the * CRC matches the recomputed value, *or* you can check that the * remainder computed on the message+CRC is 0. This latter approach * is used by a lot of hardware implementations, and is why so many * protocols put the end-of-frame flag after the CRC. * * It's actually the same long division you learned in school, except that * - We're working in binary, so the digits are only 0 and 1, and * - When dividing polynomials, there are no carries. Rather than add and * subtract, we just xor. Thus, we tend to get a bit sloppy about * the difference between adding and subtracting. * * A 32-bit CRC polynomial is actually 33 bits long. But since it's * 33 bits long, bit 32 is always going to be set, so usually the CRC * is written in hex with the most significant bit omitted. (If you're * familiar with the IEEE 754 floating-point format, it's the same idea.) * * Note that a CRC is computed over a string of *bits*, so you have * to decide on the endianness of the bits within each byte. To get * the best error-detecting properties, this should correspond to the * order they're actually sent. For example, standard RS-232 serial is * little-endian; the most significant bit (sometimes used for parity) * is sent last. And when appending a CRC word to a message, you should * do it in the right order, matching the endianness. * * Just like with ordinary division, the remainder is always smaller than * the divisor (the CRC polynomial) you're dividing by. Each step of the * division, you take one more digit (bit) of the dividend and append it * to the current remainder. Then you figure out the appropriate multiple * of the divisor to subtract to being the remainder back into range. * In binary, it's easy - it has to be either 0 or 1, and to make the * XOR cancel, it's just a copy of bit 32 of the remainder. * * When computing a CRC, we don't care about the quotient, so we can * throw the quotient bit away, but subtract the appropriate multiple of * the polynomial from the remainder and we're back to where we started, * ready to process the next bit. * * A big-endian CRC written this way would be coded like: * for (i = 0; i < input_bits; i++) { * multiple = remainder & 0x80000000 ? CRCPOLY : 0; * remainder = (remainder << 1 | next_input_bit()) ^ multiple; * } * Notice how, to get at bit 32 of the shifted remainder, we look * at bit 31 of the remainder *before* shifting it. * * But also notice how the next_input_bit() bits we're shifting into * the remainder don't actually affect any decision-making until * 32 bits later. Thus, the first 32 cycles of this are pretty boring. * Also, to add the CRC to a message, we need a 32-bit-long hole for it at * the end, so we have to add 32 extra cycles shifting in zeros at the * end of every message, * * So the standard trick is to rearrage merging in the next_input_bit() * until the moment it's needed. Then the first 32 cycles can be precomputed, * and merging in the final 32 zero bits to make room for the CRC can be * skipped entirely. * This changes the code to: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit() << 31; * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * With this optimization, the little-endian code is simpler: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit(); * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder >> 1) ^ multiple; * } * * Note that the other details of endianness have been hidden in CRCPOLY * (which must be bit-reversed) and next_input_bit(). * * However, as long as next_input_bit is returning the bits in a sensible * order, we can actually do the merging 8 or more bits at a time rather * than one bit at a time: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte() << 24; * for (j = 0; j < 8; j++) { * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * Or in little-endian: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte(); * for (j = 0; j < 8; j++) { * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * If the input is a multiple of 32 bits, you can even XOR in a 32-bit * word at a time and increase the inner loop count to 32. * * You can also mix and match the two loop styles, for example doing the * bulk of a message byte-at-a-time and adding bit-at-a-time processing * for any fractional bytes at the end. * * The only remaining optimization is to the byte-at-a-time table method. * Here, rather than just shifting one bit of the remainder to decide * in the correct multiple to subtract, we can shift a byte at a time. * This produces a 40-bit (rather than a 33-bit) intermediate remainder, * but again the multiple of the polynomial to subtract depends only on * the high bits, the high 8 bits in this case. * * The multile we need in that case is the low 32 bits of a 40-bit * value whose high 8 bits are given, and which is a multiple of the * generator polynomial. This is simply the CRC-32 of the given * one-byte message. * * Two more details: normally, appending zero bits to a message which * is already a multiple of a polynomial produces a larger multiple of that * polynomial. To enable a CRC to detect this condition, it's common to * invert the CRC before appending it. This makes the remainder of the * message+crc come out not as zero, but some fixed non-zero value. * * The same problem applies to zero bits prepended to the message, and * a similar solution is used. Instead of starting with a remainder of * 0, an initial remainder of all ones is used. As long as you start * the same way on decoding, it doesn't make a difference. */ getstream2-20100616/pat.c0000664000372000037200000001540211406140020013572 0ustar florfcorg #include #include #include #include #include "getstream.h" #include "psi.h" #include "crc32.h" /* * PAT Programm Association Table * * The PAT is multiplexed into the TS (Transport Stream) * at PID 0x0. It contains the table of all PMT (Program Map Table) * pids within the TS. * * Name Bits Offset * * table_id 8 0 * section_syntax_indicator 1 1 * pad (0) 1 1 * reserved 2 1 * section_length 12 1/2 * transport_stream_id 16 3/4 * reserved 2 5 * version_number 5 5 * current_next_indicator 1 5 * section_number 8 6 * last_section_number 8 7 * * 1..N * program_number 16 * reserved 3 * pid 13 * (if pnum==0 network_pid else program_map_pid) * * crc32 32 * */ struct patprog_s { uint16_t pnr; uint16_t pid; }; static int pat_pnrno(struct psisec_s *section) { return psi_payload_len(section)/PAT_PNR_LEN; } static int pat_pnrfrompat(struct psisec_s *section, int i) { uint8_t *pat=section->data; return pat[PAT_HDR_LEN+i*4+0] << 8 | pat[PAT_HDR_LEN+i*4+1]; } static int pat_pidfrompat(struct psisec_s *section, int i) { uint8_t *pat=section->data; return (pat[PAT_HDR_LEN+i*4+2] << 8 | pat[PAT_HDR_LEN+i*4+3]) & PID_MASK; } /* * Send out a pat created from a struct pat_s. Things like the CC (Continuity Counter) * version and transport id are passed from the caller. * pat_send assembles the sections and passes them onto psi_segment_and_send which * will create TS packets and feed them into the callback. * * FIXME This is completely untested with multisegment PATs so beware of the dogs. * * */ unsigned int pat_send(struct pat_s *pat, uint8_t cc, uint8_t version, uint16_t tid, void (*callback)(void *data, void *arg), void *arg) { struct psisec_s *section=psi_section_new(); uint8_t *p; uint32_t ccrc; GList *pl=g_list_first(pat->program); int i, seclen, patlen; int pkts=0; /* FIXME - Need to calculate number of sections */ p=section->data; while(1) { memset(p, 0xff, PSI_SECTION_MAX); p[PSI_TABLE_ID_OFF]=PAT_TABLE_ID; p[PAT_TID_OFF1]=(tid>>8); p[PAT_TID_OFF2]=(tid&0xff); /* Version and set lowest bit to 1 (current next) */ p[PSI_VERSION_OFF]=(version&0x1f)<<1|1; p[PSI_SECNO_OFF]=0x0; p[PSI_LASTSECNO_OFF]=0x0; i=0; for(;pl;pl=g_list_next(pl)) { struct patprog_s *pp=pl->data; p[PAT_HDR_LEN+i*4+0]=pp->pnr >> 8; p[PAT_HDR_LEN+i*4+1]=pp->pnr & 0xff; p[PAT_HDR_LEN+i*4+2]=pp->pid >> 8; p[PAT_HDR_LEN+i*4+3]=pp->pid & 0xff; /* FIXME - Should check for PSI Section overflow (multi section PAT) */ i++; } patlen=PAT_HDR_LEN+i*4+CRC32_LEN; seclen=(patlen-PSI_SECLEN_ADD)&PSI_SECLEN_MASK; p[PSI_SECLEN_OFF+0]=0x80|(seclen>>8); p[PSI_SECLEN_OFF+1]=(seclen&0xff); ccrc=crc32_be(0xffffffff, p, patlen-CRC32_LEN); p[patlen-CRC32_LEN+0]=(ccrc>>24)&0xff; p[patlen-CRC32_LEN+1]=(ccrc>>16)&0xff; p[patlen-CRC32_LEN+2]=(ccrc>>8)&0xff; p[patlen-CRC32_LEN+3]=(ccrc&0xff); pkts=psi_segment_and_send(section, 0, cc+pkts, callback, arg); /* If we have all programs in this section */ if (!pl) break; } psi_section_free(section); return pkts; } struct pat_s *pat_new() { return calloc(1, sizeof(struct pat_s)); } void pat_free(struct pat_s *pat) { GList *pl=g_list_first(pat->program); while(pl) { g_free(pl->data); pl=g_list_next(pl); } g_list_free(pat->program); free(pat); } void pat_add_program(struct pat_s *pat, uint16_t pnr, uint16_t pid) { struct patprog_s *prog=g_new(struct patprog_s, 1); prog->pnr=pnr; prog->pid=pid; pat->program=g_list_append(pat->program, prog); } struct pat_s *pat_parse(struct adapter_s *a) { struct psisec_s *s; struct pat_s *next; unsigned int secnum; unsigned int lastsecnum; unsigned int pnr; if (!a->pat.psi.section[0]) return NULL; next=pat_new(); lastsecnum=psi_last_section_number(a->pat.psi.section[0]); for(secnum=0;secnum<=lastsecnum;secnum++) { s=a->pat.psi.section[secnum]; if (!s) continue; for(pnr=0;pnrprogcount++; logwrite(LOG_DEBUG, "pat: found program %04x(%d) pmt pid %04x", pat_pnrfrompat(s, pnr), pat_pnrfrompat(s, pnr), pat_pidfrompat(s, pnr)); } } logwrite(LOG_DEBUG, "pat: pat parse found %d programs", next->progcount); return next; } static struct patprog_s *pat_findpnr(struct pat_s *pat, uint16_t pnr) { GList *ppl=g_list_first(pat->program); while(ppl) { struct patprog_s *prog=ppl->data; if (prog->pnr == pnr) return prog; ppl=g_list_next(ppl); } return NULL; } /* * The PAT did change e.g. we added or changed a section * Parse it into our pat_s structure and check which PMTs * changed or were added and service the PMT callback * */ static void pat_update(struct adapter_s *a) { struct pat_s *current=a->pat.current; struct pat_s *last=a->pat.last; GList *pl; struct patprog_s *plast, *pcur; /* Check whether new programs appeared or the programs PMTPIDs changed */ for(pl=g_list_first(current->program);pl;pl=g_list_next(pl)) { pcur=pl->data; if (pcur->pid == 0) { logwrite(LOG_ERROR, "pat: Invalid PMT pid 0 for pnr %d", pcur->pnr); continue; } /* Do we have a current PAT ? */ if (!last) { pmt_pidfrompat(a, pcur->pnr, pcur->pid); } else { /* Do we have the program and if so did the PMT pid change ? */ plast=pat_findpnr(last, pcur->pnr); if (plast && plast->pid != pcur->pid) pmt_pidfrompat(a, pcur->pnr, pcur->pid); } } if (!last) return; /* Did programs disappear e.g. we dont have their PNR anymore */ for(pl=g_list_first(last->program);pl;pl=g_list_next(pl)) { plast=pl->data; pcur=pat_findpnr(current, plast->pnr); if (!pcur) pmt_pidfrompat(a, plast->pnr, 0); } } /* Add/Update a section in our PAT. * * - Check whether this is a current section * - Check whether its really a PAT * - Add/Update the section in our PSI table * - If we had an update/change parse the PAT and call pat_update * * Input: * - pointer to our adapter * - a static section (need to clone before usage) * */ static void pat_section_cb(void *data, void *arg) { struct psisec_s *section=data; struct adapter_s *adapter=arg; if (!psi_currentnext(section)) return; if (psi_tableid(section) != PAT_TABLE_ID) return; if (!psi_update_table(&adapter->pat.psi, section)) return; if (adapter->pat.last) pat_free(adapter->pat.last); adapter->pat.last=adapter->pat.current; adapter->pat.current=pat_parse(adapter); pat_update(adapter); return; } void pat_init(struct adapter_s *adapter) { /* Did we already add a callback? */ if (adapter->pat.cbc != NULL) return; adapter->pat.cbc=dvr_add_pcb(adapter, 0, DVRCB_SECTION, PID_PAT, pat_section_cb, adapter); } getstream2-20100616/Makefile0000664000372000037200000000134511406140020014303 0ustar florfcorgCC=gcc CFLAGS=-Wall -I. -I/usr/include/glib-2.0/ -I/usr/lib/glib-2.0/include/ ifdef OPT CFLAGS+=-O2 else CFLAGS+=-O0 -g endif LDFLAGS=-levent -lglib-2.0 -lpthread OBJ-getstream=getstream.o fe.o crc32.o \ libhttp.o libconf.o config.o util.o logging.o \ stream.o input.o \ output.o output_http.o output_udp.o output_pipe.o output_rtp.o \ dmx.o dvr.o \ pat.o pmt.o psi.o \ simplebuffer.o sap.o \ socket.o OBJ-tsdecode=tsdecode.o psi.o crc32.o all: getstream tsdecode tsdecode: $(OBJ-tsdecode) gcc $(LDFLAGS) -o $@ $+ getstream: $(OBJ-getstream) gcc $(LDFLAGS) -o $@ $+ clean: -rm -f $(OBJ-getstream) $(OBJ-tsdecode) -rm -f getstream tsdecode -rm -f core vgcore.pid* core.* gmon.out distclean: clean -rm -rf CVS .cvsignore getstream2-20100616/getstream.c0000664000372000037200000000622211406140020015001 0ustar florfcorg #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getstream.h" #include "config.h" #include "libhttp.h" #ifdef DEBUG static int guardcounter; void *guard_thread(void *mtp) { pthread_t mt=(pthread_t) mtp; struct timeval tv; guardcounter=0; while(1) { tv.tv_sec=1; tv.tv_usec=0; select(0, NULL, NULL, NULL, &tv); if (guardcounter==0) { pthread_kill(mt, SIGABRT); pthread_exit(NULL); } guardcounter=0; } } static void init_guard_evtimer(void ); static void guard_evtimer(int fd, short event, void *arg) { guardcounter++; init_guard_evtimer(); } static void init_guard_evtimer(void ) { static struct event gevent; static struct timeval tv; tv.tv_usec=50000; tv.tv_sec=00; evtimer_set(&gevent, guard_evtimer, &gevent); evtimer_add(&gevent, &tv); } void init_guard_thread(void ) { pthread_t mt, gt; init_guard_evtimer(); mt=pthread_self(); pthread_create(>, NULL, guard_thread, (void *) mt); } #endif /* * When profiling one needs to call "exit" or return to main. * As we are running in a wheel like a Hamster there is no way * returning to main so we unconditionally call exit after a timeout * */ static void terminate_timeout(int fd, short event, void *arg) { exit(0); } static void terminate_init(int timeout) { static struct event event; static struct timeval tv; tv.tv_usec=0; tv.tv_sec=timeout; evtimer_set(&event, terminate_timeout, &event); evtimer_add(&event, &tv); } static void usage(void ) { fprintf(stderr, "-c -d -t \n"); exit(-1); } struct http_server *hserver; int main(int argc, char **argv) { extern char *optarg; int ch; int timeout=0; GList *al; struct config_s *config=NULL; while((ch=getopt(argc, argv, "c:dt:")) != -1) { switch(ch) { case 'c': config=readconfig(optarg); if (!config) exit(1); break; case 'd': logwrite_inc_level(); break; case 't': timeout=strtol(optarg, NULL, 10); break; default: usage(); break; } } if (!config) exit(-1); /* Initialize libevent */ event_init(); if (config->http_port) { hserver=http_init(config->http_port); if (!hserver) { fprintf(stderr, "Could not create http server on port %d\n", config->http_port); exit(-1); } } /* In case of profiling we want to call exit after a timeout */ if (timeout) terminate_init(timeout); al=g_list_first(config->adapter); while(al) { struct adapter_s *a=al->data; GList *sl=g_list_first(a->streams); /* Tune to the one and only transponder */ fe_tune_init(a); /* Initialize demux0 dvr0 and co */ dmx_init(a); dvr_init(a); /* Init all streams */ while(sl) { struct stream_s *stream=sl->data; stream_init(stream); sl=g_list_next(sl); } al=g_list_next(al); } #ifdef DEBUG init_guard_thread(); #endif /* Pigs can fly */ event_dispatch(); return 0; } getstream2-20100616/libconf.c0000664000372000037200000002655211406140020014432 0ustar florfcorg /* * TODO: * - token has childs although validate entrys say no childs * -> need to error * */ #include #include #include #include #include #include #include #include #include "libconf.h" enum { TOKEN_STRING, TOKEN_QSTRING, TOKEN_COLON, TOKEN_OBRACKET, TOKEN_CBRACKET, TOKEN_NEWLINE, TOKEN_EOF, TOKEN_QSTRING_EOL, TOKEN_QSTRING_EOF, TOKEN_QSTRING_BROKEN }; static void dumpconfig(struct lc_centry *c) { printf("token %p\n", c); printf("\tprev %p next %p\n", c->prev, c->next); printf("\tchild %p parent %p\n", c->child, c->parent); printf("\ttoken %s value %s\n", c->token, c->value); if(c->child) dumpconfig(c->child); if(c->next) dumpconfig(c->next); } static int gettoken(char *c, off_t len, char *tb, off_t *off) { off_t o=*off, ts; char *tbp=NULL; while(o=len) /* EOF ? */ return TOKEN_EOF; break; /* Quoted string */ case('"'): o++; ts=o; if (tbp) return TOKEN_QSTRING_BROKEN; tbp=tb; /* Seek closing quote * FIXME What about quoted chars ? */ while(oparent=lce; lce->child=cce; break; case(TOKEN_CBRACKET): if (!cce->parent) { fprintf(stderr, "To many closing brackets in line %d\n", line); return NULL; } /* Empty {} ? */ if (!cce->token && !cce->value && cce->parent) { /* Remove empty child ce */ cce=cce->parent; free(cce->child); cce->child=NULL; /* Get next token - hopefully ; */ break; } /* Did we close last token/value pair ? */ if (!cce->closed) { fprintf(stderr, "Token not finished e.g. missing ; in line %d\n", line); return NULL; } cce=cce->parent; break; case(TOKEN_COLON): /* Close token */ cce->closed=1; break; case(TOKEN_STRING): /* Last token was closed - we need a new one */ if (cce->closed) { lce=cce; cce=calloc(1, sizeof(struct lc_centry)); cce->prev=lce; lce->next=cce; cce->parent=lce->parent; } if (!cce->token) { cce->token=strdup(tb); cce->tline=line; } else if (!cce->value) { cce->value=strdup(tb); cce->vline=line; } else { fprintf(stderr, "Double value for token in line %d\n", line); return NULL; } break; case(TOKEN_QSTRING): if (!cce || !cce->token) { fprintf(stderr, "Quoted token in line %d\n", line); return NULL; } else if (!cce->value) { cce->value=strdup(tb); cce->vline=line; } else { fprintf(stderr, "Double value for token in line %d\n", line); return NULL; } break; case(TOKEN_EOF): return config; default: fprintf(stderr, "failed reading config %d\n", te); return NULL; } } /* Unreachable */ return NULL; } static int libconf_validate_value(struct lc_centry *ce, struct lc_ventry *ve) { if (!ve->name) { fprintf(stderr, "Unknown config option %s in line %d\n", ce->token, ce->tline); return 0; } /* We dont have a value but we would need one */ if (ve->type != LCV_NONE && (!ce->value && !(ve->opt & LCO_OPTIONAL))) { fprintf(stderr, "Config value for option %s in line %d is missing\n", ve->name, ce->tline); return 0; } if (ce->value) { switch(ve->type) { case(LCV_NONE): fprintf(stderr, "Config option %s does not allow value in line %d\n", ve->name, ce->vline); return 0; case(LCV_STRING): ce->cbvalue.string=ce->value; break; case(LCV_BOOL): { if ((strcasecmp("true", ce->value) == 0) || (strcasecmp("yes", ce->value) == 0) || (strcasecmp("on", ce->value) == 0) || (strcasecmp("1", ce->value) == 0)) { ce->cbvalue.num=1; } else if((strcasecmp("false", ce->value) == 0) || (strcasecmp("no", ce->value) == 0 ) || (strcasecmp("off", ce->value) == 0 ) || (strcasecmp("0", ce->value) == 0)) { ce->cbvalue.num=0; } else { fprintf(stderr, "Value %s for boolean option %s is invalid in line %d\n", ce->value, ve->name, ce->vline); return 0; } break; } case(LCV_NUM): { char *endptr; unsigned long num; num=strtol(ce->value, &endptr, 0); if (*endptr != 0x0) { fprintf(stderr, "Value %s for option %s is not a number in line %d\n", ce->value, ve->name, ce->vline); return 0; } ce->cbvalue.num=num; break; } case(LCV_HEX): { char *endptr; unsigned long num; num=strtol(ce->value, &endptr, 16); if (*endptr != 0x0) { fprintf(stderr, "Value %s for option %s is not a hex number in line %d\n", ce->value, ve->name, ce->vline); return 0; } ce->cbvalue.num=num; break; } case(LCV_IPADDR): case(LCV_IPV6ADDR): case(LCV_IPV4ADDR): { struct addrinfo *ai; int rc; rc=getaddrinfo(ce->value, NULL, NULL, &ai); if (rc) { fprintf(stderr, "Value %s for option %s is not an ip address in line %d\n", ce->value, ve->name, ce->vline); return 0; } if (ve->type == LCV_IPV6ADDR && ai->ai_family != PF_INET6) { fprintf(stderr, "Value %s for option %s is not an ip version 6 address in line %d\n", ce->value, ve->name, ce->vline); freeaddrinfo(ai); return 0; } if (ve->type == LCV_IPV4ADDR && ai->ai_family != PF_INET) { fprintf(stderr, "Value %s for option %s is not an ip version 4 address in line %d\n", ce->value, ve->name, ce->vline); freeaddrinfo(ai); return 0; } ce->cbvalue.string=ce->value; freeaddrinfo(ai); break; } } } return 1; } static struct lc_ventry *libconf_find_token(struct lc_centry *ce, struct lc_ventry *ventry) { struct lc_ventry *ve; for(ve=ventry;ve->name;ve++) { /* Do we know this config entry ? */ if (strcasecmp(ce->token, ve->name) == 0) break; } return (ve->name) ? ve : NULL; } /* * Recurse through parsed config entrys and check tokens * Fill in ventry pointer into centrys * */ static int libconf_validate_token(struct lc_centry *centry, struct lc_ventry *ventry) { struct lc_ventry *ve; struct lc_centry *ce; /* Loop on config option entrys */ for(ce=centry;ce;ce=ce->next) { /* Find token */ ve=libconf_find_token(ce, ventry); if (!ve) { fprintf(stderr, "Unknown config option %s in line %d\n", ce->token, ce->tline); return 0; } /* Fill ventry in centry */ ce->ventry=ve; /* Recurse into children */ if (ce->child) { if(!ve->child) { fprintf(stderr, "Config option %s does not allow suboptions in line %d\n", ve->name, ce->tline); return 0; } /* Recurse into childs */ if (!libconf_validate_token(ce->child, ve->child)) return 0; } centry->noce++; } return 1; } /* Recurse through centrys and validate values */ static int libconf_validate_values(struct lc_centry *centry) { struct lc_centry *ce; for(ce=centry;ce;ce=ce->next) { if (!libconf_validate_value(ce, ce->ventry)) return 0; if (ce->child) if (!libconf_validate_values(ce->child)) return 0; } return 1; } static int libconf_validate_valueopt(struct lc_centry *centry, struct lc_ventry *ventry) { struct lc_centry *ce; struct lc_ventry *ve; char **tuval; /* First confentry the *child points to has a noce field */ tuval=calloc(centry->noce+1, sizeof(char *)); for(ve=ventry;ve->name;ve++) { int optcount=0, optline=0, valcount=0, i; for(ce=centry;ce;ce=ce->next) { /* Check prefilled ventry ptr */ if (ce->ventry != ve) continue; optcount++; optline=ce->tline; /* Check values */ if ((ve->opt & LCO_UNIQ) && (ce->value)) { /* Check stored value pointers for uniqueness */ for(i=0;ivalue, tuval[i]) == 0) { fprintf(stderr, "Value %s for option %s in line %d is not unique\n", ce->value, ve->name, ce->vline); return 0; } } /* Store pointer for later uniqueness check */ tuval[valcount++]=ce->value; } } if (ve->max && optcount > ve->max) { fprintf(stderr, "Option %s is only allowed %d times in line %d\n", ve->name, ve->max, optline); return 0; } if (ve->min && optcount < ve->min) { /* FIXME - line is not really nice */ fprintf(stderr, "Option %s is needed %d times\n", ve->name, ve->min); return 0; } } free(tuval); for(ce=centry;ce;ce=ce->next) { if (ce->child) if (!libconf_validate_valueopt(ce->child, ce->ventry->child)) return 0; } return 1; } /* * Run through the parsed and validated tree and * call applications callback functions * */ static int libconf_validate_callback(struct lc_centry *centry) { struct lc_centry *ce; for(ce=centry;ce;ce=ce->next) { if (ce->ventry->cback) if (!ce->ventry->cback(ce, &ce->cbvalue)) return 0; if (ce->child) if (!libconf_validate_callback(ce->child)) return 0; /* Does application want a late callback ? */ if (ce->ventry->opt & LCO_LATECB) if (!ce->ventry->cback(ce, NULL)) return 0; } return 1; } int libconf_validate(struct lc_centry *ce, struct lc_ventry *ve) { /* Recurse through config entrys - check tokens * and fill ventry pointer */ if (!libconf_validate_token(ce, ve)) return 0; /* Recurse through config entrys - check values for syntax */ if (!libconf_validate_values(ce)) return 0; /* Recurse through config and ventry and check for value options * e.g. uniqueness, min and max count */ if (!libconf_validate_valueopt(ce, ve)) return 0; /* Recurse through config and call user callbacks */ if (!libconf_validate_callback(ce)) return 0; return 1; } /* Free all libconf related structures */ void libconf_free(struct lc_centry *ce) { struct lc_centry *cce=ce, *next; while(cce) { /* Recurse into children */ if (cce->child) libconf_free(cce->child); /* Save next pointer */ next=cce->next; /* Free current centry */ if (cce->token) free(cce->token); if (cce->value) free(cce->value); free(cce); cce=next; } return; } getstream2-20100616/psi.h0000664000372000037200000000732511406140020013613 0ustar florfcorg#ifndef PSI_H #define PSI_H #define PSI_MAX_SIZE 4096 /* ISO 13818-1 says private sections shall not exceed 4093 bytes. Page 91 */ #define PSI_SECLEN_ADD 3 /* Size starts after SECLEN */ #define PSI_SECLEN_OFF 1 #define PSI_SECLEN_MASK 0xfff #define PSI_VERSION_OFF 5 #define PSI_VERSION_MASK 0x3e #define PSI_VERSION_SHIFT 1 #define PSI_CURRENTNEXT_OFF 5 #define PSI_CURRENTNEXT_MASK 1 #define PSI_SECNO_OFF 6 #define PSI_LASTSECNO_OFF 7 #define PMT_TABLE_OFF 0 #define PMT_TABLE_ID 0x02 #define PMT_SECLEN_OFF1 1 #define PMT_SECLEN_OFF2 2 #define PMT_SECLEN_MASK 0x0fff #define PMT_SECNO_OFF 6 #define PMT_LASTSECNO_OFF 7 #define PMT_PNR_OFF1 3 #define PMT_PNR_OFF2 4 #define PMT_PILEN_OFF1 10 #define PMT_PILEN_OFF2 11 #define PMT_PILEN_MASK 0x03ff #define PMT_PI_OFF 12 #define PMT_PCR_OFF1 8 #define PMT_PCR_MASK1 0x1f #define PMT_PCR_OFF2 9 #define PMT_PCR_MASK2 0xff #define PMT_ST_STYPE_OFF 0 #define PMT_ST_PID_OFF1 1 #define PMT_ST_PID_OFF2 2 #define PMT_ST_ESLEN_OFF1 3 #define PMT_ST_ESLEN_OFF2 4 #define PMT_ST_ESLEN_MASK 0x0fff #define PMT_ST_ES_OFF 5 #define PMT_D_TAG_OFF 0 #define PMT_D_LEN_OFF 1 #define PMT_SECTION_OFF 5 #define PMT_LAST_SECTION_OFF 5 #define PMT_MIN_LEN (PMT_PI_OFF+CRC32_LEN) #define PSI_TABLE_ID_OFF 0 #define PSI_TABLE_ID 0x0 #define PAT_SECTION_OFF 6 #define PAT_LAST_SECTION_OFF 7 #define PAT_SLEN_OFF1 1 #define PAT_SLEN_OFF2 2 #define PAT_SLEN_MASK 0x0fff #define PAT_TID_OFF1 3 #define PAT_TID_OFF2 4 #define PAT_VER_OFF 5 #define PSI_HDR_LEN 8 /* PSI Header of the PAT */ #define PAT_HDR_LEN 8 /* PSI Header of the PAT */ #define PAT_PNR_LEN 4 /* Single PAT entry length in bytes */ #define PAT_MIN_LEN (PAT_HDR_LEN+PAT_PNR_LEN+CRC32_LEN) #define PAT_TABLE_ID 0x00 #define PSI_SECTION_MAX 255 #define PSI_RC_OK 0 #define PSI_RC_TEI -1 #define PSI_RC_NOPAYLOAD -2 #define PSI_RC_INCOMPLETE -3 #define PSI_RC_CRCFAIL -4 #define PSI_RC_CCFAIL -5 #define PSI_RC_LENFAIL -6 #define PSI_RC_CORRUPT -7 struct psisec_s { unsigned int len; unsigned int valid; unsigned int pid; unsigned int cc; uint8_t data[PSI_MAX_SIZE]; }; struct psi_s { struct psisec_s *section[PSI_SECTION_MAX]; }; /* * * psi.c * * */ #define _psi_version(data) \ ((data[PSI_VERSION_OFF]&PSI_VERSION_MASK)>>PSI_VERSION_SHIFT) #define psi_version(section) \ _psi_version(section->data) #define _psi_section_number(data) \ (data[PSI_SECNO_OFF]) #define psi_section_number(section) \ _psi_section_number(section->data) #define _psi_last_section_number(data) \ (data[PSI_LASTSECNO_OFF]) #define psi_last_section_number(section) \ _psi_last_section_number(section->data) #define _psi_currentnext(data) \ (data[PSI_CURRENTNEXT_OFF]&PSI_CURRENTNEXT_MASK) #define psi_currentnext(section) \ _psi_currentnext(section->data) #define _psi_tableid(data) \ (data[PSI_TABLE_ID_OFF]) #define psi_tableid(section) \ _psi_tableid(section->data) #define _psi_len(data) \ (((data[PSI_SECLEN_OFF]<<8|data[PSI_SECLEN_OFF+1])&PSI_SECLEN_MASK)+PSI_SECLEN_ADD) #define psi_len(section) \ _psi_len(section->data) #define _psi_payload_len(data) \ (_psi_len(data)-PSI_HDR_LEN-CRC32_LEN) #define psi_payload_len(section) \ _psi_payload_len(section->data) struct psisec_s *psi_section_new(void ); void psi_section_free(struct psisec_s *); int psi_section_fromdata(struct psisec_s *section, unsigned int pid, uint8_t *data, int len); int psi_reassemble(struct psisec_s *psi, uint8_t *tsp, int offset); struct psisec_s *psi_section_clone(struct psisec_s *section); unsigned int psi_segment_and_send(struct psisec_s *section, unsigned int pid, uint8_t cc, void (*callback)(void *data, void *arg), void *arg); int psi_update_table(struct psi_s *psi, struct psisec_s *section); #endif getstream2-20100616/logging.c0000664000372000037200000000127111406140020014433 0ustar florfcorg#include #include #include #include #include #include "getstream.h" int loglevel=LOG_ERROR; void logwrite_inc_level() { loglevel++; } void logwrite(int level, const char *format, ...) { va_list pvar; char logbuffer[MAXPATHLEN]; char timedate[64]; struct timeval tv; struct tm *tm; time_t *t=&tv.tv_sec; if (level > loglevel) return; va_start (pvar, format); vsnprintf(logbuffer, sizeof(logbuffer), format, pvar); va_end (pvar); gettimeofday(&tv, NULL); tm=localtime(t); strftime(timedate, sizeof(timedate), "%Y-%m-%d %H:%M:%S", tm); printf("%s.%03d %s\n", timedate, (int) tv.tv_usec/1000, logbuffer); } getstream2-20100616/sap.c0000664000372000037200000001556411406140020013602 0ustar florfcorg #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getstream.h" #include "sap.h" #include "socket.h" /* * Implement Mini-SAP Service * * RFC 2974 (Session Announcement Protocol) * http://www.ietf.org/rfc/rfc2974.txt * RFC 2327 (SDP: Session Description Protocol) * http://www.ietf.org/rfc/rfc2327.txt * */ /* RFC 2327 v=0 o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4 o=
s=SDP Seminar i=A Seminar on the session description protocol u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps e=mjh@isi.edu (Mark Handley) c=IN IP4 224.2.17.12/127 c=
t=2873397496 2873404696 a=recvonly m=audio 49170 RTP/AVP 0 m=video 51372 RTP/AVP 31 m=application 32416 udp wb m= a=orient:portrait */ static void sap_init_timer_single(struct sap_s *sap); static char sappkt[SAP_MAX_SIZE]; static int sid=1; /* MSG Identifier - Unique for each session */ #define SAP_VERSION 1 #define SAP_VERSION_SHIFT 5 static void sap_send(int fd, short event, void *arg) { struct sap_s *sap=arg; char *sp; GList *el, *pl, *al; /* Clear Packet */ memset(&sappkt, 0, SAP_MAX_SIZE); sappkt[0]=SAP_VERSION<sid>>8&0xff; /* Unique session ID */ sappkt[3]=sap->sid&0xff; /* Set originating address */ sappkt[4]=sap->originatingaddr&0xff; sappkt[5]=sap->originatingaddr>>8&0xff; sappkt[6]=sap->originatingaddr>>16&0xff; sappkt[7]=sap->originatingaddr>>24&0xff; sp=sappkt+8; sp+=sprintf(sp, "v=0\r\n"); sp+=sprintf(sp, "o=%s\r\n", sap->odata); if (sap->output->stream->name) sp+=sprintf(sp, "s=%s\r\n", sap->output->stream->name); if (sap->description) sp+=sprintf(sp, "i=%s\r\n", sap->description); if (sap->uri) sp+=sprintf(sp, "u=%s\r\n", sap->uri); el=g_list_first(sap->emaillist); while(el) { char *email=el->data; sp+=sprintf(sp, "e=%s\r\n", email); el=g_list_next(el); } pl=g_list_first(sap->phonelist); while(pl) { char *phone=pl->data; sp+=sprintf(sp, "p=%s\r\n", phone); pl=g_list_next(pl); } sp+=sprintf(sp, "t=0 0\r\n"); sp+=sprintf(sp, "a=type:broadcast\r\n"); al=g_list_first(sap->attributelist); while(al) { char *attribute=al->data; sp+=sprintf(sp, "a=%s\r\n", attribute); al=g_list_next(al); } sp+=sprintf(sp, "m=%s\r\n", sap->mdata); sp+=sprintf(sp, "c=%s\r\n", sap->cdata); if (sap->playgroup) { sp+=sprintf(sp, "a=x-plgroup:%s\r\n", sap->playgroup); } send(sap->fd, sappkt, sp-sappkt, MSG_DONTWAIT); sap_init_timer_single(sap); } static void sap_init_timer_single(struct sap_s *sap) { static struct timeval tv; /* * Create timer to send out SAPs regularly */ tv.tv_usec=0; tv.tv_sec=sap->interval; evtimer_set(&sap->event, &sap_send, sap); evtimer_add(&sap->event, &tv); } static void sap_init_socket_single(struct sap_s *sap) { char *maddr; int port; int ttl; sap->fd=socket_open(NULL, 0); if (sap->fd == -1) { fprintf(stderr, "Unable to open SAP socket\n"); exit(-1); } /* Last resort Multicast config */ maddr=SAP_V4_GLOBAL_ADDRESS; port=SAP_PORT; /* get SAP remote address from scope or group */ switch(sap->scope) { case(SAP_SCOPE_GLOBAL): maddr=SAP_V4_GLOBAL_ADDRESS; break; case(SAP_SCOPE_ORG): maddr=SAP_V4_ORG_ADDRESS; break; case(SAP_SCOPE_LOCAL): maddr=SAP_V4_LOCAL_ADDRESS; break; case(SAP_SCOPE_LINK): maddr=SAP_V4_LINK_ADDRESS; break; case(SAP_SCOPE_NONE): break; } if(sap->group) maddr=sap->group; if(sap->port) port=sap->port; if(sap->ttl >= 0) { ttl = sap->ttl; /* use ttl value from sap config */ } else { ttl = sap->output->ttl; /* use ttl value form output config */ } socket_join_multicast(sap->fd, maddr); socket_connect(sap->fd, maddr, port); socket_set_ttl(sap->fd, ttl); } /* Create SDP Connection Data as per RFC2327 */ static char *sap_init_cdata(struct sap_s *sap) { char cdata[128]; /* mcast c=IN IP4 ipaddr/ttl */ /* ucast c=IN IP4 ipaddr */ /* Ignore the ucast case for UDP and RTP as it does not make * sense to announce ucast UDP/RTP */ switch(sap->output->type) { case(OTYPE_UDP): case(OTYPE_RTP): sprintf(cdata, "IN IP4 %s/%d", sap->output->remoteaddr, sap->output->ttl); break; case(OTYPE_RTCP): if (sap->output->localaddr) { sprintf(cdata, "IN IP4 %s/%d", sap->output->localaddr, sap->output->ttl); } else { char hname[80]; gethostname(hname, sizeof(hname)); sprintf(cdata, "IN IP4 %s/%d", hname, sap->output->ttl); } break; default: fprintf(stderr, "Unknown stream output type in %s", __FUNCTION__); exit(-1); } return strdup(cdata); } /* Create SDP Media Announcement as per RFC2327 */ static char *sap_init_mdata(struct sap_s *sap) { char mdata[128]; /* m=audio/video ipport udp 33 */ /* m=audio/video ipport rtp/avp 31 */ switch(sap->output->type) { case(OTYPE_UDP): sprintf(mdata, "video %d udp 33", sap->output->remoteport); break; case(OTYPE_RTP): sprintf(mdata, "video %d RTP/AVP 33", sap->output->remoteport); break; case(OTYPE_RTCP): sprintf(mdata, "video %d RTP/AVP 33", sap->output->rtpport); break; } return strdup(mdata); } /* Create SDP Origin as per RFC2327 */ static char *sap_init_odata(struct sap_s *sap) { char odata[128]; switch(sap->output->type) { case(OTYPE_UDP): case(OTYPE_RTP): sprintf(odata, "- %d %lu IN IP4 %s", sap->sid, time(NULL), sap->output->remoteaddr); break; case(OTYPE_RTCP): if (sap->output->localaddr) { sprintf(odata, "IN IP4 %s", sap->output->localaddr); } else { char hname[80]; gethostname(hname, sizeof(hname)); sprintf(odata, "- %d %lu IN IP4 %s", sap->sid, time(NULL), hname); } break; } return strdup(odata); } int sap_init(struct sap_s *sap) { /* Copy a Session ID into the SAP structure. This needs to be unique * for a SAP sender and needs to be changed in case the SAP announcement * changes */ sap->sid=sid++; /* * FIXME - This would need to be the originating * not the destination address */ if(sap->output->remoteaddr) sap->originatingaddr=inet_addr(sap->output->remoteaddr); /* Open Socket */ sap_init_socket_single(sap); /* Create Stream Connection Data for SDP */ sap->cdata=sap_init_cdata(sap); /* Create Media Announcement Data for SDP */ sap->mdata=sap_init_mdata(sap); /* Create Origin Data for SDP */ sap->odata=sap_init_odata(sap); /* Start timer */ sap_init_timer_single(sap); return 1; } getstream2-20100616/config.h0000664000372000037200000000023211406140020014253 0ustar florfcorg #include #include "getstream.h" struct config_s { GList *adapter; int http_port; }; struct config_s *readconfig(char *filename); getstream2-20100616/output_udp.c0000664000372000037200000000332711406140020015221 0ustar florfcorg #include #include #include #include #include #include #include "getstream.h" #include "simplebuffer.h" #include "output.h" #include "socket.h" #define UDP_MAX_TS ((1500-40)/TS_PACKET_SIZE) int output_init_udp(struct output_s *o) { o->buffer=sb_init(UDP_MAX_TS, TS_PACKET_SIZE, 0); if (!o->buffer) goto errout1; o->sockfd=socket_open(o->localaddr, 0); if (o->sockfd < 0) goto errout2; socket_set_nonblock(o->sockfd); if (socket_connect(o->sockfd, o->remoteaddr, o->remoteport)) goto errout3; /* Join Multicast group if its a multicast destination */ socket_join_multicast(o->sockfd, o->remoteaddr); /* * Set socket TTL - Be warned - I DoSed a Cisco 5500 RSM by sending * a 60MBit/s stream in 14 Groups all with TTL of 1 and the switch * went to lala land. It seems dropping MCAST traffic is very expensive * in IOS 12.1 and its even dropped in Layer 3 instead of Layer 2 although * nobody expects ICMP "TTL expired" for MCAST traffic */ if (o->ttl) socket_set_ttl(o->sockfd, o->ttl); /* We do want to get TSP packets */ o->receiver=1; return 1; errout3: socket_close(o->sockfd); errout2: sb_free(o->buffer); errout1: return 0; } void output_send_udp(struct output_s *o, uint8_t *tsp) { int len; sb_add_atoms(o->buffer, tsp, 1); /* check whether another packet would fit ? */ if (!sb_free_atoms(o->buffer)) { /* Send packet and reset valid counter */ len=send(o->sockfd, sb_bufptr(o->buffer), sb_buflen(o->buffer), MSG_DONTWAIT); if (len != sb_buflen(o->buffer)) logwrite(LOG_DEBUG, "streamudp: send didnt send all %d of %d byte - %s\n", len, sb_buflen(o->buffer), strerror(errno)); sb_zap(o->buffer); } } getstream2-20100616/output_pipe.c0000664000372000037200000000706011406140020015364 0ustar florfcorg/* * * Stream PIPE output - Stream a program to a FIFO for postprocessing * * One of the problems i could imaging is that for a program which does not get * and traffic e.g. TS Packets we'll never discover that the reader closed the pipe * as we never issue a write. This shouldnt be a problem but you are warned. Flo 2007-04-19 * */ #include #include #include #include #include #include #include #include #include #include #include #include "output.h" #include "simplebuffer.h" #include "getstream.h" #define PIPE_BUFFER 32768 #define PIPE_INTERVAL 5 #define PIPE_MAXEAGAIN 10 static int output_pipe_tryopen(struct output_s *o) { o->pipe.fd=open(o->pipe.filename, O_NONBLOCK|O_WRONLY); if (o->pipe.fd >= 0) { logwrite(LOG_INFO, "stream_pipe: starting to write to %s - got reader", o->pipe.filename); o->receiver++; sb_zap(o->buffer); return 1; } if (errno == ENXIO) return 0; logwrite(LOG_ERROR, "stream_pipe: failed to open fifo %s", o->pipe.filename); return 0; } static void output_pipe_open_event(int fd, short event, void *arg); static void output_pipe_event_init(struct output_s *o) { struct timeval tv; tv.tv_usec=0; tv.tv_sec=PIPE_INTERVAL; evtimer_set(&o->pipe.event, output_pipe_open_event, o); evtimer_add(&o->pipe.event, &tv); } static void output_pipe_open_event(int fd, short event, void *arg) { struct output_s *o=arg; /* Try opening - if it fails - retry */ if (!output_pipe_tryopen(o)) output_pipe_event_init(o); } static void output_pipe_close(struct output_s *o) { o->receiver--; close(o->pipe.fd); logwrite(LOG_INFO, "stream_pipe: closing %s - reader exited", o->pipe.filename); /* PIPE is closed - try opening on regular interval */ output_pipe_event_init(o); } int output_init_pipe(struct output_s *o) { struct stat st; if (!lstat(o->pipe.filename, &st)) { if (!S_ISFIFO(st.st_mode)) { logwrite(LOG_ERROR, "stream_pipe: %s exists and is not a pipe", o->pipe.filename); return 0; } } else { /* if lstat fails we possibly have no fifo */ /* FIXME we need to check errno for non existant */ if (mknod(o->pipe.filename, S_IFIFO|0700, 0)) { logwrite(LOG_ERROR, "stream_pipe: failed to create fifo %s", o->pipe.filename); return 0; } } o->buffer=sb_init(PIPE_BUFFER, 1, 0); if (!o->buffer) return 0; signal(SIGPIPE, SIG_IGN); output_pipe_event_init(o); return 1; } void output_send_pipe(struct output_s *o, uint8_t *tsp) { int len; if (sb_free_atoms(o->buffer) < TS_PACKET_SIZE) { logwrite(LOG_ERROR, "stream_pipe: buffer overflow - dropping"); output_pipe_close(o); return; } sb_add_atoms(o->buffer, tsp, TS_PACKET_SIZE); len=write(o->pipe.fd, sb_bufptr(o->buffer), sb_buflen(o->buffer)); /* * We zap the buffer if we succeeded writing or not - there is no point * in keeping the data - If the reader aint fast enough there is no point * in buffering. */ if (len < 0) { if (errno == EAGAIN) { return; } logwrite(LOG_ERROR, "stream_pipe: write returned %d / %s", errno, strerror(errno)); output_pipe_close(o); return; } sb_drop_atoms(o->buffer, len); /* FIXME - We might want to do more graceful here. We tried * writing multiple TS packets to the FIFO and it failed. We * now discard ALL packets in our buffer so we might loose some. * A more graceful way would be to retry writing - For this we * might need a different buffer design e.g. a ringbuffr * * Use libevent to get a callback when the reader is ready ? * */ } getstream2-20100616/simplebuffer.h0000664000372000037200000000047611406140020015503 0ustar florfcorg void *sb_init(int atoms, int atomsize, int headroom); void sb_free(void *sbv); int sb_used_atoms(void *sbv); int sb_free_atoms(void *sbv); int sb_add_atoms(void *sbv, uint8_t *atom, int atoms); void sb_zap(void *sbv); void sb_drop_atoms(void *sbv, int atoms); uint8_t *sb_bufptr(void *sbv); int sb_buflen(void *sbv); getstream2-20100616/sap.h0000664000372000037200000000310611406140020013574 0ustar florfcorg#ifndef SAP_H #define SAP_H 1 #include "getstream.h" #include "output.h" struct sappkt_s { uint8_t version:3, addrtype:1, reserved:1, msgtype:1, encryption:1, compressed:1; uint8_t authlen; uint16_t msgidhash; uint32_t origin; }; struct sap_s { /* Config items */ int scope; char *group; int port; char *announcehost; int announceport; char *playgroup; int interval; char *uri; char *description; GList *emaillist; GList *phonelist; GList *attributelist; /* SAP socket port and address */ int fd, ttl; struct event event; int sid; /* Session Identifier */ char *name; /* Session Name */ char *cdata; /* Connection Data RFC2327 */ char *mdata; /* Media Announcement Data RFC2327 */ char *odata; /* Origin Data RFC2327 */ uint32_t originatingaddr; /* Originating Address for the SAP header */ struct output_s *output; }; #define SAP_ADDRTYPE_V4 0 #define SAP_ADDRTYPE_V6 1 #define SAP_MSGTYPE_ANNOUNCE 1 #define SAP_MSGTYPE_DELETE 0 #define SAP_V4_GLOBAL_ADDRESS "224.2.127.254" #define SAP_V4_ORG_ADDRESS "239.195.255.255" /* Organization-local SAP address */ #define SAP_V4_LOCAL_ADDRESS "239.255.255.255" /* Local (smallest non-link-local scope) SAP address */ #define SAP_V4_LINK_ADDRESS "224.0.0.255" /* Link-local SAP address */ #define SAP_TTL 15 #define SAP_PORT 9875 /* As per RFC 2974 */ #define SAP_MAX_SIZE 1024 /* As per RFC 2974 */ int sap_init(struct sap_s *sap); enum { SAP_SCOPE_NONE = 0, SAP_SCOPE_GLOBAL, SAP_SCOPE_ORG, SAP_SCOPE_LOCAL, SAP_SCOPE_LINK }; #endif getstream2-20100616/dmx.c0000664000372000037200000000706611406140020013605 0ustar florfcorg#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getstream.h" #include "psi.h" static inline char *dmxname(int adapter) { static char dmxname[128]; sprintf(dmxname, "/dev/dvb/adapter%d/demux0", adapter); return dmxname; } /* * DMX_PES_VIDEO * DMX_PES_AUDIO * DMX_PES_TELETEXT * DMX_PES_OTHER * */ static int dmx_set_pes_filter(int fd, int pid, int pestype) { struct dmx_pes_filter_params pesFilterParams; memset(&pesFilterParams, 0, sizeof(struct dmx_pes_filter_params)); logwrite(LOG_INFO,"dmx: Setting filter for pid %d pestype %d", pid, pestype); pesFilterParams.pid = pid; pesFilterParams.input = DMX_IN_FRONTEND; pesFilterParams.output = DMX_OUT_TS_TAP; pesFilterParams.pes_type = pestype; pesFilterParams.flags = DMX_IMMEDIATE_START; if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { logwrite(LOG_ERROR,"demux: ioctl DMX_SET_PES_FILTER failed for pid %u pestype %d ",pid, pestype); return 0; } return 1; } void dmx_leave_pid(struct adapter_s *a, int pid) { if (a->dmx.pidtable[pid].fd >= 0) close(a->dmx.pidtable[pid].fd); a->dmx.pidtable[pid].fd=-1; return; } int dmx_join_pid(struct adapter_s *a, unsigned int pid, int type) { int fd; /* Budget mode does not need this */ if(a->budgetmode && a->dmx.pidtable[0x2000].fd >= 0) return 1; /* Already joined ? */ if (a->dmx.pidtable[pid].fd >= 0) { logwrite(LOG_ERROR,"dmx: already joined pid %d", pid); return 1; } fd=open(dmxname(a->no), O_RDWR); if (fd < 0) { logwrite(LOG_ERROR,"dmx: failed opening dmx device for joining pid %d", pid); return 0; } if (!dmx_set_pes_filter(fd, pid, type)) { close(fd); return 0; } a->dmx.pidtable[pid].fd=fd; a->dmx.pidtable[pid].type=type; return 1; } /* * Apply section filter to opened demux interface. * */ int demux_set_sct_filter(int fd, int pid, struct dmx_filter *df, int flags, int timeout) { struct dmx_sct_filter_params sctFilterParams; memset(&sctFilterParams, 0, sizeof(struct dmx_sct_filter_params)); sctFilterParams.pid=pid; sctFilterParams.timeout=timeout; sctFilterParams.flags=flags; memcpy(&sctFilterParams.filter, df, sizeof(struct dmx_filter)); if (ioctl(fd, DMX_SET_FILTER, &sctFilterParams) < 0) { logwrite(LOG_ERROR, "demux: ioctl DMX_SET_PES_FILTER failed for pid %u",pid); exit(-1); } return 0; } /* * It is known that the flexcop chipset aka SkyStar2/AirStar cards * sometimes stop receiving interrupts. A workaround in the kernel * trys to reset the card in case the number of joined/forwarded * pids gets from 0 to 1 which means we need to drop all filters * and reaquire them. This function is called from dvr.c in case * we see no read avalability on the dvr0 device in 5 seconds. * * We are going up to PID_MAX+1 aka 0x2000 in case we are in budget * mode and just have a single filter. * */ void dmx_bounce_filter(struct adapter_s *adapter) { int i; for(i=0;i<=PID_MAX+1;i++) { if (adapter->dmx.pidtable[i].fd < 0) continue; ioctl(adapter->dmx.pidtable[i].fd, DMX_STOP); } for(i=0;i<=PID_MAX+1;i++) { if (adapter->dmx.pidtable[i].fd < 0) continue; ioctl(adapter->dmx.pidtable[i].fd, DMX_START); } } int dmx_init(struct adapter_s *adapter) { int i; /* Reset dmxfd fds - Run until 0x2000 as thats the budget mode pid */ for(i=0;i<=PID_MAX+1;i++) adapter->dmx.pidtable[i].fd=-1; return 1; } getstream2-20100616/config.c0000664000372000037200000004235311406140020014260 0ustar florfcorg #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "libconf.h" #include "sap.h" #include "output.h" #include "getstream.h" static struct config_s *config; /* Temporary variables for parsing */ static struct adapter_s *adapter; static struct output_s *output; static struct input_s *input; static struct stream_s *stream; static struct sap_s *sap; static int cf_adapter_start(struct lc_centry *ce, struct lc_value *val) { adapter=calloc(1, sizeof(struct adapter_s)); adapter->no=val->num; adapter->budgetmode=1; adapter->dvr.stuckinterval=DVR_DEFAULT_STUCKINT; adapter->dvr.buffer.size=DVR_BUFFER_DEFAULT*TS_PACKET_SIZE; config->adapter=g_list_append(config->adapter, adapter); return 1; } static int cf_sap_scope(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp(val->string, "global") == 0) { sap->scope=SAP_SCOPE_GLOBAL; } else if(strcasecmp(val->string, "org") == 0) { sap->scope=SAP_SCOPE_ORG; } else if(strcasecmp(val->string, "local") == 0) { sap->scope=SAP_SCOPE_LOCAL; } else if(strcasecmp(val->string, "link") == 0) { sap->scope=SAP_SCOPE_LINK; } else { logwrite(LOG_ERROR, "Illegal SAP scope \"%s\" in line %d\n", val->string, ce->vline); return 0; } return 1; } static int cf_sap_sap_group(struct lc_centry *ce, struct lc_value *val) { sap->group=val->string; return 1; } static int cf_sap_sap_port(struct lc_centry *ce, struct lc_value *val) { sap->port=val->num; return 1; } static int cf_sap_ttl(struct lc_centry *ce, struct lc_value *val) { sap->ttl=val->num; return 1; } static int cf_sap_interval(struct lc_centry *ce, struct lc_value *val) { sap->interval=val->num; return 1; } static int cf_sap_playgroup(struct lc_centry *ce, struct lc_value *val) { sap->playgroup=val->string; return 1; } static int cf_sap_announce_host(struct lc_centry *ce, struct lc_value *val) { sap->announcehost=val->string; return 1; } static int cf_sap_announce_port(struct lc_centry *ce, struct lc_value *val) { sap->announceport=val->num; return 1; } static int cf_sap_uri(struct lc_centry *ce, struct lc_value *val) { sap->uri=val->string; return 1; } static int cf_sap_description(struct lc_centry *ce, struct lc_value *val) { sap->description=val->string; return 1; } static int cf_sap_email(struct lc_centry *ce, struct lc_value *val) { sap->emaillist=g_list_append(sap->emaillist, val->string); return 1; } static int cf_sap_phone(struct lc_centry *ce, struct lc_value *val) { sap->phonelist=g_list_append(sap->phonelist, val->string); return 1; } static int cf_sap_attribute(struct lc_centry *ce, struct lc_value *val) { sap->attributelist=g_list_append(sap->attributelist, val->string); return 1; } static int cf_output_remoteport(struct lc_centry *ce, struct lc_value *val) { output->remoteport=val->num; return 1; } static int cf_output_remoteaddr(struct lc_centry *ce, struct lc_value *val) { output->remoteaddr=val->string; return 1; } static int cf_output_localaddr(struct lc_centry *ce, struct lc_value *val) { output->localaddr=val->string; return 1; } static int cf_output_ttl(struct lc_centry *ce, struct lc_value *val) { output->ttl=val->num; return 1; } static int cf_output_url(struct lc_centry *ce, struct lc_value *val) { output->url=val->string; return 1; } static int cf_http_port(struct lc_centry *ce, struct lc_value *val) { config->http_port=val->num; return 1; } static int cf_pipe_filename(struct lc_centry *ce, struct lc_value *val) { output->pipe.filename=val->string; return 1; } static int cf_sap_start(struct lc_centry *ce, struct lc_value *val) { sap=calloc(1, sizeof(struct sap_s)); output->sap=sap; sap->output=output; /* Default values */ sap->interval=10; sap->ttl=-1; /* use the output stream's ttl */ return 1; } static int cf_stream_start(struct lc_centry *ce, struct lc_value *val) { stream=calloc(1, sizeof(struct stream_s)); stream->adapter=adapter; adapter->streams=g_list_append(adapter->streams, stream); return 1; } static int cf_input_start(int itype) { input=calloc(1, sizeof(struct input_s)); input->type=itype; stream->input=g_list_append(stream->input, input); input->stream=stream; return 1; } static int cf_input_pid(struct lc_centry *ce, struct lc_value *val) { cf_input_start(INPUT_PID); input->pid.pid=val->num; return 1; } static int cf_input_pnr(struct lc_centry *ce, struct lc_value *val) { cf_input_start(INPUT_PNR); input->pnr.pnr=val->num; input->stream->psineeded=1; return 1; } static int cf_input_full(struct lc_centry *ce, struct lc_value *val) { cf_input_start(INPUT_FULL); return 1; } static int cf_output_start(struct lc_centry *ce, struct lc_value *val, int stype) { output=calloc(1, sizeof(struct output_s)); output->type=stype; output->ttl=15; output->stream=stream; stream->output=g_list_append(stream->output, output); return 1; } static int cf_output_udp_start(struct lc_centry *ce, struct lc_value *val) { return cf_output_start(ce, val, OTYPE_UDP); } static int cf_output_rtp_start(struct lc_centry *ce, struct lc_value *val) { return cf_output_start(ce, val, OTYPE_RTP); } static int cf_output_http_start(struct lc_centry *ce, struct lc_value *val) { return cf_output_start(ce, val, OTYPE_HTTP); } static int cf_output_pipe_start(struct lc_centry *ce, struct lc_value *val) { return cf_output_start(ce, val, OTYPE_PIPE); } struct lc_ventry conf_sap[] = { { "scope", 0, 1, LCV_STRING, 0, NULL, cf_sap_scope }, { "sap-group", 0, 1, LCV_IPV4ADDR, 0, NULL, cf_sap_sap_group }, { "sap-port", 0, 1, LCV_NUM, 0, NULL, cf_sap_sap_port }, { "announce-host", 0, 1, LCV_STRING, 0, NULL, cf_sap_announce_host }, { "announce-port", 0, 1, LCV_NUM, 0, NULL, cf_sap_announce_port }, { "ttl", 0, 1, LCV_NUM, 0, NULL, cf_sap_ttl }, { "interval", 0, 1, LCV_NUM, 0, NULL, cf_sap_interval }, { "playgroup", 0, 1, LCV_STRING, 0, NULL, cf_sap_playgroup }, { "uri", 0, 1, LCV_STRING, 0, NULL, cf_sap_uri }, { "description", 0, 1, LCV_STRING, 0, NULL, cf_sap_description }, { "email", 0, 0, LCV_STRING, 0, NULL, cf_sap_email }, { "phone", 0, 0, LCV_STRING, 0, NULL, cf_sap_phone }, { "attribute", 0, 0, LCV_STRING, 0, NULL, cf_sap_attribute }, { NULL, 0, 0, 0, 0, NULL }, }; struct lc_ventry conf_output_udp[] = { { "local-address", 0, 1, LCV_IPV4ADDR, 0, NULL, cf_output_localaddr }, { "remote-address", 1, 1, LCV_IPADDR, 0, NULL, cf_output_remoteaddr }, { "remote-port", 1, 1, LCV_NUM, 0, NULL, cf_output_remoteport }, { "ttl", 0, 1, LCV_NUM, 0, NULL, cf_output_ttl }, { "sap", 0, 1, LCV_NONE, 0, conf_sap, cf_sap_start }, { NULL, 0, 0, 0, 0, NULL, NULL }, }; struct lc_ventry conf_output_rtp[] = { { "local-address", 0, 1, LCV_IPV4ADDR, 0, NULL, cf_output_localaddr }, { "remote-address", 1, 1, LCV_IPV4ADDR, 0, NULL, cf_output_remoteaddr }, { "remote-port", 1, 1, LCV_NUM, 0, NULL, cf_output_remoteport }, { "ttl", 0, 1, LCV_NUM, 0, NULL, cf_output_ttl }, { "sap", 0, 1, LCV_NONE, 0, conf_sap, cf_sap_start }, { NULL, 0, 0, 0, 0, NULL }, }; struct lc_ventry conf_output_http[] = { { "url", 1, 1, LCV_STRING, 0, NULL, cf_output_url }, { NULL, 0, 0, 0, 0, NULL }, }; struct lc_ventry conf_output_pipe[] = { { "filename", 1, 1, LCV_STRING, 0, NULL, cf_pipe_filename }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_stream_name(struct lc_centry *ce, struct lc_value *val) { stream->name=val->string; return 1; } #if 0 static int cf_channel_csa_key(struct lc_centry *ce, struct lc_value *val) { char *eptr; uint64_t key; int i, l=strlen(val->string); key=strtoull(val->string, &eptr, 16); /* * Was the string parsed ? * Was the string a number until the end ? * Was the string between 16 and 18 (0x) bytes long ? * */ if (val->string == eptr || (eptr != NULL && eptr[0] != 0x0) || l<16 || l>18) { fprintf(stderr, "config: Invalid csa-key \"%s\" in line %d\n", val->string, ce->vline); return 0; } /* cpu_to_64be anyone ? */ for(i=0;i<8;i++) channel->csakey[i]=(key>>(56-8*i))&0xff; channel->csat=csa_New(); csa_SetCW(channel->csat, channel->csakey, channel->csakey); return 1; }; static int cf_channel_csa_length(struct lc_centry *ce, struct lc_value *val) { if (val->num > TS_PACKET_SIZE) { fprintf(stderr, "config: Invalid csa length %ld in line %d. Needs to be between 0 and 188\n", val->num, ce->vline); return 0; } channel->csalength=val->num; return 1; } static int cf_channel_csa(struct lc_centry *ce, struct lc_value *val) { channel->csalength=TS_PACKET_SIZE; return 1; } static struct lc_ventry conf_channel_csa[] = { { "key", 0, 1, LCV_STRING, 0, NULL, cf_channel_csa_key }, { "length", 0, 1, LCV_STRING, 0, NULL, cf_channel_csa_length }, { NULL, 0, 0, 0, 0, NULL }, }; #endif static struct lc_ventry conf_input[] = { { "pid", 0, 0, LCV_NUM, LCO_UNIQ, NULL, cf_input_pid }, { "pnr", 0, 1, LCV_NUM, LCO_UNIQ, NULL, cf_input_pnr }, { "full", 0, 1, LCV_NONE, LCO_UNIQ, NULL, cf_input_full }, { NULL, 0, 0, 0, 0, NULL }, }; static struct lc_ventry conf_stream[] = { { "name", 1, 1, LCV_STRING, 0, NULL, cf_stream_name }, { "sap", 0, 1, LCV_NONE, 0, conf_sap, cf_sap_start }, { "input", 0, 1, LCV_NONE, 0, conf_input, NULL }, { "output-udp", 0, 0, LCV_NONE, 0, conf_output_udp, cf_output_udp_start }, { "output-rtp", 0, 0, LCV_NONE, 0, conf_output_rtp, cf_output_rtp_start }, { "output-http", 0, 0, LCV_NONE, 0, conf_output_http, cf_output_http_start }, { "output-pipe", 0, 0, LCV_NONE, 0, conf_output_pipe, cf_output_pipe_start }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_dvbs_trans_pol(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp(val->string, "h") == 0) { adapter->fe.dvbs.t_pol=POL_H; } else if(strcasecmp(val->string, "v") == 0) { adapter->fe.dvbs.t_pol=POL_V; } else { logwrite(LOG_ERROR, "Illegal polarization \"%s\" in line %d\n", val->string, ce->vline); return 0; } return 1; } static int cf_dvbs_trans_freq(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.t_freq=val->num; return 1; } static int cf_dvbs_trans_srate(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.t_srate=val->num; return 1; } static int cf_dvbs_trans_diseqc(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.t_diseqc=val->num; return 1; } static int cf_dvbs_lnbsharing(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.lnbsharing=val->num; return 1; } static struct lc_ventry conf_dvbs_transponder[] = { { "frequency", 1, 1, LCV_NUM, 0, NULL, cf_dvbs_trans_freq }, { "polarisation", 1, 1, LCV_STRING, 0, NULL, cf_dvbs_trans_pol }, { "symbol-rate", 1, 1, LCV_NUM, 0, NULL, cf_dvbs_trans_srate }, { "diseqc", 0, 1, LCV_NUM, 0, NULL, cf_dvbs_trans_diseqc }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_dvbs_lnb_lof1(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.lnb_lof1=val->num; return 1; } static int cf_dvbs_lnb_lof2(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.lnb_lof2=val->num; return 1; } static int cf_dvbs_lnb_slof(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.lnb_slof=val->num; return 1; } static struct lc_ventry conf_dvbs_lnb[] = { { "lof1", 1, 1, LCV_NUM, 0, NULL, cf_dvbs_lnb_lof1 }, { "lof2", 0, 1, LCV_NUM, 0, NULL, cf_dvbs_lnb_lof2 }, { "slof", 0, 1, LCV_NUM, 0, NULL, cf_dvbs_lnb_slof }, { NULL, 0, 0, 0, 0, NULL }, }; static struct lc_ventry conf_dvbs[] = { { "lnb-sharing", 0, 1, LCV_BOOL, 0, NULL, cf_dvbs_lnbsharing }, { "lnb", 1, 1, LCV_NONE, 0, conf_dvbs_lnb, NULL }, { "transponder", 1, 1, LCV_NONE, 0, conf_dvbs_transponder, NULL }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_dvbt_bandwidth(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp("auto", val->string) == 0) { adapter->fe.dvbt.bandwidth=0; } else { int bw=strtol(val->string, NULL, 10); if (bw != 6 && bw != 7 && bw != 8) { fprintf(stderr, "config: Illegal DVB-T bandwidth \"%s\" in line %d\n", val->string, ce->vline); return 0; } adapter->fe.dvbt.bandwidth=bw; } return 1; } static int cf_dvbt_freq(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbt.freq=val->num; return 1; } static int cf_dvbt_tmode(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp("auto", val->string) == 0) { adapter->fe.dvbt.tmode=0; } else { int t=strtol(val->string, NULL, 10); if (t != 2 && t != 8) { fprintf(stderr, "config: Illegal DVB-T transmission-mode \"%s\" in line %d\n", val->string, ce->vline); return 0; } adapter->fe.dvbt.tmode=t; } return 1; } static int cf_dvbt_modulation(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp("auto", val->string) == 0) { adapter->fe.dvbt.modulation=0; } else { int m=strtol(val->string, NULL, 10); if (m != 16 && m != 32 && m != 64 && m != 128 && m != 256) { fprintf(stderr, "config: Illegal DVB-T modulation \"%s\" in line %d\n", val->string, ce->vline); return 0; } adapter->fe.dvbt.modulation=m; } return 1; } static int cf_dvbt_guard(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp("auto", val->string) == 0) { adapter->fe.dvbt.guard=0; } else { int gi=strtol(val->string, NULL, 10); if (gi != 4 && gi != 8 && gi != 16 && gi != 32) { fprintf(stderr, "config: Illegal DVB-T guard-interval \"%s\" in line %d\n", val->string, ce->vline); return 0; } adapter->fe.dvbt.guard=gi; } return 1; } static int cf_dvbt_hierarchy(struct lc_centry *ce, struct lc_value *val) { if (strcasecmp("none", val->string) == 0) adapter->fe.dvbt.hierarchy=-1; else if (strcasecmp("auto", val->string) == 0) adapter->fe.dvbt.hierarchy=0; else { int h=strtol(val->string, NULL, 0); if (h != 1 && h != 2 && h != 4) { fprintf(stderr, "config: Illegal DVB-T hierarchy %s in line %d\n", val->string, ce->vline); return 0; } adapter->fe.dvbt.hierarchy=h; } return 1; } static struct lc_ventry conf_dvbt[] = { { "bandwidth", 0, 1, LCV_STRING, 0, NULL, cf_dvbt_bandwidth }, { "frequency", 1, 1, LCV_NUM, 0, NULL, cf_dvbt_freq }, { "transmission-mode", 0, 1, LCV_STRING, 0, NULL, cf_dvbt_tmode }, { "modulation", 0, 1, LCV_STRING, 0, NULL, cf_dvbt_modulation }, { "guard-interval", 0, 1, LCV_STRING, 0, NULL, cf_dvbt_guard }, { "hierarchy", 0, 1, LCV_STRING, 0, NULL, cf_dvbt_hierarchy }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_dvbs(struct lc_centry *ce, struct lc_value *val) { adapter->type=AT_DVBS; return 1; } static int cf_dvbs2(struct lc_centry *ce, struct lc_value *val) { adapter->type=AT_DVBS2; return 1; } static int cf_dvbt(struct lc_centry *ce, struct lc_value *val) { adapter->type=AT_DVBT; return 1; } static int cf_dvbc(struct lc_centry *ce, struct lc_value *val) { adapter->type=AT_DVBC; return 1; } static int cf_dvbc_freq(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbc.freq=val->num; return 1; } static int cf_dvbc_modulation(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbc.modulation=val->num; return 1; } static int cf_dvbc_trans_srate(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbc.srate=val->num; return 1; } static int cf_dvbc_fec(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbc.fec=val->num; return 1; } static struct lc_ventry conf_dvbc[] = { { "frequency", 1, 1, LCV_NUM, 0, NULL, cf_dvbc_freq }, { "modulation", 0, 1, LCV_NUM, 0, NULL, cf_dvbc_modulation }, { "symbol-rate", 1, 1, LCV_NUM, 0, NULL, cf_dvbc_trans_srate }, { "fec", 0, 1, LCV_NUM, 0, NULL, cf_dvbc_fec }, { NULL, 0, 0, 0, 0, NULL }, }; static int cf_adapter_budget(struct lc_centry *ce, struct lc_value *val) { adapter->budgetmode=val->num; return 1; } static int cf_adapter_packetbuffer(struct lc_centry *ce, struct lc_value *val) { adapter->dvr.buffer.size=val->num; return 1; } static int cf_adapter_statinterval(struct lc_centry *ce, struct lc_value *val) { adapter->dvr.stat.interval=val->num; return 1; } static int cf_adapter_stuckinterval(struct lc_centry *ce, struct lc_value *val) { adapter->dvr.stuckinterval=val->num; return 1; } static struct lc_ventry conf_adapter[] = { { "budget-mode", 0, 1, LCV_BOOL, 0, NULL, cf_adapter_budget }, { "packet-buffer", 0, 1, LCV_NUM, 0, NULL, cf_adapter_packetbuffer }, { "stat-interval", 0, 1, LCV_NUM, 0, NULL, cf_adapter_statinterval }, { "stuck-interval", 0, 1, LCV_NUM, 0, NULL, cf_adapter_stuckinterval }, { "stream", 0, 0, LCV_NONE, 0, conf_stream, cf_stream_start }, { "dvb-s", 0, 1, LCV_NONE, 0, conf_dvbs, cf_dvbs }, { "dvb-s2", 0, 1, LCV_NONE, 0, conf_dvbs, cf_dvbs2 }, { "dvb-t", 0, 1, LCV_NONE, 0, conf_dvbt, cf_dvbt }, { "dvb-c", 0, 1, LCV_NONE, 0, conf_dvbc, cf_dvbc }, { NULL, 0, 0, 0, 0, NULL }, }; static struct lc_ventry conf_http[] = { { "port", 1, 1, LCV_NUM, 0, NULL, cf_http_port }, { NULL, 0, 0, 0, 0, NULL }, }; static struct lc_ventry conf_main[] = { { "adapter", 1, 0, LCV_NUM, LCO_UNIQ, conf_adapter, cf_adapter_start }, { "http", 1, 1, LCV_NONE, 0, conf_http, NULL }, { NULL, 0, 0, 0, 0, NULL }, }; struct config_s *readconfig(char *filename) { int cfd; struct stat sb; char *ctext; struct lc_centry *c; /* Base for config storage */ config=calloc(1, sizeof(struct config_s)); cfd=open(filename, O_RDONLY); if (cfd<0) return NULL; if (fstat(cfd, &sb)) { close(cfd); return NULL; } ctext=mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, cfd, 0); c=libconf_parse(ctext, sb.st_size); munmap(ctext, sb.st_size); close(cfd); if (!c) return NULL; if (!libconf_validate(c, conf_main)) { libconf_free(c); return NULL; } return config; } getstream2-20100616/output.c0000664000372000037200000000154211406140020014346 0ustar florfcorg #include "output.h" #include "getstream.h" #include "sap.h" void output_send(struct output_s *o, uint8_t *tsp) { /* Does this stream output have receiver */ if (o->receiver == 0) return; switch(o->type) { case(OTYPE_UDP): output_send_udp(o, tsp); break; case(OTYPE_RTP): case(OTYPE_RTCP): output_send_rtp(o, tsp); break; case(OTYPE_HTTP): output_send_http(o, tsp); break; case(OTYPE_PIPE): output_send_pipe(o, tsp); break; } } int output_init(struct output_s *o) { /* Initialize all stream outputs for this stream */ switch(o->type) { case(OTYPE_HTTP): output_init_http(o); break; case(OTYPE_UDP): output_init_udp(o); break; case(OTYPE_RTP): case(OTYPE_RTCP): output_init_rtp(o); break; case(OTYPE_PIPE): output_init_pipe(o); break; } if (o->sap) sap_init(o->sap); return 1; } getstream2-20100616/ringbuffer.c0000664000372000037200000000630511406140020015141 0ustar florfcorg #include #include #include #include #include /* * Ringbuffer implementation * * * head==tail Ringbuffer empty * head==tail-1 Ringbuffer full * * H H * 1234567 -> 1234567 * T T * * * * * * */ struct ringbuffer_s { uint8_t *buffer; unsigned int atoms; unsigned int atomsize; unsigned int used; unsigned int head; unsigned int tail; }; struct ringbuffer_s *ringbuffer_init(unsigned int atoms, unsigned int atomsize) { struct ringbuffer_s *rb; rb=calloc(1, sizeof(struct ringbuffer_s)); if (!rb) return NULL; rb->atoms=atoms; rb->atomsize=atomsize; rb->buffer=malloc(atomsize*atoms); if (!rb->buffer) { free(rb); return NULL; } return rb; } static int ringbuffer_headroom(struct ringbuffer_s *rb) { /* If head == tail we are either full or empty */ if (rb->used >= rb->atoms) return 0; if (rb->tail > rb->head) return rb->tail-rb->head; return rb->atoms-rb->head; } static void ringbuffer_push(struct ringbuffer_s *rb, int num) { rb->head+=num; if (rb->head >= rb->atoms) rb->head-=rb->atoms; rb->used+=num; assert(rb->used <= rb->atoms); } static int ringbuffer_add_atoms(struct ringbuffer_s *rb, uint8_t *atom, int num) { int headroom, atoms; uint8_t *dptr; headroom=ringbuffer_headroom(rb); if (!headroom) return 0; atoms=MIN(headroom, num); dptr=&rb->buffer[rb->head*rb->atomsize]; memcpy(dptr, atom, atoms*rb->atomsize); ringbuffer_push(rb, atoms); return atoms; } /* * Add atoms to the ringbuffer - We are using ringbuffer_add_atoms which * will not know about wrapping. As we know we call it twice and summ up * the result. * */ int ringbuffer_add(struct ringbuffer_s *rb, uint8_t *atom, int num) { int atoms; atoms=ringbuffer_add_atoms(rb, atom, num); if (atoms >= num) return atoms; atoms+=ringbuffer_add_atoms(rb, &atom[atoms*rb->atomsize], num-atoms); return atoms; } void ringbuffer_free(struct ringbuffer_s *rb) { free(rb->buffer), free(rb); } #ifdef TEST #include void dump_hex(char *prefix, uint8_t *buf, int size) { int i; unsigned char ch; char sascii[17]; char linebuffer[16*4+1]; sascii[16]=0x0; for(i=0;i= ' ' && ch <= '}') sascii[i%16]=ch; else sascii[i%16]='.'; if (i%16 == 15) printf("%s %s %s\n", prefix, linebuffer, sascii); } /* i++ after loop */ if (i%16 != 0) { for(;i%16 != 0;i++) { sprintf(&linebuffer[(i%16)*3], " "); sascii[i%16]=' '; } printf("%s %s %s\n", prefix, linebuffer, sascii); } } void ringbuffer_dump(struct ringbuffer_s *rb) { printf("Atoms: %d AtomSize: %d Head: %d Tail: %d Used: %d\n", rb->atoms, rb->atomsize, rb->head, rb->tail, rb->used); dump_hex(" ", rb->buffer, rb->atoms*rb->atomsize); printf("\n"); } int main(void ) { struct ringbuffer_s *rb=ringbuffer_init(7, 1); int i, j; uint8_t add[2]; for(i=1;i<=5;i++) { add[0]=i; add[1]=i; ringbuffer_dump(rb); printf("Trying to add 2 bytes... \n"); j=ringbuffer_add(rb, add, 2); printf("... returned j=%d\n", j); ringbuffer_dump(rb); } } #endif getstream2-20100616/.gitignore0000664000372000037200000000011011406140020014620 0ustar florfcorg.* *.o *.o.* *.a *.s core* *.orig *.rej tsdecode getstream !.gitignore getstream2-20100616/util.c0000664000372000037200000000316011406140020013761 0ustar florfcorg#include #include #include #include #include "getstream.h" int addr_is_mcast(char *addr) { struct in_addr iaddr; inet_aton(addr, &iaddr); return IN_MULTICAST(iaddr.s_addr); } void dump_hex(int level, const char *prefix, uint8_t *buf, int size) { int i; unsigned char ch; char sascii[17]; char linebuffer[16*4+1]; /* Speedup */ if (level > loglevel) return; sascii[16]=0x0; for(i=0;i= ' ' && ch <= '}') sascii[i%16]=ch; else sascii[i%16]='.'; if (i%16 == 15) logwrite(level, "%s %s %s", prefix, linebuffer, sascii); } /* i++ after loop */ if (i%16 != 0) { for(;i%16 != 0;i++) { sprintf(&linebuffer[(i%16)*3], " "); sascii[i%16]=' '; } logwrite(level, "%s %s %s", prefix, linebuffer, sascii); } } void ts_packet_decode(uint8_t *ts) { logwrite(LOG_DEBUG, "ts_packet_decode\n"); dump_hex(LOG_DEBUG, "pdecode:", ts, TS_PACKET_SIZE); logwrite(LOG_DEBUG, "\tsync: %02x\n", ts[0]); logwrite(LOG_DEBUG, "\ttransport_error_indicator: %d\n", ts[1]&0x80 ? 1 : 0); logwrite(LOG_DEBUG, "\tpayload_unit_start_indicator: %d\n", ts[1]&0x40 ? 1 : 0); logwrite(LOG_DEBUG, "\ttransport_priority: %d\n", ts[1]&0x20 ? 1 : 0); logwrite(LOG_DEBUG, "\tpid: %d\n", (ts[1]<<8|ts[2])&0x1fff); logwrite(LOG_DEBUG, "\ttransport_scrambling_control: %d\n", (ts[3]>>6) & 0x3); logwrite(LOG_DEBUG, "\tadaption_field_control: %d\n", (ts[3]>>4) & 0x3); logwrite(LOG_DEBUG, "\tcontinuity_counter: %d\n", ts[3]&0xf); } getstream2-20100616/socket.h0000664000372000037200000000036511406140020014305 0ustar florfcorg int socket_join_multicast(int sock, char *addr); int socket_set_ttl(int sock, int ttl); int socket_set_nonblock(int sock); int socket_open(char *laddr, int port); void socket_close(int sock); int socket_connect(int sock, char *addr, int port); getstream2-20100616/psi.c0000664000372000037200000001516211406140020013604 0ustar florfcorg#include #include #include "getstream.h" #include "psi.h" #include "crc32.h" /* * PSI (Program Specific Information) handling * * Reassembly of TS Packets into sections * Handling of multiple sections * Callback on change * * * */ static uint32_t psi_crc(struct psisec_s *section) { uint8_t *crcp=§ion->data[psi_len(section)-4]; return crcp[0]<<24|crcp[1]<<16|crcp[2]<<8|crcp[3]; } /* Calculate the CRC of the section */ static uint32_t psi_ccrc(struct psisec_s *section) { return crc32_be(0xffffffff, section->data, psi_len(section)-4); } /* Check if PSI has valid CRC32 - return true if it has */ static int psi_crc_valid(struct psisec_s *section) { return (psi_crc(section) == psi_ccrc(section)); } static void psi_section_clear(struct psisec_s *section) { section->valid=0; section->len=0; } int psi_reassemble_continue(struct psisec_s *section, uint8_t *ts, int off) { int copylen; uint8_t ccexp, cc; /* * Calculate the next CC counter value. As a section needs to * be completed before the next may begin on a PID we only * accept continuous packets. If we fail the CC test we zap * the whole section. * */ ccexp=(section->cc+1)&TS_CC_MASK; cc=ts_cc(ts); if (ccexp != cc) { psi_section_clear(section); return PSI_RC_CCFAIL; } /* * If we didnt have a hdr - complete it otherwise we dont * even know the length and cant tell whether the section is * complete. */ if (!section->len) { copylen=PSI_HDR_LEN-section->valid; memcpy(§ion->data[section->valid], &ts[off], copylen); section->valid+=copylen; section->len=_psi_len(section->data); off+=copylen; } copylen=MIN(TS_PACKET_SIZE-off, section->len-section->valid); memcpy(§ion->data[section->valid], &ts[off], copylen); section->valid+=copylen; section->cc=cc; return off+copylen; } /* * Copy the start of an PSI packet into our section buffer beginning * at offset and fill section->len if possible * * Return the new offset */ int psi_reassemble_start(struct psisec_s *section, uint8_t *ts, int off) { uint8_t *payloadptr=&ts[off]; int copylen; psi_section_clear(section); section->cc=ts_cc(ts); section->pid=ts_pid(ts); /* Copy until the end of the packet */ copylen=TS_PACKET_SIZE-off; /* * If not we include the PSI header in which case we * can get the real length * */ if (TS_PACKET_SIZE-off > PSI_HDR_LEN) { section->len=_psi_len(payloadptr); copylen=MIN(section->len, TS_PACKET_SIZE-off); } memcpy(section->data, payloadptr, copylen); section->valid=copylen; return off+copylen; } /* * We get passed a static allocated psisec structure, a TS packet and an * offset into the packet where we need to start looking for sections. * * Input: * PSI section structute * TS packet * Offset to start looking for PSI data * * Output: * Fills PSI section as far as possible * * Returns: * 0 - If section is finished and no more bytes in TS * positive- If section is finished and more bytes in TS * negative- If section is not finished and we are done * */ /* Reassemble a generic PSI (Program Specific Information) section e.g. PMT PAT CA packet */ int psi_reassemble(struct psisec_s *section, uint8_t *ts, int off) { int noff; int payload; if (off) { payload=off; if (ts[payload] == 0xff) return PSI_RC_NOPAYLOAD; } else { if (ts_tei(ts)) return PSI_RC_TEI; if (!ts_has_payload(ts)) return PSI_RC_NOPAYLOAD; payload=ts_payload_start(ts); /* * If "Payload Unit Start Indicator" is set the first byte * payload is the pointer to the first section. * ISO 13818-1 2.4.4.2 */ if (ts_pusi(ts)) payload+=ts[payload]+1; if (payload >= TS_PACKET_SIZE) return PSI_RC_CORRUPT; } /* If we dont have a Payload Unit Start Indicator and we already * started a section on this PID (valid is non null) we continue * otherwise we start the reassembly ... * * This means that a packet with a */ if (!ts_pusi(ts)) { if (!section->valid) return PSI_RC_NOPAYLOAD; noff=psi_reassemble_continue(section, ts, payload); } else { noff=psi_reassemble_start(section, ts, payload); } /* * We didnt finish this section in this packet so wait for the next packet * The section->valid check is another precaution for broken/empty * invalid section parts collected ... */ if (section->len != section->valid || section->valid < PSI_HDR_LEN) return PSI_RC_INCOMPLETE; if (!psi_crc_valid(section)) return PSI_RC_CRCFAIL; return noff; } int psi_section_valid(unsigned int pid, struct psisec_s *section, int len) { section->pid=pid; section->len=len; section->valid=0; if (!psi_crc_valid(section)) return PSI_RC_CRCFAIL; if (psi_len(section)!=len) return PSI_RC_LENFAIL; return PSI_RC_OK; } struct psisec_s *psi_section_new(void ) { return calloc(1, sizeof(struct psisec_s)); } void psi_section_free(struct psisec_s *section) { free(section); } struct psisec_s *psi_section_clone(struct psisec_s *section) { struct psisec_s *new=psi_section_new(); memcpy(new, section, sizeof(struct psisec_s)); return new; } int psi_section_fromdata(struct psisec_s *section, unsigned int pid, uint8_t *data, int len) { psi_section_clear(section); memcpy(§ion->data, data, len); return psi_section_valid(pid, section, len); } unsigned int psi_segment_and_send(struct psisec_s *section, unsigned int pid, uint8_t cc, void (*callback)(void *data, void *arg), void *arg) { uint8_t ts[TS_PACKET_SIZE]; int pkts=0; int plen=psi_len(section); int left=plen; int tspayloadoff; int copylen; while(1) { memset(&ts, 0xff, TS_PACKET_SIZE); ts[TS_SYNC_OFF]=TS_SYNC; ts[TS_PID_OFF1]=pid>>8; ts[TS_PID_OFF2]=pid&0xff; ts[TS_AFC_OFF]=0x1<data[plen-left], copylen); callback(ts, arg); left-=copylen; pkts++; if (left <= 0) break; } return pkts; } int psi_update_table(struct psi_s *psi, struct psisec_s *section) { uint8_t secnum; uint8_t version; secnum=psi_section_number(section); version=psi_version(section); /* Check if we have this section or if the section version changed */ if (psi->section[secnum]) { if (version == psi_version(psi->section[secnum])) return 0; psi_section_free(psi->section[secnum]); } psi->section[secnum]=psi_section_clone(section); return 1; } getstream2-20100616/output.h0000664000372000037200000000521111406140020014350 0ustar florfcorg#ifndef STREAM_H #define STREAM_H #include "getstream.h" #include "libhttp.h" #include #include #define RTCP_BUFFER_SIZE 4096 #define RTCP_VERSION_OFF 0 #define RTCP_VERSION_SHIFT 6 #define RTCP_PT_OFF 1 #define RTCP_VERSION(x) (x[RTCP_VERSION_OFF]>>RTCP_VERSION_SHIFT) #define RTCP_PT(x) (x[RTCP_PT_OFF]) #define RTP_PT_H261 31 /* RFC2032 */ #define RTP_PT_MP2T 33 /* RFC2250 */ #define RTP_PT_RR 201 #define RTP_PT_BYE 203 #define RTP_PT_OFF 1 #define RTP_VERSION_OFF 0 #define RTP_SEQ_OFF 2 #define RTP_TSTAMP_OFF 4 #define RTP_SSRC_OFF 8 #define RTP_MAX_PAYLOAD 1000 #define RTP_MAX_TS (RTP_MAX_PAYLOAD/TS_PACKET_SIZE) #define RTP_HEADROOM 12 enum { OTYPE_UDP, OTYPE_RTP, OTYPE_RTCP, OTYPE_HTTP, OTYPE_PIPE }; #if 0 struct stream_out_rtp_s { /* RTCP informations */ int rtcpfd; struct event rtcpevent; char *rtcpbuf; struct sockaddr *rtcpsockaddr; int rtcpsockaddrlen; /* RTP Informations */ int rtpfd; struct addrspec local, remote; struct rtp_receiver_s *rcvr; int ttl; uint8_t *buffer; int buffervalid; }; #endif struct http_receiver_s { struct http_receiver_s *next; struct http_connection *hc; struct output_s *output; int overflow; }; struct rtp_receiver_s { struct rtp_receiver_s *next; char *addr; int port; struct sockaddr_in sin; int sinlen; time_t lastrr; uint32_t ssrc; }; struct output_s { /* Config elements */ struct output_s *next; int type; /* Simple Buffer */ void *buffer; /* UDP & RTP - MCast or UCast */ char *remoteaddr; int remoteport; int ttl; /* RTCP or HTTP local port or local address */ char *localaddr; struct sap_s *sap; /* */ //struct channel_s *channel; struct stream_s *stream; int receiver; /* No of receivers */ int sockfd; /* RTP/RTCP */ uint8_t *rtcpbuffer; struct rtp_receiver_s *rtpreceiver; int rtcpfd; uint16_t rtpseq; uint32_t rtpssrc; int rtpport, rtcpport; struct event rtcpevent; /* HTTP */ char *url; GList *http_receiver; struct http_url *hurl; /* PIPE */ struct { char *filename; int fd; time_t last; struct event event; } pipe; }; int output_init(struct output_s *channel); int output_init_udp(struct output_s *o); int output_init_rtp(struct output_s *o); int output_init_http(struct output_s *o); int output_init_pipe(struct output_s *o); void output_send(struct output_s *c, uint8_t *tsp); void output_send_udp(struct output_s *o, uint8_t *tsp); void output_send_rtp(struct output_s *o, uint8_t *tsp); void output_send_http(struct output_s *o, uint8_t *tsp); void output_send_pipe(struct output_s *o, uint8_t *tsp); #endif getstream2-20100616/libhttp.c0000664000372000037200000003032211406140020014452 0ustar florfcorg #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libhttp.h" //#define DEBUG #ifdef DEBUG #define Dprintf printf #else #define Dprintf( a... ) #endif int http_get_cmd(char *cmd) { if (strcasecmp("GET", cmd) == 0) return HC_CMD_GET; if (strcasecmp("POST", cmd) == 0) return HC_CMD_POST; if (strcasecmp("HEAD", cmd) == 0) return HC_CMD_HEAD; return HC_CMD_UNKNOWN; } /* Split line into string elements seperated by space or tab */ static int split_string_by_space(char *input, char **elem, int max) { int no; char *i=input; for(no=0;no HTTP/ * */ static int http_parse_command_url(struct http_connection *hc, struct evbuffer *input) { char *p=(char *) EVBUFFER_DATA(input); char *elem[10]; int no; no=split_string_by_space(p, elem, 10); /* Need "CMD URL PROTO/VERSION" which makes it 3 parms */ if (no != 3) return 0; if ((hc->cmd=http_get_cmd(elem[0])) == HC_CMD_UNKNOWN) return 0; hc->url=strdup(elem[1]); hc->proto=http_parse_proto(elem[2]); if (hc->proto == HP_HTTP11) hc->keepalive=1; else hc->keepalive=0; return 1; } static int http_parse_attrib(struct http_connection *hc, struct evbuffer *input) { char *p=(char *) EVBUFFER_DATA(input); struct http_attrib *ha; char *token, *value; token=p; while(*p != ':' && *p != ' ' && *p != 0x0) p++; /* Did we find the colon as delimiter ? * If not something went really wrong - terminate the connection */ if (*p == ':') *p++=0x0; else return 0; while(*p == ' ' || *p == '\t') p++; value=p; ha=malloc(sizeof(struct http_attrib)); ha->token=strdup(token); ha->value=strdup(value); hc->attrib=g_list_append(hc->attrib, ha); Dprintf("%s: %s %s\n", __FUNCTION__, token, value); return 1; } /* * HTTP/1.1 404 Not Found * Date: Wed, 17 May 2006 17:33:00 GMT * Server: Apache/2.0.54 (Debian GNU/Linux) * Content-Length: 310 * Connection: close * Content-Type: text/html; charset=iso-8859-1 * */ static char *http_static_msg_404 = " \ \ 404 Not Found \ \

404 Not Found

\ \ "; int http_url_handler_404(struct http_connection *hc, int cbtype, void *arg) { int err; Dprintf("%s: cbtype %d\n", __FUNCTION__, cbtype); /* Answer with 404 page on QUERY */ if (cbtype == HCB_QUERY) { err=http_return_simple(hc, "404 Not found", "text/html", http_static_msg_404, strlen(http_static_msg_404)); return 1; } /* * Drop connection if socket gets writeable again * or we have an error */ http_request_end(hc); return 1; } size_t http_get_queue(struct http_connection *hc) { return EVBUFFER_LENGTH(hc->bev->output); } /* * Function to return an http chunk with previous unknown size. HTTP/1.1 allows * for chunked transfers which HTTP/1.0 doesnt know about. There is no legal way * to do this so we hope our best. * */ int http_return_stream(struct http_connection *hc, void *data, size_t datalen) { /* We are chunked and done sending - end session */ if (!data) { /* Chunked transfer - send a 0 chunk */ if (hc->proto == HP_HTTP11) { evbuffer_add_printf(hc->evb,"0\r\n\r\n"); bufferevent_write_buffer(hc->bev, hc->evb); } hc->status=HC_STATUS_END; return 1; } /* In case of Transfer-Encoding: chunked send a chunk - otherwise just data */ if (hc->proto == HP_HTTP11) { evbuffer_add_printf(hc->evb,"%x\r\n", datalen); evbuffer_add(hc->evb, data, datalen); evbuffer_add_printf(hc->evb, "\r\n"); } else { evbuffer_add(hc->evb, data, datalen); } bufferevent_write_buffer(hc->bev, hc->evb); Dprintf("%s: output buffer len %d\n", __FUNCTION__, EVBUFFER_LENGTH(hc->bev->output)); return 1; } int http_header_add(struct http_connection *hc, char *fmt, ...) { va_list ap; va_start(ap, fmt); hc->hsize+=vsprintf(&hc->hdr[hc->hsize], fmt, ap); hc->hdr[hc->hsize++]=0x0d; hc->hdr[hc->hsize++]=0x0a; hc->hdr[hc->hsize]=0x00; va_end(ap); return 0; } int http_header_end(struct http_connection *hc) { if (hc->keepalive) http_header_add(hc, "Connection: keep-alive"); else http_header_add(hc, "Connection: close"); hc->hsize+=sprintf(&hc->hdr[hc->hsize], "\r\n"); bufferevent_write(hc->bev, hc->hdr, hc->hsize); hc->hsize=0; return 0; } int http_header_clength(struct http_connection *hc, ssize_t length) { if (length >= 0) http_header_add(hc, "Content-Length: %d", length); else { if (hc->proto == HP_HTTP11) http_header_add(hc, "Transfer-Encoding: chunked"); } return 0; } int http_header_nocache(struct http_connection *hc) { http_header_add(hc, "Pragma: no-cache"); http_header_add(hc, "Cache-Control: no-cache"); return 0; } int http_header_start(struct http_connection *hc, char *result, char *type) { struct tm *tm; time_t t; /* Wed, 17 May 2006 17:33:00 GMT */ t=time(NULL); tm=gmtime(&t); hc->hsize=0; hc->hsize+=sprintf(&hc->hdr[hc->hsize], "HTTP/1.1 %s\r\n", result); hc->hsize+=sprintf(&hc->hdr[hc->hsize], "Date: "); hc->hsize+=strftime(&hc->hdr[hc->hsize], MAX_HEADER_SIZE-hc->hsize, "%a, %d %b %Y %H:%M:%S %Z\r\n", tm); http_header_add(hc, "Content-Type: %s", type); return 0; } /* * Send a simple request answer: * Content-Length: is set - No chunked, partial transfer, * After requests gets answered - connection will be closed * */ int http_return_simple(struct http_connection *hc, char *result, char *type, void *data, size_t datalen) { http_header_start(hc, result, type); http_header_clength(hc, datalen); http_header_end(hc); bufferevent_write(hc->bev, data, datalen); hc->status=HC_STATUS_END; return 1; } static int http_process_query(struct http_connection *hc) { struct http_url *hu; /* * FIXME Need to parse url to parameters, unescaping * characters etc ... * */ hu=g_hash_table_lookup(hc->server->urls, hc->url); Dprintf("%s: hu %p\n", __FUNCTION__, hu); if (hu) { hc->url_handler=hu->cb; return hc->url_handler(hc, HCB_QUERY, hu->arg); } return hc->url_handler(hc, HCB_QUERY, NULL); } static int http_read_head(struct http_connection *hc, struct bufferevent *bev) { struct evbuffer *input = EVBUFFER_INPUT(bev); u_char *eol, *p; int err, drainlen; while((eol=evbuffer_find(input, (u_char *) "\n", 1)) != NULL) { /* get begin of line */ p=EVBUFFER_DATA(input); /* Line length */ drainlen=eol-p+1; /* * Remove \n and if exists the \r * FIXME Is this robust enough ? */ *eol=0x0; if (eol > p && *(eol-1) == '\r') *(--eol) = 0x0; if (!hc->url) { /* Did we have the first line with command and url ? */ err=http_parse_command_url(hc, input); } else if (eol == p) { err=http_process_query(hc); } else { err=http_parse_attrib(hc, input); } /* tell buffer to remove leading bytes */ evbuffer_drain(input, drainlen); if (!err) return 0; } return 1; } /* * Free Request based resources from the http_connection * */ static void http_request_free(struct http_connection *hc) { GList *item; struct http_attrib *ha; if (hc->url) free(hc->url); hc->url=NULL; /* Free http attributes */ while((item=g_list_first(hc->attrib))) { hc->attrib=g_list_remove_link(hc->attrib, item); ha=item->data; free(ha->token); free(ha->value); free(ha); g_list_free(item); } } /* * Drop the connection in case of error of non keepalive */ void http_drop_connection(struct http_connection *hc) { struct http_server *hs=hc->server; Dprintf("%s:%d\n", __FUNCTION__, __LINE__); hs->conn=g_list_remove(hs->conn, hc); bufferevent_disable(hc->bev, EV_READ|EV_WRITE); bufferevent_free(hc->bev); http_request_free(hc); close(hc->fd); evbuffer_free(hc->evb); free(hc); } /* * Application signals http request end - either * drop the connection or simply free the request * resources. * */ void http_request_end(struct http_connection *hc) { if (!hc->keepalive) { http_drop_connection(hc); } else { http_request_free(hc); hc->status=HC_STATUS_HEAD; hc->url_handler=&http_url_handler_404; hc->hsize=0; } } /* * Callback handler for EV_READ of the client connection. If data * arrives for our handler */ static void http_cb_read(struct bufferevent *bev, void *arg) { struct http_connection *hc=arg; Dprintf("%s:%d\n", __FUNCTION__, __LINE__); switch(hc->status) { case(HC_STATUS_END): hc->url_handler(hc, HCB_END, hc->arg); break; case(HC_STATUS_HEAD): if (!http_read_head(hc, bev)) hc->url_handler(hc, HCB_ERROR, hc->arg); break; case(HC_STATUS_BODY): hc->url_handler(hc, HCB_READ, hc->arg); break; } } static void http_cb_write(struct bufferevent *bev, void *arg) { struct http_connection *hc=arg; Dprintf("%s:%d\n", __FUNCTION__, __LINE__); if (hc->status != HC_STATUS_END) hc->url_handler(hc, HCB_WRITE, hc->arg); if (hc->status == HC_STATUS_END) hc->url_handler(hc, HCB_END, hc->arg); } static void http_cb_error(struct bufferevent *bev, short what, void *arg) { struct http_connection *hc=arg; Dprintf("%s:%d\n", __FUNCTION__, __LINE__); hc->url_handler(hc, HCB_ERROR, hc->arg); } static void http_connect(int fd, short ev, void *arg) { struct http_server *hs=arg; struct http_connection *hc; struct sockaddr_in sin; socklen_t slen=sizeof(struct sockaddr_in); int nfd; unsigned long flags; nfd=accept(fd, (struct sockaddr *) &sin, &slen); if (nfd < 0) return; Dprintf("%s:%d New connection\n", __FUNCTION__, __LINE__); /* Set new socket O_NONBLOCK */ flags=fcntl(nfd, F_GETFL); fcntl(nfd, F_SETFL, flags | O_NONBLOCK); hc=calloc(1, sizeof(struct http_connection)); hc->fd=nfd; hc->server=hs; hc->status=HC_STATUS_HEAD; hc->cid=hs->cid++; hc->evb=evbuffer_new(); /* Copy remote endpoint sockaddr to http_connection structure */ memcpy(&hc->sin, &sin, sizeof(struct sockaddr_in)); hc->bev=bufferevent_new(hc->fd, http_cb_read, http_cb_write, http_cb_error, hc); bufferevent_enable(hc->bev, EV_READ); hc->url_handler=&http_url_handler_404; hs->conn=g_list_append(hs->conn, hc); } int http_register_url(struct http_server *hs, struct http_url *hu) { while(hu->url) { g_hash_table_insert(hs->urls, hu->url, hu); hu++; } return 1; } struct http_server *http_init(int port) { struct http_server *hs; unsigned long flags; int one=1; hs=calloc(1, sizeof(struct http_server)); hs->port=port; hs->urls=g_hash_table_new(g_str_hash, g_str_equal); hs->fd=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt(hs->fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(int)); hs->sin.sin_family=AF_INET; hs->sin.sin_port=htons(port); hs->sin.sin_addr.s_addr=INADDR_ANY; if (bind(hs->fd, (struct sockaddr *) &hs->sin, sizeof(struct sockaddr_in))) return NULL; if (listen(hs->fd, 3)) return NULL; flags=fcntl(hs->fd, F_GETFL); fcntl(hs->fd, F_GETFL, flags | O_NONBLOCK); event_set(&hs->ev, hs->fd, EV_READ|EV_PERSIST, http_connect, (void *) hs); event_add(&hs->ev, NULL); return hs; }