getstream2-20081204/0000755000223500017500000000000011115763535012541 5ustar flomwaysgetstream2-20081204/Changelog0000644000223500017500000002634111115763532014356 0ustar flomwayscommit 6647c90e6c44cfbd2188696e4ee3ad37284d8e50 Author: Florian Lohoff Date: Thu Dec 4 15:05:02 2008 +0100 Add more files to ignore for git commit 56d591d72e84dd6da3ab185ce9b24965304a9926 Author: Romolo Manfredini Date: Thu Dec 4 10:32:03 2008 +0100 Rewrite DiseqC setup code commit 05c581b9effa70f30b28420649a4b91677be2e89 Author: Florian Lohoff Date: Wed Sep 17 07:37:31 2008 +0200 Fix another VLC 0.9.2 incompatibility Since 0.9.2 vlc refused to accept a hex session id in SAP announcements. commit b8c48e15e108993051f87cd23fa6f0fa0d4a0027 Author: Florian Lohoff Date: Tue Sep 16 17:18:12 2008 +0200 Add a PSI sanity check Damjan Marion reported getstream crashes in case of a high BER which were caused by the pointer field beeing corrupted. commit 70a81959162b8dc2de688fa7275a6fefc751002a Author: Florian Lohoff Date: Tue Sep 16 14:03:16 2008 +0200 Another bug hint from Egor Shibeko with the PSI versioning commit 2598fd5f8d024f24a45395cc58adcd4ee9b2670c Author: Florian Lohoff Date: Tue Sep 16 12:05:59 2008 +0200 Fix hang in PSI reassembly Got a report about a possible hang. It seems when a PSI section would exactly consume the full TS packet up to the last byte getstream could hang. commit 5a8c04d9678f10c60e03c0eb8cc810dce9f7d13a Author: Florian Lohoff Date: Tue Sep 16 11:19:23 2008 +0200 Fix adaption field size calculation This did not occur for me as i dont see any adaption field but in case we have an adaption field the psi would not work. commit e2d242c94db84e54c8e061f25b5d7db29a2e8311 Author: Florian Lohoff Date: Tue Sep 16 11:08:22 2008 +0200 Fix SDP incompatibility with VLC 0.9.2 Change order of SDP lines in the SAP announcements. VLC 0.9.2 seems to be picky about ordering although i cant seem to find it in the RFC. Pull o= to the top. commit b1af6bce8d6d23647bfb0794ff7b68bd2a9bb328 Author: Florian Lohoff Date: Sat Apr 26 06:29:26 2008 +0000 Follow API changes of Multiproto (DVBFE_SET_DELSYS) commit dc763bf68578bf8a1376a53466d66be2962c3061 Author: Florian Lohoff Date: Sat Apr 26 06:17:28 2008 +0000 Add Multiproto build instructions commit 3f03e00c16f2c44267a8fbceef730136f0cbc45a Author: Florian Lohoff Date: Sat Mar 1 21:10:09 2008 +0000 Fix HTTP memory leak (didnt free the http connection), reuse evbuffer in libhttp.c commit b4e7b83c0af5146dee617a3ce3aca54782371002 Author: Florian Lohoff Date: Sat Mar 1 20:43:18 2008 +0000 Cleanup some DVB capability checks Needed for multiproto which makes it a broken API. commit 4eefaa8f2887cfcf171844036e542e84e323725c Author: Florian Lohoff Date: Sat Mar 1 17:04:13 2008 +0000 Fix http bug/crash This bug is is remote triggerable by sending data in the http connection which is not the right format commit 086d94229d6c14141a1c74d8cb7f730f25319f45 Author: Florian Lohoff Date: Sat Mar 1 15:09:16 2008 +0100 Fix Multiproto init bug With multiproto one HAS TO ISSUE an ioctl DVBFE_GET_INFO otherwise the information about the delivery system is not known in kernel - I think this is a kernel bug ... commit 94423d2c52fcd3a1bf51dc65a884f7467655ae72 Author: Florian Lohoff Date: Wed Feb 27 11:04:27 2008 +0100 Add some hints about DVB-S2 to Documentation commit 2097ec13b146945b17c151eaaa1bb932521c9331 Author: Florian Lohoff Date: Wed Feb 27 11:01:00 2008 +0100 Fix error messages on adapter type mismatch commit 69c2df82e18e9ced5d1b0c97e97f890376eb73b3 Author: Florian Lohoff Date: Tue Feb 26 12:11:21 2008 +0100 Fix some non Multiproto compile errors commit e68b166ce3b192ec7f21961bdd9ce52067508100 Author: Florian Lohoff Date: Tue Feb 26 13:08:55 2008 +0000 Add Multiproto support commit a4427be6acf555e9dbddc325d3698a2c24918558 Author: Florian Lohoff Date: Tue Feb 26 10:24:53 2008 +0000 Fix small error log bug in fe.c concerning FEC_AUTO commit 90ad8b10258fd560b003e6b87932261db249f7eb Author: Florian Lohoff Date: Thu Feb 21 08:27:19 2008 +0100 The frontend filedescriptor may actually be 0 Thank goes to Karel Tuma for the patch. commit 5f5c8b35a865454fd9ed1295b19555481370382f Author: Florian Lohoff Date: Thu Feb 21 08:12:41 2008 +0100 Added setsockopt for the ttl to output_rtp.c commit 626f690a94799c46a94388b17e99f4e4a17937d4 Author: Florian Lohoff Date: Thu Feb 21 08:07:25 2008 +0100 Documentation for the input section e.h. pid, pnr and full commit 31a971139fe8c2e62be003fe0fd27a62ddcb4a69 Author: Florian Lohoff Date: Thu Feb 21 07:41:00 2008 +0100 More error msg on send error in output_udp, Add full transponder commit 9cfcde6534419c718d953b2e3c87f18ee553cce9 Author: Florian Lohoff Date: Wed Feb 20 12:43:15 2008 +0100 Fix local-address beeing mandatory for udp and rtp commit d6adb306c96b37cdf08f8c376cc9760666cdd17d Author: Florian Lohoff Date: Tue Feb 19 19:17:30 2008 +0100 Add possibility to bind to local address for output_{udp,rtp} commit af56a42d016f3aa1a6491199849eaaec81b8251f Author: Florian Lohoff Date: Tue Feb 19 18:57:58 2008 +0100 Fix RTP output SAP config - was removed on getstream2 rewrite commit 61a0a29faa22dbfc11a0d4a4741645bbc7cebde5 Author: Florian Lohoff Date: Tue Feb 19 18:33:05 2008 +0100 Fix error paths in output_{udp,rtp}.c and add socket_close commit c40b84599ffb8f896c850c114e21e205246c38fb Author: Florian Lohoff Date: Tue Feb 19 15:57:44 2008 +0100 Convert SAP to socket helpers and make it join the MCast group commit 70d55fef07e25f232f8b025b0124194f68244353 Author: Florian Lohoff Date: Tue Feb 19 15:46:57 2008 +0100 Fix a socket_set_nonblock in output_rtp.c commit 15f2a3ae3cd6120c782d50d2d074b4ead7866dd4 Author: Florian Lohoff Date: Tue Feb 19 15:44:23 2008 +0100 Fix startup crash in PSI reassembly Fix an startup crash caused by trying to continue a non started psi reassembly. Use the opportunity to simplify the decisions. Backtrace looked like this: #0 0x0804b351 in crc32_be (crc=3072780846, p=0x806ee90 "", len=-192881) at crc32.c:413 #1 0x08051406 in psi_ccrc (section=0x806ee80) at psi.c:25 #2 0x08051427 in psi_crc_valid (section=0x806ee80) at psi.c:30 #3 0x0805179c in psi_reassemble (section=0x806ee80, ts=0x80602c8 "G", off=0) at psi.c:178 #4 0x0804fb93 in dvr_input_ts (a=0xb7cd5008, ts=0x80602c8 "G") at dvr.c:64 #5 0x0804fa63 in dvr_read (fd=8, event=2, arg=0xb7cd5008) at dvr.c:168 #6 0xb7eeac79 in event_base_priority_init () from /usr/lib/libevent-1.1a.so.1 #7 0xb7eeaf65 in event_base_loop () from /usr/lib/libevent-1.1a.so.1 #8 0xb7eeadcb in event_loop () from /usr/lib/libevent-1.1a.so.1 #9 0xb7eeacb0 in event_dispatch () from /usr/lib/libevent-1.1a.so.1 #10 0x08049d42 in main (argc=4, argv=0xbfad8d74) at getstream.c:177 commit a8b801f854fb4e403025cbc5165286737cfbcb46 Author: Florian Lohoff Date: Tue Feb 19 14:43:29 2008 +0100 Fix endianess Bug in IN_MULTICAST commit 0b7ddc67bf4aa03def01d54c4298ad10ebbeb906 Author: Florian Lohoff Date: Tue Feb 19 15:37:55 2008 +0100 Add some socket helpers, drop RTCP, add MCast joins * Add socket helpers * Convert output_rtp and output_udp to socket helpers * Drop RTCP implementations (#if 0) because its incomplete * Add MULTICAST joins to output_udp and output_rtp commit a33a6cb51487f3ec483c82f28f41fa589de4b825 Author: Florian Lohoff Date: Mon Oct 29 14:17:44 2007 +0100 Make lnb-sharing optional and add stuck-interval check option commit 83f60c31bae813d279b700412a6a3c157664cd48 Author: Florian Lohoff Date: Thu Oct 25 10:02:53 2007 +0200 Add lnb-sharing documentation commit 910a8b71fd0d15b85a4f064a2336bfc5074338ca Author: Florian Lohoff Date: Thu Oct 25 09:55:22 2007 +0200 Add DVB-S config and stat-interval to README commit 706e5eb9a3c4e7f76f2b4091d1146ab13caa9df2 Author: Florian Lohoff Date: Sun Sep 23 17:27:17 2007 +0200 Add lnb sharing option Add lnb-sharing option to let the frontend code turn off lnb influencing tones and voltages. commit 843320225720e743321896a204f76f6c52881f73 Author: Florian Lohoff Date: Sun Sep 23 10:13:26 2007 +0200 Check for correct card capabilities and dump fe setup Add FE_GET_INFO and check for correct card type and Dump frontend capabilities for debugging commit 421a63335e40d21e2e8c6e120bd38af0e9cd8d17 Author: Florian Lohoff Date: Sat Sep 15 12:10:40 2007 +0200 Fix another stuck card issue Fix another stuck card issue. Sometimes not only closing and reopening the filters is needed but also retuning. commit 292dc295a2c53b1206122fb70ae04cf4da15d63f Author: Florian Lohoff Date: Tue Sep 4 15:05:11 2007 +0200 Fix incrementing read counter. Fix filter bouncing * Fix a flaw in not incrementing the read counter * Stop/Start the demux filter with DMX_STOP/DMX_START ioctl commit 5211636c3ae08ba2c0f4205baa89c0fc2d3b99a3 Author: Florian Lohoff Date: Tue Sep 4 13:14:27 2007 +0200 Clean up more object files commit 46f8b02a1390c6068232d475e480d3dda9ef1614 Author: Florian Lohoff Date: Tue Sep 4 13:13:21 2007 +0200 Fix namespace. Remove PAT special case, add bounce filter * Fixup some namespaces in getstream.h/struct adapter_s * Remove PAT special case where we let the kernel reassemble the PAT section. We have our own PSI section reassembler so let it do its job * Add a function dmx_bounce_filter which should remove all PID filters and thus leave all PIDs and rejoin them which should reset the flexcop. Retuning in the patches sense means - close everything - change transponder, join all pids. From conversation with Patrick Boettcher the reset happens on the transition from forward pid count 0 to 1. commit fe379584b499d0f7058bf68a59f9a0cff27bc639 Author: Florian Lohoff Date: Mon Sep 3 12:39:55 2007 +0200 Add flexcop/SkyStar2 IRQ Stop workaround * Rewrote frontend handling without a thread. The ioctls should not block too long to do it in a poll/select loop. * Added flexcop aka SkyStar2 IRQ Stop workaround. The Kernel workaround is not enough as it only resets the card in case of a retune. As we never retune it will lockup one day. We now retune in case there are no more packets from the dvr device. We might try retuning regularly in case we still get packets but i am unshure on the effect on other cards. commit f15f0a62a75e3ed4990cca90ed427ed0bdbf39a7 Author: Florian Lohoff Date: Thu Dec 4 14:17:47 2008 +0100 Initial import getstream2-20081204/util.c0000644000223500017500000000316011115763523013657 0ustar flomways#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-20081204/tsdecode.c0000644000223500017500000001062211115763523014475 0ustar flomways#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-20081204/stream.c0000644000223500017500000000414611115763523014202 0ustar flomways #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-20081204/socket.h0000644000223500017500000000036511115763523014203 0ustar flomways 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-20081204/socket.c0000644000223500017500000000365711115763523014205 0ustar flomways #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-20081204/simplebuffer.h0000644000223500017500000000042411115763523015372 0ustar flomways 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); uint8_t *sb_bufptr(void *sbv); int sb_buflen(void *sbv); getstream2-20081204/simplebuffer.c0000644000223500017500000000246611115763523015375 0ustar flomways#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; } getstream2-20081204/sap.h0000644000223500017500000000251211115763523013472 0ustar flomways#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 */ char *scope; char *group; int port; char *announcehost; int announceport; char *playgroup; /* 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 addr; /* 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); #endif getstream2-20081204/sap.c0000644000223500017500000001407511115763523013474 0ustar flomways #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; /* Clear Packet */ memset(&sappkt, 0, SAP_MAX_SIZE); sappkt[0]=SAP_VERSION<sid>>8&0xff; /* Unique session ID */ sappkt[3]=sap->sid&0xff; /* * FIXME - This would need to be the originating * not the destination address */ sappkt[4]=sap->addr&0xff; sappkt[5]=sap->addr>>8&0xff; sappkt[6]=sap->addr>>16&0xff; sappkt[7]=sap->addr>>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); sp+=sprintf(sp, "t=0 0\r\n"); sp+=sprintf(sp, "a=tool:getstream\r\n"); sp+=sprintf(sp, "a=type:broadcast\r\n"); 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=1; 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; 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 */ if (sap->scope) { /* Most simple way - We have a scope */ if (strcasecmp(sap->scope, "global") == 0) { maddr=SAP_V4_GLOBAL_ADDRESS; } else if(strcasecmp(sap->scope, "org") == 0) { maddr=SAP_V4_ORG_ADDRESS; } else if(strcasecmp(sap->scope, "local") == 0) { maddr=SAP_V4_LOCAL_ADDRESS; } else { maddr=SAP_V4_LINK_ADDRESS; } port=SAP_PORT; } else if(sap->group) { maddr=sap->group; port=sap->port; } socket_join_multicast(sap->fd, maddr); socket_connect(sap->fd, maddr, port); socket_set_ttl(sap->fd, sap->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++; /* 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-20081204/ringbuffer.c0000644000223500017500000000630511115763523015037 0ustar flomways #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-20081204/psi.h0000644000223500017500000000732511115763523013511 0ustar flomways#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-20081204/psi.c0000644000223500017500000001511511115763523013500 0ustar flomways#include #include #include "getstream.h" #include "psi.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; 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-20081204/pmt.c0000644000223500017500000002616611115763523013515 0ustar flomways#include #include #include #include "getstream.h" #include "psi.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-20081204/pat.c0000644000223500017500000001466711115763523013504 0ustar flomways #include #include #include #include #include "getstream.h" #include "psi.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;pnr<=pat_pnrno(s);pnr++) { pat_add_program(next, pat_pnrfrompat(s, pnr), pat_pidfrompat(s, pnr)); next->progcount++; } } 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; /* 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-20081204/output_udp.c0000644000223500017500000000332711115763523015117 0ustar flomways #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-20081204/output_rtp.c0000644000223500017500000001522111115763523015130 0ustar flomways #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-20081204/output_pipe.c0000644000223500017500000000647311115763523015271 0ustar flomways/* * * 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_MAX_TS (2048/TS_PACKET_SIZE) #define PIPE_INTERVAL 5 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++; 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_MAX_TS, TS_PACKET_SIZE, 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; sb_add_atoms(o->buffer, tsp, 1); if (!sb_free_atoms(o->buffer)) { 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. */ sb_zap(o->buffer); if (len < 0) { if (errno == EPIPE) output_pipe_close(o); return; } /* 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-20081204/output_http.c0000644000223500017500000000606111115763523015304 0ustar flomways #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); } 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-20081204/output.h0000644000223500017500000000521111115763523014246 0ustar flomways#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-20081204/output.c0000644000223500017500000000154211115763523014244 0ustar flomways #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-20081204/logging.c0000644000223500017500000000127111115763523014331 0ustar flomways#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-20081204/libhttp.h0000644000223500017500000000441111115763523014355 0ustar flomways#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-20081204/libhttp.c0000644000223500017500000003032211115763523014350 0ustar flomways #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; } getstream2-20081204/libconf.h0000644000223500017500000000220111115763523014316 0ustar flomways 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-20081204/libconf.c0000644000223500017500000002655211115763523014330 0ustar flomways /* * 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-20081204/input.c0000644000223500017500000000204511115763523014042 0ustar flomways #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-20081204/getstream.h0000644000223500017500000002711611115763523014711 0ustar flomways#ifndef GETSTREAM_H #define GETSTREAM_H #include #include #include #include #include #include #include #include #include "sap.h" #include "psi.h" #if (DVB_API_VERSION==3) && (DVB_API_VERSION_MINOR>=3) #define MULTIPROTO #define AT_DVBS DVBFE_DELSYS_DVBS #define AT_DVBS2 DVBFE_DELSYS_DVBS2 #define AT_DVBT DVBFE_DELSYS_DVBT #define AT_DVBC DVBFE_DELSYS_DVBC #define FE_PARAM struct dvbfe_params #define FE_EVENT struct dvbfe_event #define FE_INFO struct dvbfe_info #define IOCTL_GET_EVENT DVBFE_GET_EVENT #define IOCTL_GET_INFO DVBFE_GET_INFO #define IOCTL_SET_FE DVBFE_SET_PARAMS #define IOCTL_SET_DELSYS DVBFE_SET_DELSYS #define FE_GET_STATUS(a) ((a).fe_events.status) #define DVB_SET_DELIVERY(a, b) (a)->delivery=(b); #define DVBC_SET_SYMBOLRATE(a, b) (a)->delsys.dvbc.symbol_rate=(b) #define DVBC_SET_MODULATION(a, b) (a)->delsys.dvbc.modulation=(b) #define DVBC_SET_FEC(a, b) (a)->delsys.dvbc.fec=(b) /* Multiproto doesnt know about different hierarchy settings - hide it */ #define DVBFE_HIERARCHY_1 DVBFE_HIERARCHY_ON #define DVBFE_HIERARCHY_2 DVBFE_HIERARCHY_ON #define DVBFE_HIERARCHY_4 DVBFE_HIERARCHY_ON #define DVBT_SET_BANDWIDTH(a, b) (a)->delsys.dvbt.bandwidth=(b) #define DVBT_SET_MODULATION(a, b) (a)->delsys.dvbt.constellation=(b) #define DVBT_SET_TMODE(a, b) (a)->delsys.dvbt.transmission_mode=(b) #define DVBT_SET_GUARD(a, b) (a)->delsys.dvbt.guard_interval=(b) #define DVBT_SET_HIERARCHY(a, b) (a)->delsys.dvbt.hierarchy=(b) #define DVBT_SET_CODERATE_HP(a, b) (a)->delsys.dvbt.code_rate_HP=(b) #define DVBT_SET_CODERATE_LP(a, b) (a)->delsys.dvbt.code_rate_LP=(b) #define DVBS_SET_SYMBOLRATE(a, b) (a)->delsys.dvbs.symbol_rate=(b) #define DVBS_SET_FEC(a, b) (a)->delsys.dvbs.fec=(b) #define DVBS2_SET_SYMBOLRATE(a, b) (a)->delsys.dvbs2.symbol_rate=(b) #define DVBS2_SET_FEC(a, b) (a)->delsys.dvbs2.fec=(b) #else enum { AT_DVBS, AT_DVBS2, AT_DVBT, AT_DVBC }; #define FE_PARAM struct dvb_frontend_parameters #define FE_EVENT struct dvb_frontend_event #define FE_INFO struct dvb_frontend_info #define IOCTL_GET_EVENT FE_GET_EVENT #define IOCTL_SET_FE FE_SET_FRONTEND #define IOCTL_GET_INFO FE_GET_INFO #define FE_GET_STATUS(a) ((a).status) #define DVBFE_INVERSION_AUTO INVERSION_AUTO #define DVBFE_MOD_QPSK QPSK #define DVBFE_MOD_QAMAUTO QAM_AUTO #define DVBFE_MOD_QAM16 QAM_16 #define DVBFE_MOD_QAM32 QAM_32 #define DVBFE_MOD_QAM64 QAM_64 #define DVBFE_MOD_QAM128 QAM_128 #define DVBFE_MOD_QAM256 QAM_256 #define DVBFE_FEC_NONE FEC_NONE #define DVBFE_FEC_1_2 FEC_1_2 #define DVBFE_FEC_2_3 FEC_2_3 #define DVBFE_FEC_3_4 FEC_3_4 #define DVBFE_FEC_4_5 FEC_4_5 #define DVBFE_FEC_5_6 FEC_5_6 #define DVBFE_FEC_6_7 FEC_6_7 #define DVBFE_FEC_7_8 FEC_7_8 #define DVBFE_FEC_8_9 FEC_8_9 #define DVBFE_FEC_AUTO FEC_AUTO #define DVB_SET_DELIVERY(a, b) do{ } while(0); #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 DVBFE_BANDWIDTH_AUTO BANDWIDTH_AUTO #define DVBFE_BANDWIDTH_6_MHZ BANDWIDTH_6_MHZ #define DVBFE_BANDWIDTH_7_MHZ BANDWIDTH_7_MHZ #define DVBFE_BANDWIDTH_8_MHZ BANDWIDTH_8_MHZ #define DVBFE_MOD_QAMAUTO QAM_AUTO #define DVBFE_MOD_QAM16 QAM_16 #define DVBFE_MOD_QAM32 QAM_32 #define DVBFE_MOD_QAM64 QAM_64 #define DVBFE_MOD_QAM128 QAM_128 #define DVBFE_MOD_QAM256 QAM_256 #define DVBFE_TRANSMISSION_MODE_AUTO TRANSMISSION_MODE_AUTO #define DVBFE_TRANSMISSION_MODE_2K TRANSMISSION_MODE_2K #define DVBFE_TRANSMISSION_MODE_8K TRANSMISSION_MODE_8K #define DVBFE_GUARD_INTERVAL_AUTO GUARD_INTERVAL_AUTO #define DVBFE_GUARD_INTERVAL_1_4 GUARD_INTERVAL_1_4 #define DVBFE_GUARD_INTERVAL_1_8 GUARD_INTERVAL_1_8 #define DVBFE_GUARD_INTERVAL_1_16 GUARD_INTERVAL_1_16 #define DVBFE_GUARD_INTERVAL_1_32 GUARD_INTERVAL_1_32 #define DVBFE_HIERARCHY_OFF HIERARCHY_NONE #define DVBFE_HIERARCHY_AUTO HIERARCHY_AUTO #define DVBFE_HIERARCHY_1 HIERARCHY_1 #define DVBFE_HIERARCHY_2 HIERARCHY_2 #define DVBFE_HIERARCHY_4 HIERARCHY_4 #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) /* DVB API < 3.3 does not support DVB-S2 */ #define DVBS2_SET_SYMBOLRATE(a, b) do{ }while(0) #define DVBS2_SET_FEC(a, b) do{ }while(0) #endif #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_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 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; FE_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; char *t_pol; 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 { GList *callback; unsigned long packets; struct psisec_s *section; unsigned int secuser; } 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); /* * * crc32.c * */ 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); #define CRC32_LEN 4 #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-20081204/getstream.c0000644000223500017500000000622311115763523014700 0ustar flomways #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; char 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-20081204/fe.c0000644000223500017500000004042311115763523013277 0ustar flomways#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_tune_dvbs(struct adapter_s *adapter) { FE_PARAM feparams; int voltage, tone=SEC_TONE_OFF; memset(&feparams, 0, sizeof(FE_PARAM)); if (strcasecmp(adapter->fe.dvbs.t_pol, "h") == 0) { voltage=SEC_VOLTAGE_18; } else if(strcasecmp(adapter->fe.dvbs.t_pol, "v") == 0) { voltage=SEC_VOLTAGE_13; } else { logwrite(LOG_ERROR, "fe: Unknown polarity \"%s\" in dvb-s transponder config", adapter->fe.dvbs.t_pol); exit(-1); } if (adapter->fe.dvbs.t_freq > 2200000) { 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 { feparams.frequency=adapter->fe.dvbs.t_freq; } feparams.inversion=DVBFE_INVERSION_AUTO; if (adapter->type == AT_DVBS2) { DVB_SET_DELIVERY(&feparams, DVBFE_DELSYS_DVBS2); DVBS2_SET_SYMBOLRATE(&feparams, adapter->fe.dvbs.t_srate); DVBS2_SET_FEC(&feparams, DVBFE_FEC_AUTO); } else { DVB_SET_DELIVERY(&feparams, DVBFE_DELSYS_DVBS); DVBS_SET_SYMBOLRATE(&feparams, adapter->fe.dvbs.t_srate); DVBS_SET_FEC(&feparams, DVBFE_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, IOCTL_SET_FE, &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) { FE_PARAM feparams; memset(&feparams, 0, sizeof(FE_PARAM)); feparams.frequency = adapter->fe.dvbt.freq; feparams.inversion = INVERSION_AUTO; DVB_SET_DELIVERY(&feparams, DVBFE_DELSYS_DVBT); DVBT_SET_CODERATE_HP(&feparams, DVBFE_FEC_AUTO); DVBT_SET_CODERATE_LP(&feparams, DVBFE_FEC_AUTO); switch(adapter->fe.dvbt.bandwidth) { case(0): DVBT_SET_BANDWIDTH(&feparams, DVBFE_BANDWIDTH_AUTO); break; case(6): DVBT_SET_BANDWIDTH(&feparams, DVBFE_BANDWIDTH_6_MHZ); break; case(7): DVBT_SET_BANDWIDTH(&feparams, DVBFE_BANDWIDTH_7_MHZ); break; case(8): DVBT_SET_BANDWIDTH(&feparams, DVBFE_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, DVBFE_MOD_QAMAUTO); break; case(16):DVBT_SET_MODULATION(&feparams, DVBFE_MOD_QAM16); break; case(32):DVBT_SET_MODULATION(&feparams, DVBFE_MOD_QAM32); break; case(64):DVBT_SET_MODULATION(&feparams, DVBFE_MOD_QAM64); break; case(128):DVBT_SET_MODULATION(&feparams, DVBFE_MOD_QAM128); break; case(256):DVBT_SET_MODULATION(&feparams, DVBFE_MOD_QAM256); 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, DVBFE_TRANSMISSION_MODE_AUTO); break; case(2):DVBT_SET_TMODE(&feparams, DVBFE_TRANSMISSION_MODE_2K); break; case(8):DVBT_SET_TMODE(&feparams, DVBFE_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, DVBFE_GUARD_INTERVAL_AUTO); break; case(4):DVBT_SET_GUARD(&feparams, DVBFE_GUARD_INTERVAL_1_4); break; case(8):DVBT_SET_GUARD(&feparams, DVBFE_GUARD_INTERVAL_1_8); break; case(16):DVBT_SET_GUARD(&feparams, DVBFE_GUARD_INTERVAL_1_16); break; case(32):DVBT_SET_GUARD(&feparams, DVBFE_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, DVBFE_HIERARCHY_OFF); break; case(0):DVBT_SET_HIERARCHY(&feparams, DVBFE_HIERARCHY_AUTO); break; case(1):DVBT_SET_HIERARCHY(&feparams, DVBFE_HIERARCHY_1); break; case(2):DVBT_SET_HIERARCHY(&feparams, DVBFE_HIERARCHY_2); break; case(4):DVBT_SET_HIERARCHY(&feparams, DVBFE_HIERARCHY_4); break; default: logwrite(LOG_ERROR, "fe: Unknown DVB-T hierarchy %d", adapter->fe.dvbt.hierarchy); exit(-1); } if (ioctl(adapter->fe.fd, IOCTL_SET_FE, &feparams) < 0) { logwrite(LOG_ERROR, "ioctl FE_SET_FRONTEND failed"); exit(-1); } return 0; } static int fe_tune_dvbc(struct adapter_s *adapter) { FE_PARAM feparams; memset(&feparams, 0, sizeof(FE_PARAM)); feparams.frequency = adapter->fe.dvbc.freq; DVB_SET_DELIVERY(&feparams, DVBFE_DELSYS_DVBC); DVBC_SET_SYMBOLRATE(&feparams, adapter->fe.dvbc.srate); feparams.inversion=INVERSION_AUTO; switch(adapter->fe.dvbc.modulation) { case -1: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QPSK); break; case 0: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAMAUTO); break; case 16: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAM16); break; case 32: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAM32); break; case 64: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAM64); break; case 128: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAM128); break; case 256: DVBC_SET_MODULATION(&feparams, DVBFE_MOD_QAM256); 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, DVBFE_FEC_NONE); break; case 1: DVBC_SET_FEC(&feparams, DVBFE_FEC_1_2); break; case 2: DVBC_SET_FEC(&feparams, DVBFE_FEC_2_3); break; case 3: DVBC_SET_FEC(&feparams, DVBFE_FEC_3_4); break; case 4: DVBC_SET_FEC(&feparams, DVBFE_FEC_4_5); break; case 5: DVBC_SET_FEC(&feparams, DVBFE_FEC_5_6); break; case 6: DVBC_SET_FEC(&feparams, DVBFE_FEC_6_7); break; case 7: DVBC_SET_FEC(&feparams, DVBFE_FEC_7_8); break; case 8: DVBC_SET_FEC(&feparams, DVBFE_FEC_8_9); break; case 9: DVBC_SET_FEC(&feparams, DVBFE_FEC_AUTO); break; default: logwrite(LOG_ERROR, "Unknown fec %d", adapter->fe.dvbc.fec); exit(-1); } if (ioctl(adapter->fe.fd, IOCTL_SET_FE, &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): 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; FE_EVENT event; res=ioctl(adapter->fe.fd, IOCTL_GET_EVENT, &event); if (res < 0 && errno != EOVERFLOW) { logwrite(LOG_ERROR, "fe: Adapter %d Status event overflow %d", adapter->no, errno); return; } status=FE_GET_STATUS(event); 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); } } } } #ifdef MULTIPROTO static void fe_checkcap(struct adapter_s *adapter) { /* With multiproto API changes from 2008-03-09 one has to set the * delsys before anything else */ if (ioctl(adapter->fe.fd, IOCTL_SET_DELSYS, &adapter->type)) { logwrite(LOG_ERROR, "fe: failed to set delivery system with IOCTL_SET_DELSYS with %s", strerror(errno)); exit(-1); } if (ioctl(adapter->fe.fd, IOCTL_GET_INFO, &adapter->fe.feinfo)) { logwrite(LOG_ERROR, "fe: ioctl(DVBFE_GET_INFO...) failed"); exit(-1); } logwrite(LOG_DEBUG, "fe: adapter %d delivery type %d name \"%s\"", adapter->no, adapter->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_step, 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); } #else static void fe_checkcap(struct adapter_s *adapter) { char *type="unknown"; if (ioctl(adapter->fe.fd, IOCTL_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); 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); } #endif 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-20081204/dvr.c0000644000223500017500000001642711115763523013507 0ustar flomways#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", "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++; /* Does somebody want this pid ? */ if (!a->dvr.pidtable[pid].callback) return; /* FIXME This is ugly to have the same list with different users */ if (a->dvr.pidtable[pid].secuser) { int off=0; while(off < TS_PACKET_SIZE) { off=psi_reassemble(a->dvr.pidtable[pid].section, ts, off); if (off<0) break; for(pcbl=g_list_first(a->dvr.pidtable[pid].callback);pcbl!=NULL;pcbl=g_list_next(pcbl)) { struct pidcallback_s *pcb=pcbl->data; if (pcb->type == DVRCB_SECTION) pcb->callback(a->dvr.pidtable[pid].section, pcb->arg); } } } for(pcbl=g_list_first(a->dvr.pidtable[pid].callback);pcbl!=NULL;pcbl=g_list_next(pcbl)) { struct pidcallback_s *pcb=pcbl->data; if (pcb->type == DVRCB_TS) 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]); a->dvr.pidtable[pid].callback=g_list_remove(a->dvr.pidtable[pid].callback, pcb); if (pcb->type == DVRCB_SECTION) { a->dvr.pidtable[pid].secuser--; if (!a->dvr.pidtable[pid].secuser) psi_section_free(a->dvr.pidtable[pid].section); } free(pcb); if (!a->dvr.pidtable[pid].callback) dmx_leave_pid(a, pid); } 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; } if (type == DVRCB_SECTION) { a->dvr.pidtable[pid].secuser++; if (!a->dvr.pidtable[pid].section) a->dvr.pidtable[pid].section=psi_section_new(); } 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); 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", 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-20081204/dmx.c0000644000223500017500000000706611115763523013503 0ustar flomways#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-20081204/crc32.h0000644000223500017500000000020511115763523013620 0ustar flomways 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-20081204/crc32.c0000644000223500017500000006045611115763523013631 0ustar flomways/* * 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-20081204/configs/0000755000223500017500000000000011115763523014166 5ustar flomwaysgetstream2-20081204/configs/config-multiadapter0000644000223500017500000000125011115763523020045 0ustar flomways 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-20081204/configs/config-dvbt-germany-owl0000644000223500017500000000147311115763523020557 0ustar flomways 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-20081204/configs/config-astra-transponder-930000644000223500017500000001572511115763523021266 0ustar flomways 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-20081204/configs/config-astra-transponder-710000644000223500017500000000266011115763523021254 0ustar flomways 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-20081204/config.h0000644000223500017500000000023211115763523014151 0ustar flomways #include #include "getstream.h" struct config_s { GList *adapter; int http_port; }; struct config_s *readconfig(char *filename); getstream2-20081204/config.c0000644000223500017500000003672211115763523014161 0ustar flomways #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) { sap->scope=val->string; 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_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_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->ttl=15; 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 }, { "playgroup", 0, 1, LCV_STRING, 0, NULL, cf_sap_playgroup }, { 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_freq(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.t_freq=val->num; return 1; } static int cf_dvbs_trans_pol(struct lc_centry *ce, struct lc_value *val) { adapter->fe.dvbs.t_pol=val->string; 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", 1, 1, LCV_NUM, 0, NULL, cf_dvbs_lnb_lof2 }, { "slof", 1, 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-20081204/README.multiproto0000644000223500017500000000100311115763523015625 0ustar flomways 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-20081204/README0000644000223500017500000001015711115763523013422 0ustar flomways 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 Now Block converter) on your Dish: 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. 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; }; getstream2-20081204/Makefile0000644000223500017500000000127411115763523014202 0ustar flomwaysCC=gcc CFLAGS=-O0 -g -Wall -I. -I/usr/include/glib-2.0/ -I/usr/lib/glib-2.0/include/ 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-20081204/.gitignore0000644000223500017500000000011011115763523014516 0ustar flomways.* *.o *.o.* *.a *.s core* *.orig *.rej tsdecode getstream !.gitignore