pax_global_header00006660000000000000000000000064125770070400014514gustar00rootroot0000000000000052 comment=7c6ff3035231f84884d4d92cf122ff504cd9f160 vdr-plugin-vnsiserver-1.3.1/000077500000000000000000000000001257700704000160115ustar00rootroot00000000000000vdr-plugin-vnsiserver-1.3.1/.gitignore000066400000000000000000000001421257700704000177760ustar00rootroot00000000000000.dependencies *.o *.so *~ po/*.pot po/*.mo .kdev4/ # Don't add patch files *.diff *.patch *.orig vdr-plugin-vnsiserver-1.3.1/COPYING000066400000000000000000000432541257700704000170540ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. vdr-plugin-vnsiserver-1.3.1/HISTORY000066400000000000000000000037131257700704000171010ustar00rootroot00000000000000VDR Plugin 'vnsiserver' Revision History ---------------------------------------- 2015-09-18: Version 1.3.0 - add support for undelete recordings - add support for rds data if present on dvb radio and send to addon if supported there - Update support for wirbelscan plugin - Rework Licence files to use KODI - Improved README - Fix possible seq fault in void cRecPlayer::reScan() - fix picons for western - make avoid EPG scan while streaming default - see github history for other fixes 2015-01-25: Version 1.2.1 - add cmd line switch for setting tcp port - fix potential segfault on stop streaming - stop triggering epg updates if client does not fetch epg 2014-09-03: Version 1.2.0 - add cmd line switch "-d". if set, vnsi creates a dummy dvb device which makes femon work on all channels - add setup parameter AvoidEPGScan. if set to 1, vnsi disables EPG scan while streaming. EPG scan during streaming can lead to artifacs on some dual tuner cards. - fix div by zero on AAC audio, thanks to Jakob Sloth Jepsen 2014-03-20: Version 1.1.0 - add support for picons - add setup paramter to select if current recording should start when tuning to a channel - only activate AAC mux workaround if environment variable VNSI_AAC_MUXMODE is set - bump max timshift buffer RAM to 8 gig 2014-03-20: Version 1.0.0 - release 1.0 - various fixes - remove suffix from plugin name vnsiserver5 -> vnsiserver 2014-01-08: Version 0.9.4 - update length of recorings in progress while playing 2013-12-04: Version 0.9.3 - add support for EDL (marks) - add channel filter - send buffer times for timeshift - bump protocol to XBMC to 5 - suffix plugin with version of protocol: vnsiserver5 - this version is compatible with XBMC 13 2013-02-03: Version 0.9.1 - most parts of the plugin rewritten - revised parsers - proper handling of PMT changes - suffix plugin with version of protocol: vnsiserver3 - this version is compatible with XBMC 12.0 2010-03-23: Version 0.0.1 - Initial revision. vdr-plugin-vnsiserver-1.3.1/Makefile000066400000000000000000000103301257700704000174460ustar00rootroot00000000000000# # Makefile for a Video Disk Recorder plugin # # $Id$ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. # By default the main source file also carries this name. PLUGIN = vnsiserver ### The version number of this plugin (taken from the main source file): VERSION = $(shell grep 'static const char \*VERSION *=' vnsi.h | awk '{ print $$6 }' | sed -e 's/[";]//g') ### The directory environment: # Use package data if installed...otherwise assume we're under the VDR source directory: PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) LIBDIR ?= $(call PKGCFG,libdir) LOCDIR = $(call PKGCFG,locdir) PLGCFG = $(call PKGCFG,plgcfg) # TMPDIR ?= /tmp ### The compiler options: export CFLAGS = $(call PKGCFG,cflags) export CXXFLAGS = $(call PKGCFG,cxxflags) ### The version number of VDR's plugin API: APIVERSION = $(call PKGCFG,apiversion) ifeq ($(strip $(APIVERSION)),) APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') NOCONFIG := 1 endif # backwards compatibility version < 1.7.34 API1733 := $(shell if [ "$(APIVERSION)" \< "1.7.34" ]; then echo true; fi; ) ifdef API1733 VDRSRC = $(VDRDIR) VDRSRC ?= ../../.. ifeq ($(strip $(LIBDIR)),) LIBDIR = $(VDRSRC)/PLUGINS/lib endif ifndef NOCONFIG CXXFLAGS = $(call PKGCFG,cflags) CXXFLAGS += -fPIC else -include $(VDRSRC)/Make.global -include $(VDRSRC)/Make.config endif export CXXFLAGS else ### Allow user defined options to overwrite defaults: -include $(PLGCFG) endif ### The name of the distribution archive: ARCHIVE = $(PLUGIN)-$(VERSION) PACKAGE = vdr-$(ARCHIVE) ### The name of the shared object file: SOFILE = libvdr-$(PLUGIN).so ### Includes and Defines (add further entries here): ifdef API1733 INCLUDES += -I$(VDRSRC)/include endif DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DVNSI_SERVER_VERSION='"$(VERSION)"' ifeq ($(DEBUG),1) DEFINES += -DDEBUG endif ### The object files (add further files here): OBJS = vnsi.o bitstream.o vnsiclient.o channelscancontrol.o config.o cxsocket.o parser.o parser_AAC.o \ parser_AC3.o parser_DTS.o parser_h264.o parser_MPEGAudio.o parser_MPEGVideo.o \ parser_Subtitle.o parser_Teletext.o streamer.o recplayer.o requestpacket.o responsepacket.o \ vnsiserver.o hash.o recordingscache.o setup.o vnsiosd.o demuxer.o videobuffer.o \ videoinput.o channelfilter.o status.o ### The main target: ifdef API1733 all: install-lib else all: $(SOFILE) endif # ### Implicit rules: %.o: %.c $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< ### Dependencies: MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ -include $(DEPFILE) ### Internationalization (I18N): PODIR = po I18Npo = $(wildcard $(PODIR)/*.po) I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) I18Npot = $(PODIR)/$(PLUGIN).pot %.mo: %.po msgfmt -c -o $@ $< $(I18Npot): $(wildcard *.c) xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='' -o $@ `ls $^` %.po: $(I18Npot) msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< @touch $@ $(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo install -D -m644 $< $@ .PHONY: i18n i18n: $(I18Nmo) $(I18Npot) install-i18n: $(I18Nmsgs) ### Targets: $(SOFILE): $(OBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ install-lib: $(SOFILE) install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) install: install-lib install-i18n dist: $(I18Npo) clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz clean: @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ compile: $(SOFILE) vdr-plugin-vnsiserver-1.3.1/README000066400000000000000000000031701257700704000166720ustar00rootroot00000000000000 ----------------------------------------------------------------------------- 1. Introduction ----------------------------------------------------------------------------- This is a "plugin" for the Video Disk Recorder (VDR). VDR plugin to handle KODI clients. The vdr-plugin-vnsiserver is able to handle serveral KODI clients connecting via the VNSI addon. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING for more information. Latest version available at: https://github.com/FernetMenta/vdr-plugin-vnsiserver ----------------------------------------------------------------------------- 2. Compile in VDR's source tree ----------------------------------------------------------------------------- Get VDR sources. $ cd /PLUGINS/src $ git clone https://github.com/FernetMenta/vdr-plugin-vnsiserver $ ln -s vdr-plugin-vnsiserver vnsiserver On newer VDR Versions (2.2.0) plugin can be build and installed alone, use: $ git clone https://github.com/FernetMenta/vdr-plugin-vnsiserver $ cd vdr-plugin-vnsiserver $ make $ sudo make install If you want to fix install dir you can use this, where the end define the VDR library folder: $ sudo make install LIBDIR=/usr/lib/vdr ----------------------------------------------------------------------------- 3. Help and Suppport ----------------------------------------------------------------------------- http://forum.kodi.tv/forumdisplay.php?fid=169 vdr-plugin-vnsiserver-1.3.1/bitstream.c000066400000000000000000000050561257700704000201550ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "bitstream.h" cBitstream::cBitstream(uint8_t *data, int bits) { m_data = data; m_offset = 0; m_len = bits; m_error = false; } void cBitstream::setBitstream(uint8_t *data, int bits) { m_data = data; m_offset = 0; m_len = bits; m_error = false; } void cBitstream::skipBits(int num) { m_offset += num; } unsigned int cBitstream::readBits(int num) { int r = 0; while(num > 0) { if(m_offset >= m_len) { m_error = true; return 0; } num--; if(m_data[m_offset / 8] & (1 << (7 - (m_offset & 7)))) r |= 1 << num; m_offset++; } return r; } unsigned int cBitstream::showBits(int num) { int r = 0; int offs = m_offset; while(num > 0) { if(offs >= m_len) { m_error = true; return 0; } num--; if(m_data[offs / 8] & (1 << (7 - (offs & 7)))) r |= 1 << num; offs++; } return r; } unsigned int cBitstream::readGolombUE(int maxbits) { int lzb = -1; int bits = 0; for(int b = 0; !b; lzb++, bits++) { if (bits > maxbits) return 0; b = readBits1(); } return (1 << lzb) - 1 + readBits(lzb); } signed int cBitstream::readGolombSE() { int v, pos; v = readGolombUE(); if(v == 0) return 0; pos = (v & 1); v = (v + 1) >> 1; return pos ? v : -v; } unsigned int cBitstream::remainingBits() { return m_len - m_offset; } void cBitstream::putBits(int val, int num) { while(num > 0) { if(m_offset >= m_len) { m_error = true; return; } num--; if(val & (1 << num)) m_data[m_offset / 8] |= 1 << (7 - (m_offset & 7)); else m_data[m_offset / 8] &= ~(1 << (7 - (m_offset & 7))); m_offset++; } } vdr-plugin-vnsiserver-1.3.1/bitstream.h000066400000000000000000000027641257700704000201650ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_BITSTREAM_H #define VNSI_BITSTREAM_H class cBitstream { private: uint8_t *m_data; int m_offset; int m_len; bool m_error; public: cBitstream(uint8_t *data, int bits); void setBitstream(uint8_t *data, int bits); void skipBits(int num); unsigned int readBits(int num); unsigned int showBits(int num); unsigned int readBits1() { return readBits(1); } unsigned int readGolombUE(int maxbits = 32); signed int readGolombSE(); unsigned int remainingBits(); void putBits(int val, int num); int length() { return m_len; } bool isError() { return m_error; } }; #endif // VNSI_BITSTREAM_H vdr-plugin-vnsiserver-1.3.1/channelfilter.c000066400000000000000000000173161257700704000210030ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2013 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "channelfilter.h" #include "config.h" #include "hash.h" #include #include #include #include #include cVNSIProvider::cVNSIProvider() :m_name(""), m_caid(0) { } cVNSIProvider::cVNSIProvider(std::string name, int caid) :m_name(name), m_caid(caid) { }; bool cVNSIProvider::operator==(const cVNSIProvider &rhs) { if (rhs.m_caid != m_caid) return false; if (rhs.m_name.compare(m_name) != 0) return false; return true; } bool cVNSIChannelFilter::IsRadio(const cChannel* channel) { bool isRadio = false; // assume channels without VPID & APID are video channels if (channel->Vpid() == 0 && channel->Apid(0) == 0) isRadio = false; // channels without VPID are radio channels (channels with VPID 1 are encrypted radio channels) else if (channel->Vpid() == 0 || channel->Vpid() == 1 || channel->Rid() == 1) isRadio = true; return isRadio; } void cVNSIChannelFilter::Load() { cMutexLock lock(&m_Mutex); cString filename; std::string line; std::ifstream rfile; cVNSIProvider provider; std::vector::iterator p_it; filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory); m_providersVideo.clear(); rfile.open(filename); if (rfile.is_open()) { while(std::getline(rfile,line)) { size_t pos = line.find("|"); if(pos == line.npos) { provider.m_name = line; provider.m_caid = 0; } else { provider.m_name = line.substr(0, pos); std::string tmp = line.substr(pos+1); char *pend; provider.m_caid = strtol(tmp.c_str(), &pend, 10); } p_it = std::find(m_providersVideo.begin(), m_providersVideo.end(), provider); if(p_it == m_providersVideo.end()) { m_providersVideo.push_back(provider); } } rfile.close(); } filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory); rfile.open(filename); m_providersRadio.clear(); if (rfile.is_open()) { while(std::getline(rfile,line)) { unsigned int pos = line.find("|"); if(pos == line.npos) { provider.m_name = line; provider.m_caid = 0; } else { provider.m_name = line.substr(0, pos); std::string tmp = line.substr(pos+1); char *pend; provider.m_caid = strtol(tmp.c_str(), &pend, 10); } p_it = std::find(m_providersRadio.begin(), m_providersRadio.end(), provider); if(p_it == m_providersRadio.end()) { m_providersRadio.push_back(provider); } } rfile.close(); } filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory); rfile.open(filename); m_channelsVideo.clear(); if (rfile.is_open()) { while(getline(rfile,line)) { char *pend; int id = strtol(line.c_str(), &pend, 10); m_channelsVideo.push_back(id); } rfile.close(); } filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory); rfile.open(filename); m_channelsRadio.clear(); if (rfile.is_open()) { while(getline(rfile,line)) { char *pend; int id = strtol(line.c_str(), &pend, 10); m_channelsRadio.push_back(id); } rfile.close(); } } void cVNSIChannelFilter::StoreWhitelist(bool radio) { cMutexLock lock(&m_Mutex); cString filename; std::ofstream wfile; cVNSIProvider provider; std::vector::iterator p_it; std::vector *whitelist; if (radio) { filename = cString::sprintf("%s/radiowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory); whitelist = &m_providersRadio; } else { filename = cString::sprintf("%s/videowhitelist.vnsi", *VNSIServerConfig.ConfigDirectory); whitelist = &m_providersVideo; } wfile.open(filename); if(wfile.is_open()) { std::string tmp; char buf[16]; for(p_it=whitelist->begin(); p_it!=whitelist->end(); ++p_it) { tmp = p_it->m_name; tmp += "|"; sprintf(buf, "%d\n", p_it->m_caid); tmp += buf; wfile << tmp; } wfile.close(); } SortChannels(); } void cVNSIChannelFilter::StoreBlacklist(bool radio) { cMutexLock lock(&m_Mutex); cString filename; std::ofstream wfile; cVNSIProvider provider; std::vector::iterator it; std::vector *blacklist; if (radio) { filename = cString::sprintf("%s/radioblacklist.vnsi", *VNSIServerConfig.ConfigDirectory); blacklist = &m_channelsRadio; } else { filename = cString::sprintf("%s/videoblacklist.vnsi", *VNSIServerConfig.ConfigDirectory); blacklist = &m_channelsVideo; } wfile.open(filename); if(wfile.is_open()) { std::string tmp; char buf[16]; for(it=blacklist->begin(); it!=blacklist->end(); ++it) { sprintf(buf, "%d\n", *it); tmp = buf; wfile << tmp; } wfile.close(); } SortChannels(); } bool cVNSIChannelFilter::IsWhitelist(const cChannel &channel) { cVNSIProvider provider; std::vector::iterator p_it; std::vector *providers; provider.m_name = channel.Provider(); if (IsRadio(&channel)) providers = &m_providersRadio; else providers = &m_providersVideo; if(providers->empty()) return true; if (channel.Ca(0) == 0) { provider.m_caid = 0; p_it = std::find(providers->begin(), providers->end(), provider); if(p_it!=providers->end()) return true; else return false; } int caid; int idx = 0; while((caid = channel.Ca(idx)) != 0) { provider.m_caid = caid; p_it = std::find(providers->begin(), providers->end(), provider); if(p_it!=providers->end()) return true; idx++; } return false; } bool cVNSIChannelFilter::PassFilter(const cChannel &channel) { cMutexLock lock(&m_Mutex); if(channel.GroupSep()) return true; if (!IsWhitelist(channel)) return false; std::vector::iterator it; if (IsRadio(&channel)) { it = std::find(m_channelsRadio.begin(), m_channelsRadio.end(), CreateChannelUID(&channel)); if(it!=m_channelsRadio.end()) return false; } else { it = std::find(m_channelsVideo.begin(), m_channelsVideo.end(), CreateChannelUID(&channel)); if(it!=m_channelsVideo.end()) return false; } return true; } void cVNSIChannelFilter::SortChannels() { Channels.IncBeingEdited(); Channels.Lock(true); for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { if(!PassFilter(*channel)) { for (cChannel *whitechan = Channels.Next(channel); whitechan; whitechan = Channels.Next(whitechan)) { if(PassFilter(*whitechan)) { Channels.Move(whitechan, channel); channel = whitechan; break; } } } } Channels.SetModified(true); Channels.Unlock(); Channels.DecBeingEdited(); } cVNSIChannelFilter VNSIChannelFilter; vdr-plugin-vnsiserver-1.3.1/channelfilter.h000066400000000000000000000031321257700704000207770ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2013 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #pragma once #include #include #include #include class cVNSIProvider { public: cVNSIProvider(); cVNSIProvider(std::string name, int caid); bool operator==(const cVNSIProvider &rhs); std::string m_name; int m_caid; }; class cVNSIChannelFilter { public: void Load(); void StoreWhitelist(bool radio); void StoreBlacklist(bool radio); bool IsWhitelist(const cChannel &channel); bool PassFilter(const cChannel &channel); void SortChannels(); static bool IsRadio(const cChannel* channel); std::vector m_providersVideo; std::vector m_providersRadio; std::vector m_channelsVideo; std::vector m_channelsRadio; cMutex m_Mutex; }; extern cVNSIChannelFilter VNSIChannelFilter; vdr-plugin-vnsiserver-1.3.1/channelscancontrol.c000066400000000000000000000226311257700704000220370ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010,2015 Alwin Esch (Team KODI) * Copyright (C) 2010, 2011 Alexander Pipelka * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "channelscancontrol.h" #include "vnsiclient.h" using namespace WIRBELSCAN_SERVICE; /*! * versions definitions */ #define MIN_CMDVERSION 1 // command api 0001 #define MIN_STATUSVERSION 1 // query status #define MIN_SETUPVERSION 1 // get/put setup, GetSetup#XXXX/SetSetup#XXXX #define MIN_COUNTRYVERSION 1 // get list of country IDs and Names #define MIN_SATVERSION 1 // get list of sat IDs and Names #define MIN_USERVERSION 1 // scan user defined transponder #define SCAN_TV ( 1 << 0 ) #define SCAN_RADIO ( 1 << 1 ) #define SCAN_FTA ( 1 << 2 ) #define SCAN_SCRAMBLED ( 1 << 3 ) #define SCAN_HD ( 1 << 4 ) /*! * macro definitions */ #define SETSCAN 0 #define SCANNING 1 #define SCANDONE 2 #define CHECKVERSION(a,b,c) p=strchr((char *) m_scanInformation->a,'#') + 1; sscanf(p,"%d ",&version); if (version < b) c = true; #define CHECKLIMITS(a,v,_min,_max,_def) a=v; if ((a<_min) || (a>_max)) a=_def; #define freeAndNull(p) if(p) { free(p); p=NULL; } #define deleteAndNull(p) if(p) { delete(p); p=NULL; } CScanControl::CScanControl(cVNSIClient *client) : m_client(client), m_isChecked(false), m_isNotSupported(true), m_channelScanPlugin(NULL), m_singleScan(0), m_cbuf(NULL), m_sbuf(NULL), m_scanInformation(NULL) { } CScanControl::~CScanControl() { if (m_scanInformation) delete m_scanInformation; freeAndNull(m_cbuf); freeAndNull(m_sbuf); } bool CScanControl::IsSupported() { if (!m_isChecked) LoadScanner(); return !m_isNotSupported; } bool CScanControl::LoadScanner() { /*! * Plugin present? */ m_channelScanPlugin = cPluginManager::GetPlugin("wirbelscan"); if (m_channelScanPlugin == NULL) return false; /*! * This plugin version compatibel to wirbelscan? */ char *p; m_scanInformation = new cWirbelscanInfo; if (asprintf(&p, "%s%s", SPlugin, SInfo) < 0 || !m_channelScanPlugin->Service("wirbelscan_GetVersion", m_scanInformation)) { m_client->OsdStatusMessage(*cString::sprintf("%s", tr("Your scanner version doesnt support services - Please upgrade."))); free(p); return false; } free(p); int version = 0; m_isNotSupported = false; CHECKVERSION(CommandVersion,MIN_CMDVERSION, m_isNotSupported); CHECKVERSION(StatusVersion, MIN_STATUSVERSION, m_isNotSupported); CHECKVERSION(SetupVersion, MIN_SETUPVERSION, m_isNotSupported); CHECKVERSION(CountryVersion,MIN_COUNTRYVERSION, m_isNotSupported); CHECKVERSION(SatVersion, MIN_SATVERSION, m_isNotSupported); CHECKVERSION(UserVersion, MIN_USERVERSION, m_isNotSupported); if (m_isNotSupported) { m_client->OsdStatusMessage(*cString::sprintf("%s", tr("Your scanner version is to old - Please upgrade." ))); return false; } /*! * Check which receivers are present */ int cardnr = 0; cDevice *device; memset(&m_receiverSystems, 0, sizeof(m_receiverSystems)); while ((device = cDevice::GetDevice(cardnr++))) { if (device->ProvidesSource(cSource::stTerr)) m_receiverSystems[RECEIVER_SYSTEM_DVB_T] = true; if (device->ProvidesSource(cSource::stCable)) m_receiverSystems[RECEIVER_SYSTEM_DVB_C] = true; if (device->ProvidesSource(cSource::stSat)) m_receiverSystems[RECEIVER_SYSTEM_DVB_S] = true; if (device->ProvidesSource(cSource::stAtsc)) m_receiverSystems[RECEIVER_SYSTEM_ATSC] = true; } if (cPluginManager::GetPlugin("pvrinput")) { m_receiverSystems[RECEIVER_SYSTEM_ANALOG_TV] = true; m_receiverSystems[RECEIVER_SYSTEM_ANALOG_RADIO] = true; } m_countryBuffer.size = 0; m_countryBuffer.count = 0; m_countryBuffer.buffer = NULL; if (asprintf(&p, "%sGet%s", SPlugin, SCountry) < 0) return false; m_channelScanPlugin->Service(p, &m_countryBuffer); // query buffer size. m_cbuf = (SListItem*) malloc(m_countryBuffer.size * sizeof(SListItem)); // now, allocate memory. m_countryBuffer.buffer = m_cbuf; // assign buffer m_channelScanPlugin->Service(p, &m_countryBuffer); // fill buffer with values. free(p); m_satBuffer.size = 0; m_satBuffer.count = 0; m_satBuffer.buffer = NULL; if (asprintf(&p, "%sGet%s", SPlugin, SSat) < 0) return false; m_channelScanPlugin->Service(p, &m_satBuffer); // query buffer size. m_sbuf = (SListItem*) malloc(m_satBuffer.size * sizeof(SListItem)); // now, allocate memory. m_satBuffer.buffer = m_sbuf; // assign buffer m_channelScanPlugin->Service(p, &m_satBuffer); // fill buffer with values. free(p); if (asprintf(&p, "%sGet%s", SPlugin, SSetup) < 0) return false; m_channelScanPlugin->Service(p, &m_setup); free(p); return true; } bool CScanControl::StartScan(sScanServiceData &data) { if (m_isNotSupported) return false; m_finishReported = false; CHECKLIMITS(m_setup.SatId , data.SatIndex , 0 , 0xFFFF, 0); CHECKLIMITS(m_setup.CountryId , data.CountryIndex , 0 , 0xFFFF, 0); CHECKLIMITS(m_setup.DVB_Type , data.type , 0 , 5 , 0); CHECKLIMITS(m_setup.DVBT_Inversion , data.DVBT_Inversion , 0 , 1 , 0); CHECKLIMITS(m_setup.DVBC_Inversion , data.DVBC_Inversion , 0 , 1 , 0); CHECKLIMITS(m_setup.DVBC_Symbolrate , data.DVBC_Symbolrate , 0 , 16 , 0); CHECKLIMITS(m_setup.DVBC_QAM , data.DVBC_QAM , 0 , 4 , 0); CHECKLIMITS(m_setup.ATSC_type , data.ATSC_Type , 0 , 1 , 0); m_setup.scanflags = data.scan_tv ? SCAN_TV : 0; m_setup.scanflags |= data.scan_radio ? SCAN_RADIO : 0; m_setup.scanflags |= data.scan_scrambled ? SCAN_SCRAMBLED : 0; m_setup.scanflags |= data.scan_fta ? SCAN_FTA : 0; m_setup.scanflags |= data.scan_hd ? SCAN_HD : 0; m_lastChannelCount = Channels.Count(); char *s; if (asprintf(&s, "%sSet%s", SPlugin, SSetup) < 0) return false; m_channelScanPlugin->Service(s, &m_setup); free(s); PutCommand(CmdStartScan); Start();// start polling thread return true; } bool CScanControl::StopScan() { if (m_isNotSupported) return false; PutCommand(CmdStopScan); cCondWait::SleepMs(500); Cancel(2); if (!m_finishReported) { if (m_scanStatus.status != StatusStopped) { m_scanStatus.status = StatusStopped; m_client->processSCAN_SetStatus(m_scanStatus.status); INFOLOG("Stopping scan plugin not reported back and stop maybe not complete confirmed"); } m_client->processSCAN_IsFinished(); } return true; } bool CScanControl::GetCountries(scannerEntryList &list) { for (uint32_t i = 0; i < m_countryBuffer.count; i++) { scannerEntry entry; entry.index = m_countryBuffer.buffer[i].id; entry.name = m_countryBuffer.buffer[i].short_name; entry.longName = m_countryBuffer.buffer[i].full_name; list.push_back(entry); } return !list.empty(); } bool CScanControl::GetSatellites(scannerEntryList &list) { for (uint32_t i = 0; i < m_satBuffer.count; i++) { scannerEntry entry; entry.index = m_satBuffer.buffer[i].id; entry.name = m_satBuffer.buffer[i].short_name; entry.longName = m_satBuffer.buffer[i].full_name; list.push_back(entry); } return !list.empty(); } void CScanControl::PutCommand(WIRBELSCAN_SERVICE::s_cmd command) { cWirbelscanCmd cmd; char * request; if (asprintf(&request, "%s%s", SPlugin, SCommand) < 0) return; cmd.cmd = command; m_channelScanPlugin->Service(request, &cmd); free(request); } void CScanControl::Action(void) { while (Running()) { cCondWait::SleepMs(1000); char * s; if (asprintf(&s, "%sGet%s", SPlugin, SStatus) < 0) return; m_channelScanPlugin->Service(s, &m_scanStatus); free(s); m_client->processSCAN_SetStatus(m_scanStatus.status); m_client->processSCAN_SetPercentage(m_scanStatus.progress); m_client->processSCAN_SetSignalStrength(m_scanStatus.strength, false); m_client->processSCAN_SetDeviceInfo(m_scanStatus.curr_device); m_client->processSCAN_SetTransponder(m_scanStatus.transponder); for (int i = 0; i < Channels.Count()-m_lastChannelCount; i++) { cChannel *channel = Channels.GetByNumber(Channels.Count()-i); m_client->processSCAN_NewChannel(channel->Name(), channel->Vpid() == 0, channel->Ca() > 0, channel->Vtype() > 2); } m_lastChannelCount = Channels.Count(); if (m_scanStatus.status == StatusStopped) { m_client->processSCAN_IsFinished(); m_finishReported = true; break; } } Cancel(0); } vdr-plugin-vnsiserver-1.3.1/channelscancontrol.h000066400000000000000000000067311257700704000220470ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010,2015 Alwin Esch (Team KODI) * Copyright (C) 2010, 2011 Alexander Pipelka * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef __CHANNELSCAN_CONTROL__ #define __CHANNELSCAN_CONTROL__ #include #include #include "wirbelscan_services.h" #define RECEIVER_SYSTEM_DVB_T 0 #define RECEIVER_SYSTEM_DVB_C 1 #define RECEIVER_SYSTEM_DVB_S 2 #define RECEIVER_SYSTEM_ATSC 3 #define RECEIVER_SYSTEM_ANALOG_TV 4 #define RECEIVER_SYSTEM_ANALOG_RADIO 5 typedef enum scantype { DVB_TERR = 0, DVB_CABLE = 1, DVB_SAT = 2, PVRINPUT = 3, PVRINPUT_FM = 4, DVB_ATSC = 5, } scantype_t; struct sScanServiceData { int type; bool scan_tv; bool scan_radio; bool scan_fta; bool scan_scrambled; bool scan_hd; int CountryIndex; int DVBC_Inversion; int DVBC_Symbolrate; int DVBC_QAM; int DVBT_Inversion; int SatIndex; int ATSC_Type; }; struct scannerEntry { int index; const char *name; const char *longName; }; typedef std::vector scannerEntryList; class cVNSIClient; class CScanControl : public cThread { public: CScanControl(cVNSIClient *client); ~CScanControl(); bool IsSupported(); bool StartScan(sScanServiceData &data); bool StopScan(); bool GetCountries(scannerEntryList &list); bool GetSatellites(scannerEntryList &list); bool SupportsDVB_T() { return m_receiverSystems[RECEIVER_SYSTEM_DVB_T]; } bool SupportsDVB_C() { return m_receiverSystems[RECEIVER_SYSTEM_DVB_C]; } bool SupportsDVB_S() { return m_receiverSystems[RECEIVER_SYSTEM_DVB_S]; } bool SupportsAnalogTV() { return m_receiverSystems[RECEIVER_SYSTEM_ANALOG_TV]; } bool SupportsAnalogRadio() { return m_receiverSystems[RECEIVER_SYSTEM_ANALOG_RADIO]; } bool SupportsATSC() { return m_receiverSystems[RECEIVER_SYSTEM_ATSC]; } virtual void Action(); private: bool LoadScanner(); void PutCommand(WIRBELSCAN_SERVICE::s_cmd command); cVNSIClient *m_client; bool m_isChecked; bool m_isNotSupported; bool m_isNotTpSupported; bool m_receiverSystems[6]; cPlugin *m_channelScanPlugin; bool m_singleScan; bool m_finishReported; WIRBELSCAN_SERVICE::SListItem *m_cbuf; WIRBELSCAN_SERVICE::SListItem *m_sbuf; WIRBELSCAN_SERVICE::cPreAllocBuffer m_countryBuffer; WIRBELSCAN_SERVICE::cPreAllocBuffer m_satBuffer; WIRBELSCAN_SERVICE::cWirbelscanInfo *m_scanInformation; WIRBELSCAN_SERVICE::cWirbelscanStatus m_scanStatus; WIRBELSCAN_SERVICE::cWirbelscanScanSetup m_setup; int m_lastChannelCount; }; #endif // __CHANNELSCAN_CONTROL__ vdr-plugin-vnsiserver-1.3.1/config.c000066400000000000000000000024071257700704000174250ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include #include "config.h" cVNSIServerConfig::cVNSIServerConfig() { listen_port = LISTEN_PORT; ConfigDirectory = NULL; stream_timeout = 10; device = false; pDevice = NULL; testStreamActive = false; } /* Global instance */ cVNSIServerConfig VNSIServerConfig; vdr-plugin-vnsiserver-1.3.1/config.h000066400000000000000000000046721257700704000174400ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_CONFIG_H #define VNSI_CONFIG_H #include #include #include // log output configuration #ifdef CONSOLEDEBUG #define DEBUGLOG(x...) printf("VNSI: "x) #elif defined DEBUG #define DEBUGLOG(x...) dsyslog("VNSI: "x) #else #define DEBUGLOG(x...) #endif #ifdef CONSOLEDEBUG #define INFOLOG(x...) printf("VNSI: "x) #define ERRORLOG(x...) printf("VNSI-Error: "x) #else #define INFOLOG(x...) isyslog("VNSI: "x) #define ERRORLOG(x...) esyslog("VNSI-Error: "x) #endif // default settings #define ALLOWED_HOSTS_FILE "allowed_hosts.conf" #define FRONTEND_DEVICE "/dev/dvb/adapter%d/frontend%d" #define LISTEN_PORT 34890 #define LISTEN_PORT_S "34890" #define DISCOVERY_PORT 34890 // backward compatibility #if APIVERSNUM < 10701 #define FOLDERDELIMCHAR '~' #endif // Error flags #define ERROR_PES_GENERAL 0x01 #define ERROR_PES_SCRAMBLE 0x02 #define ERROR_PES_STARTCODE 0x04 #define ERROR_DEMUX_NODATA 0x10 class cVNSIServerConfig { public: cVNSIServerConfig(); // Remote server settings cString ConfigDirectory; // config directory path uint16_t listen_port; // Port of remote server uint16_t stream_timeout; // timeout in seconds for stream data bool device; // true if vnsi should act as dummy device void *pDevice; // pointer to cDvbVnsiDevice cString testStreamFile; // TS file to simulate channel bool testStreamActive; // true if test mode is enabled }; // Global instance extern cVNSIServerConfig VNSIServerConfig; #endif // VNSI_CONFIG_H vdr-plugin-vnsiserver-1.3.1/cxsocket.c000066400000000000000000000102461257700704000200030ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2003-2006 Petri Hintukainen * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ /* * Socket wrapper classes * * Code is taken from xineliboutput plugin. * */ #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "cxsocket.h" #ifndef MSG_MORE #define MSG_MORE 0 #endif cxSocket::~cxSocket() { close(); delete m_pollerRead; delete m_pollerWrite; } void cxSocket::close() { if(m_fd >= 0) { ::close(m_fd); m_fd=-1; } } void cxSocket::Shutdown() { if(m_fd >= 0) { ::shutdown(m_fd, SHUT_RD); } } void cxSocket::LockWrite() { m_MutexWrite.Lock(); } void cxSocket::UnlockWrite() { m_MutexWrite.Unlock(); } ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms, bool more_data) { cMutexLock CmdLock(&m_MutexWrite); if(m_fd == -1) return -1; ssize_t written = (ssize_t)size; const unsigned char *ptr = (const unsigned char *)buffer; while (size > 0) { if(!m_pollerWrite->Poll(timeout_ms)) { ERRORLOG("cxSocket::write: poll() failed"); return written-size; } ssize_t p = ::send(m_fd, ptr, size, (more_data ? MSG_MORE : 0)); if (p <= 0) { if (errno == EINTR || errno == EAGAIN) { DEBUGLOG("cxSocket::write: EINTR during write(), retrying"); continue; } else if (errno != EPIPE) ERRORLOG("cxSocket::write: write() error"); return p; } ptr += p; size -= p; } return written; } ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms) { int retryCounter = 0; if(m_fd == -1) return -1; ssize_t missing = (ssize_t)size; unsigned char *ptr = (unsigned char *)buffer; while (missing > 0) { if(!m_pollerRead->Poll(timeout_ms)) { ERRORLOG("cxSocket::read: poll() failed at %d/%d", (int)(size-missing), (int)size); return size-missing; } ssize_t p = ::read(m_fd, ptr, missing); if (p < 0) { if (retryCounter < 10 && (errno == EINTR || errno == EAGAIN)) { DEBUGLOG("cxSocket::read: EINTR/EAGAIN during read(), retrying"); retryCounter++; continue; } ERRORLOG("cxSocket::read: read() error at %d/%d", (int)(size-missing), (int)size); return 0; } else if (p == 0) { INFOLOG("cxSocket::read: eof, connection closed"); return 0; } retryCounter = 0; ptr += p; missing -= p; } return size; } void cxSocket::SetHandle(int h) { if(h != m_fd) { close(); m_fd = h; delete m_pollerRead; delete m_pollerWrite; m_pollerRead = new cPoller(m_fd); m_pollerWrite = new cPoller(m_fd, true); } } char *cxSocket::ip2txt(uint32_t ip, unsigned int port, char *str) { // inet_ntoa is not thread-safe (?) if(str) { unsigned int iph =(unsigned int)ntohl(ip); unsigned int porth =(unsigned int)ntohs(port); if(!porth) sprintf(str, "%d.%d.%d.%d", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff)); else sprintf(str, "%u.%u.%u.%u:%u", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff), porth); } return str; } vdr-plugin-vnsiserver-1.3.1/cxsocket.h000066400000000000000000000032741257700704000200130ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2003-2006 Petri Hintukainen * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_CXSOCKET_H #define VNSI_CXSOCKET_H #ifdef __FreeBSD__ #include #endif #include #include #include #include #include class cxSocket { private: int m_fd; cMutex m_MutexWrite; cPoller *m_pollerRead; cPoller *m_pollerWrite; public: cxSocket() : m_fd(-1), m_pollerRead(NULL), m_pollerWrite(NULL) {} ~cxSocket(); void SetHandle(int h); void close(void); void Shutdown(void); void LockWrite(); void UnlockWrite(); ssize_t read(void *buffer, size_t size, int timeout_ms = -1); ssize_t write(const void *buffer, size_t size, int timeout_ms = -1, bool more_data = false); static char *ip2txt(uint32_t ip, unsigned int port, char *str); }; #endif // VNSI_CXSOCKET_H vdr-plugin-vnsiserver-1.3.1/demuxer.c000066400000000000000000000353621257700704000176370ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "config.h" #include "demuxer.h" #include "parser.h" #include "videobuffer.h" #include #include cVNSIDemuxer::cVNSIDemuxer(bool bAllowRDS) : m_bAllowRDS(bAllowRDS) { m_OldPmtVersion = -1; } cVNSIDemuxer::~cVNSIDemuxer() { } void cVNSIDemuxer::Open(const cChannel &channel, cVideoBuffer *videoBuffer) { cMutexLock lock(&m_Mutex); m_CurrentChannel = channel; m_VideoBuffer = videoBuffer; m_OldPmtVersion = -1; if (m_CurrentChannel.Vpid()) m_WaitIFrame = true; else m_WaitIFrame = false; m_PtsWrap.m_Wrap = false; m_PtsWrap.m_NoOfWraps = 0; m_PtsWrap.m_ConfirmCount = 0; m_MuxPacketSerial = 0; m_Error = ERROR_DEMUX_NODATA; m_SetRefTime = true; } void cVNSIDemuxer::Close() { cMutexLock lock(&m_Mutex); for (std::list::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it) { DEBUGLOG("Deleting stream parser for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type()); delete (*it); } m_Streams.clear(); m_StreamInfos.clear(); } int cVNSIDemuxer::Read(sStreamPacket *packet, sStreamPacket *packet_side_data) { uint8_t *buf; int len; cTSStream *stream; cMutexLock lock(&m_Mutex); // clear packet if (!packet) return -1; packet->data = NULL; packet->streamChange = false; packet->pmtChange = false; // read TS Packet from buffer len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime); // eof if (len == -2) return -2; else if (len != TS_SIZE) return -1; m_Error &= ~ERROR_DEMUX_NODATA; int ts_pid = TsPid(buf); // parse PAT/PMT if (ts_pid == PATPID) { m_PatPmtParser.ParsePat(buf, TS_SIZE); } #if APIVERSNUM >= 10733 else if (m_PatPmtParser.IsPmtPid(ts_pid)) #else else if (ts_pid == m_PatPmtParser.PmtPid()) #endif { int patVersion, pmtVersion; m_PatPmtParser.ParsePmt(buf, TS_SIZE); if (m_PatPmtParser.GetVersions(patVersion, pmtVersion)) { if (pmtVersion != m_OldPmtVersion) { cChannel pmtChannel(m_CurrentChannel); SetChannelPids(&pmtChannel, &m_PatPmtParser); SetChannelStreams(&pmtChannel); m_PatPmtParser.Reset(); m_OldPmtVersion = pmtVersion; if (EnsureParsers()) { packet->pmtChange = true; return 1; } } } } else if (stream = FindStream(ts_pid)) { int error = stream->ProcessTSPacket(buf, packet, packet_side_data, m_WaitIFrame); if (error == 0) { m_WaitIFrame = false; packet->serial = m_MuxPacketSerial; if (m_SetRefTime) { m_refTime = m_VideoBuffer->GetRefTime(); packet->reftime = m_refTime; m_SetRefTime = false; } return 1; } else if (error < 0) { m_Error |= abs(error); } } return 0; } bool cVNSIDemuxer::SeekTime(int64_t time) { off_t pos, pos_min, pos_max, pos_limit, start_pos; int64_t ts, ts_min, ts_max, last_ts; int no_change; if (!m_VideoBuffer->HasBuffer()) return false; cMutexLock lock(&m_Mutex); // INFOLOG("----- seek to time: %ld", time); // rescale to 90khz time = cTSStream::Rescale(time, 90000, DVD_TIME_BASE); m_VideoBuffer->GetPositions(&pos, &pos_min, &pos_max); // INFOLOG("----- seek to time: %ld", time); // INFOLOG("------pos: %ld, pos min: %ld, pos max: %ld", pos, pos_min, pos_max); if (!GetTimeAtPos(&pos_min, &ts_min)) { ResetParsers(); m_WaitIFrame = true; return false; } // INFOLOG("----time at min: %ld", ts_min); if (ts_min >= time) { m_VideoBuffer->SetPos(pos_min); ResetParsers(); m_WaitIFrame = true; m_MuxPacketSerial++; return true; } int64_t timecur; GetTimeAtPos(&pos, &timecur); // get time at end of buffer unsigned int step= 1024; bool gotTime; do { pos_max -= step; gotTime = GetTimeAtPos(&pos_max, &ts_max); step += step; } while (!gotTime && pos_max >= step); if (!gotTime) { ResetParsers(); m_WaitIFrame = true; return false; } if (ts_max <= time) { ResetParsers(); m_WaitIFrame = true; m_MuxPacketSerial++; return true; } // INFOLOG(" - time in buffer: %ld", cTSStream::Rescale(ts_max-ts_min, DVD_TIME_BASE, 90000)/1000000); // bisect seek if(ts_min > ts_max) { ResetParsers(); m_WaitIFrame = true; return false; } else if (ts_min == ts_max) { pos_limit = pos_min; } else pos_limit = pos_max; no_change = 0; ts = time; last_ts = 0; while (pos_min < pos_limit) { if (no_change==0) { // interpolate position pos = cTSStream::Rescale(time - ts_min, pos_max - pos_min, ts_max - ts_min) + pos_min - (pos_max - pos_limit); } else if (no_change==1) { // bisection, if interpolation failed to change min or max pos last time pos = (pos_min + pos_limit) >> 1; } else { // linear search if bisection failed pos = pos_min; } // clamp calculated pos into boundaries if( pos <= pos_min) pos = pos_min + 1; else if (pos > pos_limit) pos = pos_limit; start_pos = pos; // get time stamp at pos if (!GetTimeAtPos(&pos, &ts)) { ResetParsers(); m_WaitIFrame = true; return false; } pos = m_VideoBuffer->GetPosCur(); // determine method for next calculation of pos if ((last_ts == ts) || (pos >= pos_max)) no_change++; else no_change=0; // INFOLOG("--- pos: %ld, \t time: %ld, diff time: %ld", pos, ts, time-ts); // 0.4 sec is close enough if (abs(time - ts) <= 36000) { break; } // target is to the left else if (time <= ts) { pos_limit = start_pos - 1; pos_max = pos; ts_max = ts; } // target is to the right if (time >= ts) { pos_min = pos; ts_min = ts; } last_ts = ts; } // INFOLOG("----pos found: %ld", pos); // INFOLOG("----time at pos: %ld, diff time: %ld", ts, cTSStream::Rescale(timecur-ts, DVD_TIME_BASE, 90000)); m_VideoBuffer->SetPos(pos); ResetParsers(); m_WaitIFrame = true; m_MuxPacketSerial++; return true; } void cVNSIDemuxer::BufferStatus(bool ×hift, uint32_t &start, uint32_t &end) { timeshift = m_VideoBuffer->HasBuffer(); if (timeshift) { if (!m_wrapTime) { start = m_refTime; } else { start = m_endTime - (m_wrapTime - m_refTime); } end = m_endTime; } else { start = 0; end = 0; } } cTSStream *cVNSIDemuxer::GetFirstStream() { m_StreamsIterator = m_Streams.begin(); if (m_StreamsIterator != m_Streams.end()) return *m_StreamsIterator; else return NULL; } cTSStream *cVNSIDemuxer::GetNextStream() { ++m_StreamsIterator; if (m_StreamsIterator != m_Streams.end()) return *m_StreamsIterator; else return NULL; } cTSStream *cVNSIDemuxer::FindStream(int Pid) { for (std::list::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it) { if (Pid == (*it)->GetPID()) return *it; } return NULL; } void cVNSIDemuxer::ResetParsers() { for (std::list::iterator it = m_Streams.begin(); it != m_Streams.end(); ++it) { (*it)->ResetParser(); } } void cVNSIDemuxer::AddStreamInfo(sStreamInfo &stream) { m_StreamInfos.push_back(stream); } bool cVNSIDemuxer::EnsureParsers() { bool streamChange = false; std::list::iterator it = m_Streams.begin(); while (it != m_Streams.end()) { std::list::iterator its; for (its = m_StreamInfos.begin(); its != m_StreamInfos.end(); ++its) { if ((its->pID == (*it)->GetPID()) && (its->type == (*it)->Type())) { break; } } if (its == m_StreamInfos.end()) { INFOLOG("Deleting stream for pid=%i and type=%i", (*it)->GetPID(), (*it)->Type()); m_Streams.erase(it); it = m_Streams.begin(); streamChange = true; } else ++it; } for (std::list::iterator it = m_StreamInfos.begin(); it != m_StreamInfos.end(); ++it) { cTSStream *stream = FindStream(it->pID); if (stream) { // TODO: check for change in lang stream->SetLanguage(it->language); continue; } if (it->type == stH264) { stream = new cTSStream(stH264, it->pID, &m_PtsWrap); } else if (it->type == stMPEG2VIDEO) { stream = new cTSStream(stMPEG2VIDEO, it->pID, &m_PtsWrap); } else if (it->type == stMPEG2AUDIO) { stream = new cTSStream(stMPEG2AUDIO, it->pID, &m_PtsWrap, it->handleRDS); stream->SetLanguage(it->language); } else if (it->type == stAACADTS) { stream = new cTSStream(stAACADTS, it->pID, &m_PtsWrap); stream->SetLanguage(it->language); } else if (it->type == stAACLATM) { stream = new cTSStream(stAACLATM, it->pID, &m_PtsWrap); stream->SetLanguage(it->language); } else if (it->type == stAC3) { stream = new cTSStream(stAC3, it->pID, &m_PtsWrap); stream->SetLanguage(it->language); } else if (it->type == stEAC3) { stream = new cTSStream(stEAC3, it->pID, &m_PtsWrap); stream->SetLanguage(it->language); } else if (it->type == stDVBSUB) { stream = new cTSStream(stDVBSUB, it->pID, &m_PtsWrap); stream->SetLanguage(it->language); #if APIVERSNUM >= 10709 stream->SetSubtitlingDescriptor(it->subtitlingType, it->compositionPageId, it->ancillaryPageId); #endif } else if (it->type == stTELETEXT) { stream = new cTSStream(stTELETEXT, it->pID, &m_PtsWrap); } else continue; m_Streams.push_back(stream); INFOLOG("Created stream for pid=%i and type=%i", stream->GetPID(), stream->Type()); streamChange = true; } m_StreamInfos.clear(); return streamChange; } void cVNSIDemuxer::SetChannelStreams(const cChannel *channel) { sStreamInfo newStream; bool containsVideo = false; int index = 0; if (channel->Vpid()) { newStream.pID = channel->Vpid(); #if APIVERSNUM >= 10701 if (channel->Vtype() == 0x1B) newStream.type = stH264; else #endif newStream.type = stMPEG2VIDEO; AddStreamInfo(newStream); containsVideo = true; } const int *DPids = channel->Dpids(); index = 0; for ( ; *DPids; DPids++) { if (!FindStream(*DPids)) { newStream.pID = *DPids; newStream.type = stAC3; #if APIVERSNUM >= 10715 if (channel->Dtype(index) == SI::EnhancedAC3DescriptorTag) newStream.type = stEAC3; #endif newStream.SetLanguage(channel->Dlang(index)); AddStreamInfo(newStream); } index++; } const int *APids = channel->Apids(); index = 0; for ( ; *APids; APids++) { if (!FindStream(*APids)) { newStream.pID = *APids; newStream.type = stMPEG2AUDIO; #if APIVERSNUM >= 10715 if (channel->Atype(index) == 0x0F) newStream.type = stAACADTS; else if (channel->Atype(index) == 0x11) newStream.type = stAACLATM; #endif newStream.handleRDS = m_bAllowRDS && newStream.type == stMPEG2AUDIO && !containsVideo ? true : false; // Relevant for RDS, if present only on mpeg 2 audio, use only if RDS is allowed newStream.SetLanguage(channel->Alang(index)); AddStreamInfo(newStream); } index++; } const int *SPids = channel->Spids(); if (SPids) { index = 0; for ( ; *SPids; SPids++) { if (!FindStream(*SPids)) { newStream.pID = *SPids; newStream.type = stDVBSUB; newStream.SetLanguage(channel->Slang(index)); #if APIVERSNUM >= 10709 newStream.subtitlingType = channel->SubtitlingType(index); newStream.compositionPageId = channel->CompositionPageId(index); newStream.ancillaryPageId = channel->AncillaryPageId(index); #endif AddStreamInfo(newStream); } index++; } } if (channel->Tpid()) { newStream.pID = channel->Tpid(); newStream.type = stTELETEXT; AddStreamInfo(newStream); } } void cVNSIDemuxer::SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser) { int Apids[MAXAPIDS + 1] = { 0 }; int Atypes[MAXAPIDS + 1] = { 0 }; int Dpids[MAXDPIDS + 1] = { 0 }; int Dtypes[MAXDPIDS + 1] = { 0 }; int Spids[MAXSPIDS + 1] = { 0 }; char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" }; char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" }; char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" }; int index = 0; const int *aPids = patPmtParser->Apids(); index = 0; for ( ; *aPids; aPids++) { Apids[index] = patPmtParser->Apid(index); Atypes[index] = patPmtParser->Atype(index); strn0cpy(ALangs[index], patPmtParser->Alang(index), MAXLANGCODE2); index++; } const int *dPids = patPmtParser->Dpids(); index = 0; for ( ; *dPids; dPids++) { Dpids[index] = patPmtParser->Dpid(index); Dtypes[index] = patPmtParser->Dtype(index); strn0cpy(DLangs[index], patPmtParser->Dlang(index), MAXLANGCODE2); index++; } const int *sPids = patPmtParser->Spids(); index = 0; for ( ; *sPids; sPids++) { Spids[index] = patPmtParser->Spid(index); strn0cpy(SLangs[index], patPmtParser->Slang(index), MAXLANGCODE2); index++; } int Vpid = patPmtParser->Vpid(); int Ppid = patPmtParser->Ppid(); int VType = patPmtParser->Vtype(); int Tpid = m_CurrentChannel.Tpid(); channel->SetPids(Vpid, Ppid, VType, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); } bool cVNSIDemuxer::GetTimeAtPos(off_t *pos, int64_t *time) { uint8_t *buf; int len; cTSStream *stream; int ts_pid; m_VideoBuffer->SetPos(*pos); ResetParsers(); while (len = m_VideoBuffer->Read(&buf, TS_SIZE, m_endTime, m_wrapTime) == TS_SIZE) { ts_pid = TsPid(buf); if (stream = FindStream(ts_pid)) { // only consider video or audio streams if ((stream->Content() == scVIDEO || stream->Content() == scAUDIO) && stream->ReadTime(buf, time)) { return true; } } } return false; } uint16_t cVNSIDemuxer::GetError() { uint16_t ret = m_Error; m_Error = ERROR_DEMUX_NODATA; return ret; } vdr-plugin-vnsiserver-1.3.1/demuxer.h000066400000000000000000000050711257700704000176360ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #pragma once #include #include "parser.h" struct sStreamPacket; class cTSStream; class cChannel; class cPatPmtParser; class cVideoBuffer; struct sStreamInfo { int pID; eStreamType type; eStreamContent content; char language[MAXLANGCODE2]; int subtitlingType; int compositionPageId; int ancillaryPageId; bool handleRDS; void SetLanguage(const char* lang) { language[0] = lang[0]; language[1] = lang[1]; language[2] = lang[2]; language[3] = 0; } }; class cVNSIDemuxer { public: cVNSIDemuxer(bool bAllowRDS); virtual ~cVNSIDemuxer(); int Read(sStreamPacket *packet, sStreamPacket *packet_side_data); cTSStream *GetFirstStream(); cTSStream *GetNextStream(); void Open(const cChannel &channel, cVideoBuffer *videoBuffer); void Close(); bool SeekTime(int64_t time); uint32_t GetSerial() { return m_MuxPacketSerial; } void SetSerial(uint32_t serial) { m_MuxPacketSerial = serial; } void BufferStatus(bool ×hift, uint32_t &start, uint32_t &end); uint16_t GetError(); protected: bool EnsureParsers(); void ResetParsers(); void SetChannelStreams(const cChannel *channel); void SetChannelPids(cChannel *channel, cPatPmtParser *patPmtParser); cTSStream *FindStream(int Pid); void AddStreamInfo(sStreamInfo &stream); bool GetTimeAtPos(off_t *pos, int64_t *time); std::list m_Streams; std::list::iterator m_StreamsIterator; std::list m_StreamInfos; cChannel m_CurrentChannel; cPatPmtParser m_PatPmtParser; int m_OldPmtVersion; bool m_WaitIFrame; cVideoBuffer *m_VideoBuffer; cMutex m_Mutex; uint32_t m_MuxPacketSerial; sPtsWrap m_PtsWrap; uint16_t m_Error; bool m_SetRefTime; time_t m_refTime, m_endTime, m_wrapTime; bool m_bAllowRDS; }; vdr-plugin-vnsiserver-1.3.1/hash.c000066400000000000000000000117001257700704000170770ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 1986 Gary S. Brown (CRC32 code) * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "hash.h" static uint32_t crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t crc32(const unsigned char *buf, size_t size) { uint32_t crc = 0xFFFFFFFF; const uint8_t *p = (uint8_t*)buf; while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return (crc ^ ~0U) & 0x7FFFFFFF; // channeluid is signed } uint32_t CreateStringHash(const cString& string) { const char* p = string; int len = strlen(p); return crc32((const unsigned char*)p, len); } uint32_t CreateChannelUID(const cChannel* channel) { cString channelid = channel->GetChannelID().ToString(); return CreateStringHash(channelid); } const cChannel* FindChannelByUID(uint32_t channelUID) { cChannel* result = NULL; // maybe we need to use a lookup table for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { cString channelid = channel->GetChannelID().ToString(); if(channelUID == CreateStringHash(channelid)) { result = channel; break; } } return result; } vdr-plugin-vnsiserver-1.3.1/hash.h000066400000000000000000000022301257700704000171020ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 1986 Gary S. Brown (CRC32 code) * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_HASH_H #define VNSI_HASH_H #include #include class cChannel; uint32_t CreateChannelUID(const cChannel* channel); const cChannel* FindChannelByUID(uint32_t channelUID); uint32_t CreateStringHash(const cString& string); #endif // VNSI_HASH_H vdr-plugin-vnsiserver-1.3.1/parser.c000066400000000000000000000433531257700704000174610ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include #include "config.h" #include "parser.h" #include "parser_AAC.h" #include "parser_AC3.h" #include "parser_DTS.h" #include "parser_h264.h" #include "parser_MPEGAudio.h" #include "parser_MPEGVideo.h" #include "parser_Subtitle.h" #include "parser_Teletext.h" #define PTS_MASK 0x1ffffffffLL //#define PTS_MASK 0x7ffffLL #ifndef INT64_MIN #define INT64_MIN (-0x7fffffffffffffffLL-1) #endif // --- cParser ------------------------------------------------- cParser::cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : m_pID(pID), m_PtsWrap(ptsWrap), m_ObservePtsWraps(observePtsWraps) { m_PesBuffer = NULL; m_Stream = stream; m_IsVideo = false; m_PesBufferInitialSize = 1024; Reset(); } cParser::~cParser() { if (m_PesBuffer) free(m_PesBuffer); } void cParser::Reset() { m_curPTS = DVD_NOPTS_VALUE; m_curDTS = DVD_NOPTS_VALUE; m_prevDTS = DVD_NOPTS_VALUE; m_PesBufferPtr = 0; m_PesParserPtr = 0; m_PesNextFramePtr = 0; m_FoundFrame = false; m_FrameValid = false; m_PesPacketLength = 0; m_PesHeaderPtr = 0; m_Error = ERROR_PES_GENERAL; } /* * Extract DTS and PTS and update current values in stream */ int cParser::ParsePESHeader(uint8_t *buf, size_t len) { m_PesPacketLength = buf[4] << 8 | buf[5]; if (!PesIsVideoPacket(buf) && !PesIsAudioPacket(buf)) return 6; unsigned int hdr_len = PesHeaderLength(buf); if (m_PesPacketLength > 0) m_PesPacketLength -= (hdr_len-6); // scrambled if ((buf[6] & 0x30) != 0) return hdr_len; // parse PTS if ((hdr_len >= 13) && ((((buf[7] & 0xC0) == 0x80) && ((buf[9] & 0xF0) == 0x20)) || ((buf[7] & 0xC0) == 0xC0) && ((buf[9] & 0xF0) == 0x30))) { int64_t pts; pts = ((int64_t)(buf[ 9] & 0x0E)) << 29 ; pts |= ((int64_t) buf[10]) << 22 ; pts |= ((int64_t)(buf[11] & 0xFE)) << 14 ; pts |= ((int64_t) buf[12]) << 7 ; pts |= ((int64_t)(buf[13] & 0xFE)) >> 1 ; int64_t bit32and31 = pts >> 31; if (m_ObservePtsWraps) { if ((bit32and31 == 3) && !m_PtsWrap->m_Wrap) { m_PtsWrap->m_ConfirmCount++; if (m_PtsWrap->m_ConfirmCount >= 2) { m_PtsWrap->m_Wrap = true; } } else if ((bit32and31 == 1) && m_PtsWrap->m_Wrap) { m_PtsWrap->m_ConfirmCount++; if (m_PtsWrap->m_ConfirmCount >= 2) { m_PtsWrap->m_Wrap = false; m_PtsWrap->m_NoOfWraps++; } } else m_PtsWrap->m_ConfirmCount = 0; } m_prevPTS = m_curPTS; m_curPTS = pts; m_PesTimePos = m_PesBufferPtr; if (m_PtsWrap->m_Wrap && !(bit32and31)) { m_curPTS += 1LL<<33; } if (m_PtsWrap->m_NoOfWraps) { m_curPTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33); } } else return hdr_len; m_prevDTS = m_curDTS; // parse DTS if ((hdr_len >= 18) && ((buf[7] & 0xC0) == 0xC0) && ((buf[14] & 0xF0) == 0x10)) { int64_t dts; dts = ((int64_t)( buf[14] & 0x0E)) << 29 ; dts |= (int64_t)( buf[15] << 22 ); dts |= (int64_t)((buf[16] & 0xFE) << 14 ); dts |= (int64_t)( buf[17] << 7 ); dts |= (int64_t)((buf[18] & 0xFE) >> 1 ); m_curDTS = dts; if (m_PtsWrap->m_Wrap && !(m_curDTS >> 31)) { m_curDTS += 1LL<<33; } if (m_PtsWrap->m_NoOfWraps) { m_curDTS += ((int64_t)m_PtsWrap->m_NoOfWraps<<33); } } else m_curDTS = m_curPTS; return hdr_len; } int cParser::ParsePacketHeader(uint8_t *data) { if (TsIsScrambled(data)) { m_Error = ERROR_PES_SCRAMBLE; return -1; } if (TsPayloadStart(data)) { m_IsPusi = true; m_Error = 0; } int bytes = TS_SIZE - TsPayloadOffset(data); if(bytes < 0 || bytes > TS_SIZE) { m_Error = ERROR_PES_GENERAL; return -1; } if (TsError(data)) { m_Error = ERROR_PES_GENERAL; return -1; } if (!TsHasPayload(data)) { DEBUGLOG("no payload, size %d", bytes); return 0; } /* drop broken PES packets */ if (m_Error) { return -1; } return bytes; } bool cParser::AddPESPacket(uint8_t *data, int size) { // check for beginning of a PES packet if (m_IsPusi && m_IsVideo && !IsValidStartCode(data, 4)) { m_IsPusi = false; } if (m_IsPusi) { int hdr_len = 6; if (m_PesHeaderPtr + size < hdr_len) { memcpy(m_PesHeader+m_PesHeaderPtr, data, size); m_PesHeaderPtr += size; return false; } else if (m_PesHeaderPtr) { int bytesNeeded = hdr_len-m_PesHeaderPtr; if (bytesNeeded > 0) { memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded); m_PesHeaderPtr += bytesNeeded; data += bytesNeeded; size -= bytesNeeded; } if (!IsValidStartCode(m_PesHeader, hdr_len)) { Reset(); m_Error |= ERROR_PES_STARTCODE; return false; } if (PesIsVideoPacket(m_PesHeader) || PesIsAudioPacket(m_PesHeader)) { hdr_len = 9; bytesNeeded = hdr_len-m_PesHeaderPtr; if (size < bytesNeeded) { memcpy(m_PesHeader+m_PesHeaderPtr, data, size); m_PesHeaderPtr += size; return false; } else if (bytesNeeded > 0) { memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded); m_PesHeaderPtr += bytesNeeded; data += bytesNeeded; size -= bytesNeeded; } if ((m_PesHeader[6] & 0x30)) { Reset(); m_Error |= ERROR_PES_SCRAMBLE; return false; } hdr_len = PesHeaderLength(m_PesHeader); if (hdr_len > PES_HEADER_LENGTH) { Reset(); return false; } } bytesNeeded = hdr_len-m_PesHeaderPtr; if (size < bytesNeeded) { memcpy(m_PesHeader+m_PesHeaderPtr, data, size); m_PesHeaderPtr += size; return false; } else if (bytesNeeded > 0) { memcpy(m_PesHeader+m_PesHeaderPtr, data, bytesNeeded); m_PesHeaderPtr += bytesNeeded; data += bytesNeeded; size -= bytesNeeded; } if (ParsePESHeader(m_PesHeader, hdr_len) < 0) { INFOLOG("error parsing pes packet error "); Reset(); return false; } m_PesHeaderPtr = 0; m_IsPusi = false; } else if (!IsValidStartCode(data, size)) { Reset(); m_Error |= ERROR_PES_STARTCODE; return false; } else { if (PesIsVideoPacket(data) || PesIsAudioPacket(data)) { if (size < 9) { memcpy(m_PesHeader+m_PesHeaderPtr, data, size); m_PesHeaderPtr += size; return false; } if ((data[6] & 0x30)) { Reset(); m_Error |= ERROR_PES_STARTCODE; return false; } hdr_len = PesHeaderLength(data); if (hdr_len > PES_HEADER_LENGTH) { Reset(); return false; } } if (size < hdr_len) { memcpy(m_PesHeader+m_PesHeaderPtr, data, size); m_PesHeaderPtr += size; return false; } if (ParsePESHeader(data, hdr_len) < 0) { INFOLOG("error parsing pes packet error 2"); Reset(); return false; } data += hdr_len; size -= hdr_len; m_IsPusi = false; } } if (m_PesBuffer == NULL) { m_PesBufferSize = m_PesBufferInitialSize; m_PesBuffer = (uint8_t*)malloc(m_PesBufferSize); if (m_PesBuffer == NULL) { ERRORLOG("cParser::AddPESPacket - malloc failed"); Reset(); return false; } } if (m_PesBufferPtr + size >= m_PesBufferSize) { if (m_PesBufferPtr + size >= 1000000) { ERRORLOG("cParser::AddPESPacket - max buffer size reached, pid: %d", m_pID); Reset(); return false; } m_PesBufferSize += m_PesBufferInitialSize / 10; m_PesBuffer = (uint8_t*)realloc(m_PesBuffer, m_PesBufferSize); if (m_PesBuffer == NULL) { ERRORLOG("cParser::AddPESPacket - realloc failed"); Reset(); return false; } } // copy first packet of new frame to front if (m_PesNextFramePtr) { memmove(m_PesBuffer, m_PesBuffer+m_PesNextFramePtr, m_PesBufferPtr-m_PesNextFramePtr); m_PesBufferPtr = m_PesBufferPtr-m_PesNextFramePtr; m_PesTimePos -= m_PesNextFramePtr; m_PesNextFramePtr = 0; } // copy payload memcpy(m_PesBuffer+m_PesBufferPtr, data, size); m_PesBufferPtr += size; return true; } inline bool cParser::IsValidStartCode(uint8_t *buf, int size) { if (size < 4) return false; uint32_t startcode = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; if (m_Stream->Type() == stH264 || m_Stream->Type() == stMPEG2VIDEO) { if (startcode >= 0x000001e0 && startcode <= 0x000001ef) return true; } else if (m_Stream->Type() == stAC3 || m_Stream->Type() == stEAC3) { if (PesIsPS1Packet(buf)) return true; } else if (m_Stream->Type() == stMPEG2AUDIO || m_Stream->Type() == stAACADTS || m_Stream->Type() == stAACLATM || m_Stream->Type() == stDTS) { if (startcode >= 0x000001c0 && startcode <= 0x000001df) return true; } else if (m_Stream->Type() == stTELETEXT) { if (PesIsPS1Packet(buf)) return true; } else if (m_Stream->Type() == stDVBSUB || m_Stream->Type() == stTEXTSUB) { if (startcode == 0x000001bd || startcode == 0x000001bf || (startcode >= 0x000001f0 && startcode <= 0x000001f9)) return true; } return false; } // --- cTSStream ---------------------------------------------------- uint32_t cTSStream::m_UniqueSideDataIDs = 0; cTSStream::cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData) : m_streamType(type) , m_pID(pid) { m_pesError = false; m_pesParser = NULL; m_language[0] = 0; m_FpsScale = 0; m_FpsRate = 0; m_Height = 0; m_Width = 0; m_Aspect = 0.0f; m_Channels = 0; m_SampleRate = 0; m_BitRate = 0; m_BitsPerSample = 0; m_BlockAlign = 0; m_IsStreamChange = false; if (m_streamType == stMPEG2VIDEO) { m_pesParser = new cParserMPEG2Video(m_pID, this, ptsWrap, true); m_streamContent = scVIDEO; } else if (m_streamType == stH264) { m_pesParser = new cParserH264(m_pID, this, ptsWrap, true); m_streamContent = scVIDEO; } else if (m_streamType == stMPEG2AUDIO) { m_pesParser = new cParserMPEG2Audio(m_pID, this, ptsWrap, true, handleSideData); m_streamContent = scAUDIO; } else if (m_streamType == stAACADTS) { m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true); m_streamContent = scAUDIO; } else if (m_streamType == stAACLATM) { m_pesParser = new cParserAAC(m_pID, this, ptsWrap, true); m_streamContent = scAUDIO; } else if (m_streamType == stAC3) { m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true); m_streamContent = scAUDIO; } else if (m_streamType == stEAC3) { m_pesParser = new cParserAC3(m_pID, this, ptsWrap, true); m_streamContent = scAUDIO; } else if (m_streamType == stDTS) { m_pesParser = new cParserDTS(m_pID, this, ptsWrap, true); m_streamContent = scAUDIO; } else if (m_streamType == stTELETEXT) { m_pesParser = new cParserTeletext(m_pID, this, ptsWrap, false); m_streamContent = scTELETEXT; } else if (m_streamType == stDVBSUB) { m_pesParser = new cParserSubtitle(m_pID, this, ptsWrap, false); m_streamContent = scSUBTITLE; } else { ERRORLOG("Unrecognised type %i inside stream %i", m_streamType, m_pID); return; } } cTSStream::~cTSStream() { if (m_pesParser) { delete m_pesParser; m_pesParser = NULL; } } int cTSStream::ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe) { int ret = 1; if (!data) return ret; if (!m_pesParser) return ret; int payloadSize = m_pesParser->ParsePacketHeader(data); if (payloadSize == 0) return ret; else if (payloadSize < 0) { return -m_pesParser->GetError(); } if (!m_pesParser->AddPESPacket(data+TS_SIZE-payloadSize, payloadSize)) { return -m_pesParser->GetError(); } m_pesParser->Parse(pkt, pkt_side_data); if (iframe && !m_pesParser->IsVideo()) return ret; if (pkt->data) { int64_t dts = pkt->dts; int64_t pts = pkt->pts; // Rescale for KODI if (pkt->dts != DVD_NOPTS_VALUE) pkt->dts = Rescale(dts, DVD_TIME_BASE, 90000); if (pkt->pts != DVD_NOPTS_VALUE) pkt->pts = Rescale(pts, DVD_TIME_BASE, 90000); pkt->duration = Rescale(pkt->duration, DVD_TIME_BASE, 90000); ret = 0; } if (pkt_side_data && pkt_side_data->data) { int64_t dts = pkt_side_data->dts; int64_t pts = pkt_side_data->pts; // Rescale for KODI if (pkt_side_data->dts != DVD_NOPTS_VALUE) pkt_side_data->dts = Rescale(dts, DVD_TIME_BASE, 90000); if (pkt_side_data->pts != DVD_NOPTS_VALUE) pkt_side_data->pts = Rescale(pts, DVD_TIME_BASE, 90000); pkt_side_data->duration = Rescale(pkt_side_data->duration, DVD_TIME_BASE, 90000); ret = 0; } return ret; } bool cTSStream::ReadTime(uint8_t *data, int64_t *dts) { if (!data) return false; if (!m_pesParser) return false; int payloadSize = m_pesParser->ParsePacketHeader(data); if (payloadSize < 0) return false; if (m_pesParser->m_IsPusi) { data += TS_SIZE-payloadSize; if (payloadSize >= 6 && m_pesParser->IsValidStartCode(data, payloadSize)) { m_pesParser->m_curPTS = DVD_NOPTS_VALUE; m_pesParser->m_curDTS = DVD_NOPTS_VALUE; m_pesParser->ParsePESHeader(data, payloadSize); if (m_pesParser->m_curDTS != DVD_NOPTS_VALUE) { *dts = m_pesParser->m_curDTS; return true; } else if (m_pesParser->m_curPTS != DVD_NOPTS_VALUE) { *dts = m_pesParser->m_curPTS; return true; } } m_pesParser->m_IsPusi = false; } return false; } void cTSStream::ResetParser() { if (m_pesParser) m_pesParser->Reset(); } int64_t cTSStream::Rescale(int64_t a, int64_t b, int64_t c) { uint64_t r = c/2; if (b<=INT_MAX && c<=INT_MAX) { if (a<=INT_MAX) return (a * b + r)/c; else return a/c*b + (a%c*b + r)/c; } else { uint64_t a0= a&0xFFFFFFFF; uint64_t a1= a>>32; uint64_t b0= b&0xFFFFFFFF; uint64_t b1= b>>32; uint64_t t1= a0*b1 + a1*b0; uint64_t t1a= t1<<32; a0 = a0*b0 + t1a; a1 = a1*b1 + (t1>>32) + (a0=0; i--) { a1+= a1 + ((a0>>i)&1); t1+=t1; if (c <= (int64_t)a1) { a1 -= c; t1++; } } return t1; } } uint32_t cTSStream::AddSideDataType(eStreamContent content) { m_UniqueSideDataIDs++; if (m_UniqueSideDataIDs == 0) m_UniqueSideDataIDs++; uint32_t sidePid = m_UniqueSideDataIDs << 16; m_SideDataTypes.push_back(std::make_pair(sidePid, content)); return sidePid; } void cTSStream::SetLanguage(const char *language) { m_language[0] = language[0]; m_language[1] = language[1]; m_language[2] = language[2]; m_language[3] = 0; } bool cTSStream::SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect) { if ((m_FpsScale != FpsScale) || (m_FpsRate != FpsRate) || (m_Height != Height) || (m_Width != Width) || (m_Aspect != Aspect)) m_IsStreamChange = true; m_FpsScale = FpsScale; m_FpsRate = FpsRate; m_Height = Height; m_Width = Width; m_Aspect = Aspect; return m_IsStreamChange; } void cTSStream::GetVideoInformation(uint32_t &FpsScale, uint32_t &FpsRate, uint32_t &Height, uint32_t &Width, double &Aspect) { FpsScale = m_FpsScale; FpsRate = m_FpsRate; Height = m_Height; Width = m_Width; Aspect = m_Aspect; m_IsStreamChange = false; } bool cTSStream::SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign) { if ((m_Channels != Channels) || (m_SampleRate != SampleRate) || (m_BlockAlign != BlockAlign) || (m_BitRate != BitRate) || (m_BitsPerSample != BitsPerSample)) m_IsStreamChange = true; m_Channels = Channels; m_SampleRate = SampleRate; m_BlockAlign = BlockAlign; m_BitRate = BitRate; m_BitsPerSample = BitsPerSample; return m_IsStreamChange; } void cTSStream::GetAudioInformation(uint32_t &Channels, uint32_t &SampleRate, uint32_t &BitRate, uint32_t &BitsPerSample, uint32_t &BlockAlign) { Channels = m_Channels; SampleRate = m_SampleRate; BlockAlign = m_BlockAlign; BitRate = m_BitRate; BitsPerSample = m_BitsPerSample; m_IsStreamChange = false; } void cTSStream::SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId) { m_subtitlingType = SubtitlingType; m_compositionPageId = CompositionPageId; m_ancillaryPageId = AncillaryPageId; } vdr-plugin-vnsiserver-1.3.1/parser.h000066400000000000000000000162061257700704000174630ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_H #define VNSI_DEMUXER_H #include #include #include #define DVD_TIME_BASE 1000000 #define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64 /* PES PIDs */ #define PRIVATE_STREAM1 0xBD #define PADDING_STREAM 0xBE #define PRIVATE_STREAM2 0xBF #define PRIVATE_STREAM3 0xFD #define AUDIO_STREAM_S 0xC0 /* 1100 0000 */ #define AUDIO_STREAM_E 0xDF /* 1101 1111 */ #define VIDEO_STREAM_S 0xE0 /* 1110 0000 */ #define VIDEO_STREAM_E 0xEF /* 1110 1111 */ #define AUDIO_STREAM_MASK 0x1F /* 0001 1111 */ #define VIDEO_STREAM_MASK 0x0F /* 0000 1111 */ #define AUDIO_STREAM 0xC0 /* 1100 0000 */ #define VIDEO_STREAM 0xE0 /* 1110 0000 */ #define ECM_STREAM 0xF0 #define EMM_STREAM 0xF1 #define DSM_CC_STREAM 0xF2 #define ISO13522_STREAM 0xF3 #define PROG_STREAM_DIR 0xFF inline bool PesIsHeader(const uchar *p) { return !(p)[0] && !(p)[1] && (p)[2] == 1; } inline int PesHeaderLength(const uchar *p) { return 8 + (p)[8] + 1; } inline bool PesIsVideoPacket(const uchar *p) { return (((p)[3] & ~VIDEO_STREAM_MASK) == VIDEO_STREAM); } inline bool PesIsMPEGAudioPacket(const uchar *p) { return (((p)[3] & ~AUDIO_STREAM_MASK) == AUDIO_STREAM); } inline bool PesIsPS1Packet(const uchar *p) { return ((p)[3] == PRIVATE_STREAM1 || (p)[3] == PRIVATE_STREAM3 ); } inline bool PesIsAudioPacket(const uchar *p) { return (PesIsMPEGAudioPacket(p) || PesIsPS1Packet(p)); } enum eStreamContent { scVIDEO, scAUDIO, scSUBTITLE, scTELETEXT, scPROGRAMM, scRDS }; enum eStreamType { stNone, stAC3, stMPEG2AUDIO, stEAC3, stAACADTS, stAACLATM, stDTS, stMPEG2VIDEO, stH264, stDVBSUB, stTEXTSUB, stTELETEXT, }; #define PKT_I_FRAME 1 #define PKT_P_FRAME 2 #define PKT_B_FRAME 3 #define PKT_NTYPES 4 struct sStreamPacket { int64_t id; int64_t dts; int64_t pts; int duration; uint8_t commercial; uint8_t componentindex; uint8_t *data; int size; bool streamChange; bool pmtChange; uint32_t serial; uint32_t reftime; }; struct sPtsWrap { bool m_Wrap; int m_NoOfWraps; int m_ConfirmCount; }; class cTSStream; #define PES_HEADER_LENGTH 128 class cParser { friend class cTSStream; public: cParser(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParser(); bool AddPESPacket(uint8_t *data, int size); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) = 0; // void ClearFrame() {m_PesBufferPtr = 0;} int ParsePacketHeader(uint8_t *data); int ParsePESHeader(uint8_t *buf, size_t len); virtual void Reset(); bool IsVideo() {return m_IsVideo; } uint16_t GetError() { return m_Error; } protected: virtual bool IsValidStartCode(uint8_t *buf, int size); uint8_t m_PesHeader[PES_HEADER_LENGTH]; int m_PesHeaderPtr; int m_PesPacketLength; uint8_t *m_PesBuffer; int m_PesBufferSize; int m_PesBufferPtr; size_t m_PesBufferInitialSize; size_t m_PesParserPtr; size_t m_PesNextFramePtr; int m_PesTimePos; bool m_FoundFrame; bool m_FrameValid; int m_pID; int64_t m_curPTS; int64_t m_curDTS; int64_t m_prevPTS; int64_t m_prevDTS; bool m_IsPusi; uint16_t m_Error; cTSStream *m_Stream; bool m_IsVideo; sPtsWrap *m_PtsWrap; bool m_ObservePtsWraps; }; class cTSStream { private: eStreamType m_streamType; const int m_pID; eStreamContent m_streamContent; bool m_IsStreamChange; bool m_pesError; cParser *m_pesParser; char m_language[4]; // ISO 639 3-letter language code (empty string if undefined) int m_FpsScale; // scale of 1000 and a rate of 29970 will result in 29.97 fps int m_FpsRate; int m_Height; // height of the stream reported by the demuxer int m_Width; // width of the stream reported by the demuxer float m_Aspect; // display aspect of stream int m_Channels; int m_SampleRate; int m_BitRate; int m_BitsPerSample; int m_BlockAlign; std::vector< std::pair > m_SideDataTypes; static uint32_t m_UniqueSideDataIDs; unsigned char m_subtitlingType; uint16_t m_compositionPageId; uint16_t m_ancillaryPageId; public: cTSStream(eStreamType type, int pid, sPtsWrap *ptsWrap, bool handleSideData = false); virtual ~cTSStream(); int ProcessTSPacket(uint8_t *data, sStreamPacket *pkt, sStreamPacket *pkt_side_data, bool iframe); bool ReadTime(uint8_t *data, int64_t *dts); void ResetParser(); void SetLanguage(const char *language); const char *GetLanguage() { return m_language; } const eStreamContent Content() const { return m_streamContent; } const eStreamType Type() const { return m_streamType; } void SetType(eStreamType type) { m_streamType = type; } const int GetPID() const { return m_pID; } uint32_t AddSideDataType(eStreamContent content); const std::vector< std::pair > *GetSideDataTypes() const { return &m_SideDataTypes; } /* Video Stream Information */ bool SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect); void GetVideoInformation(uint32_t &FpsScale, uint32_t &FpsRate, uint32_t &Height, uint32_t &Width, double &Aspect); /* Audio Stream Information */ bool SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign); void GetAudioInformation(uint32_t &Channels, uint32_t &SampleRate, uint32_t &BitRate, uint32_t &BitsPerSample, uint32_t &BlockAlign); /* Subtitle related stream information */ void SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId); unsigned char SubtitlingType() const { return m_subtitlingType; } uint16_t CompositionPageId() const { return m_compositionPageId; } uint16_t AncillaryPageId() const { return m_ancillaryPageId; } static int64_t Rescale(int64_t a, int64_t b, int64_t c); }; #endif // VNSI_DEMUXER_H vdr-plugin-vnsiserver-1.3.1/parser_AAC.c000066400000000000000000000160411257700704000201170ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "parser_AAC.h" static int aac_sample_rates[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; cParserAAC::cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_Configured = false; m_FrameLengthType = 0; m_PTS = 0; m_DTS = 0; m_FrameSize = 0; m_SampleRate = 0; m_Channels = 0; m_BitRate = 0; m_PesBufferInitialSize = 1920*2; m_DetectMuxMode = false; char *detectEnv = getenv("VNSI_AAC_MUXMODE"); if (detectEnv) { m_DetectMuxMode = true; INFOLOG("cParserAAC - detect muxing mode while parsing"); } } cParserAAC::~cParserAAC() { } void cParserAAC::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { int p = m_PesParserPtr; int l; while ((l = m_PesBufferPtr - p) > 8) { if (FindHeaders(m_PesBuffer + p, l) < 0) break; p++; } m_PesParserPtr = p; if (m_FoundFrame && l >= m_FrameSize) { bool streamChange = m_Stream->SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0); pkt->id = m_pID; pkt->data = &m_PesBuffer[p]; pkt->size = m_FrameSize; if(!m_SampleRate) m_SampleRate = aac_sample_rates[4]; pkt->duration = 1024 * 90000 / m_SampleRate; pkt->dts = m_DTS; pkt->pts = m_PTS; pkt->streamChange = streamChange; m_PesNextFramePtr = p + m_FrameSize; m_PesParserPtr = 0; m_FoundFrame = false; } } int cParserAAC::FindHeaders(uint8_t *buf, int buf_size) { if (m_FoundFrame) return -1; uint8_t *buf_ptr = buf; if (m_Stream->Type() == stAACLATM) { if ((buf_ptr[0] == 0x56 && (buf_ptr[1] & 0xE0) == 0xE0)) { // TODO if (buf_size < 16) return -1; cBitstream bs(buf_ptr, 16 * 8); bs.skipBits(11); m_FrameSize = bs.readBits(13) + 3; if (!ParseLATMAudioMuxElement(&bs)) return 0; m_FoundFrame = true; m_DTS = m_curPTS; m_PTS = m_curPTS; if(!m_SampleRate) m_SampleRate = aac_sample_rates[4]; m_curPTS += 90000 * 1024 / m_SampleRate; return -1; } else if (m_DetectMuxMode && buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xF0) == 0xF0) { m_Stream->SetType(stAACADTS); INFOLOG("cParserAAC::FindHeaders - detected ADTS muxing mode"); return -1; } } else if (m_Stream->Type() == stAACADTS) { if(buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xF0) == 0xF0) { // need at least 7 bytes for header if (buf_size < 7) return -1; cBitstream bs(buf_ptr, 9 * 8); bs.skipBits(15); // check if CRC is present, means header is 9 byte long int noCrc = bs.readBits(1); if (!noCrc && (buf_size < 9)) return -1; bs.skipBits(2); // profile int SampleRateIndex = bs.readBits(4); bs.skipBits(1); // private m_Channels = bs.readBits(3); bs.skipBits(4); m_FrameSize = bs.readBits(13); m_SampleRate = aac_sample_rates[SampleRateIndex & 0x0E]; m_FoundFrame = true; m_DTS = m_curPTS; m_PTS = m_curPTS; m_curPTS += 90000 * 1024 / m_SampleRate; return -1; } else if (m_DetectMuxMode && buf_ptr[0] == 0x56 && (buf_ptr[1] & 0xE0) == 0xE0) { m_Stream->SetType(stAACLATM); INFOLOG("cParserAAC::FindHeaders - detected LATM muxing mode"); return -1; } } return 0; } bool cParserAAC::ParseLATMAudioMuxElement(cBitstream *bs) { if (!bs->readBits1()) ReadStreamMuxConfig(bs); if (!m_Configured) return false; return true; } void cParserAAC::ReadStreamMuxConfig(cBitstream *bs) { int AudioMuxVersion = bs->readBits(1); m_AudioMuxVersion_A = 0; if (AudioMuxVersion) // audioMuxVersion m_AudioMuxVersion_A = bs->readBits(1); if(m_AudioMuxVersion_A) return; if (AudioMuxVersion) LATMGetValue(bs); // taraFullness bs->skipBits(1); // allStreamSameTimeFraming = 1 bs->skipBits(6); // numSubFrames = 0 bs->skipBits(4); // numPrograms = 0 // for each program (which there is only on in DVB) bs->skipBits(3); // numLayer = 0 // for each layer (which there is only on in DVB) if (!AudioMuxVersion) ReadAudioSpecificConfig(bs); else return; // these are not needed... perhaps m_FrameLengthType = bs->readBits(3); switch (m_FrameLengthType) { case 0: bs->readBits(8); break; case 1: bs->readBits(9); break; case 3: case 4: case 5: bs->readBits(6); // celp_table_index break; case 6: case 7: bs->readBits(1); // hvxc_table_index break; } if (bs->readBits(1)) { // other data? if (AudioMuxVersion == 1) { LATMGetValue(bs); // other_data_bits } else { int esc; do { esc = bs->readBits(1); bs->skipBits(8); } while (esc); } } if (bs->readBits(1)) // crc present? bs->skipBits(8); // config_crc m_Configured = true; } void cParserAAC::ReadAudioSpecificConfig(cBitstream *bs) { int aot = bs->readBits(5); if (aot == 31) aot = 32 + bs->readBits(6); int SampleRateIndex = bs->readBits(4); if (SampleRateIndex == 0xf) m_SampleRate = bs->readBits(24); else m_SampleRate = aac_sample_rates[SampleRateIndex & 0xf]; m_Channels = bs->readBits(4); if (aot == 5) { // AOT_SBR if (bs->readBits(4) == 0xf) { // extensionSamplingFrequencyIndex bs->skipBits(24); } aot = bs->readBits(5); // this is the main object type (i.e. non-extended) if (aot == 31) aot = 32 + bs->readBits(6); } if(aot != 2) return; bs->skipBits(1); //framelen_flag if (bs->readBits1()) // depends_on_coder bs->skipBits(14); if (bs->readBits(1)) // ext_flag bs->skipBits(1); // ext3_flag } void cParserAAC::Reset() { cParser::Reset(); m_Configured = false; } vdr-plugin-vnsiserver-1.3.1/parser_AAC.h000066400000000000000000000036201257700704000201230ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_AAC_H #define VNSI_DEMUXER_AAC_H #include "parser.h" #include "bitstream.h" // --- cParserAAC ------------------------------------------------- class cParserAAC : public cParser { private: int m_SampleRate; int m_Channels; int m_BitRate; int m_FrameSize; int64_t m_PTS; /* pts of the current frame */ int64_t m_DTS; /* dts of the current frame */ bool m_Configured; int m_AudioMuxVersion_A; int m_FrameLengthType; bool m_DetectMuxMode; int FindHeaders(uint8_t *buf, int buf_size); bool ParseLATMAudioMuxElement(cBitstream *bs); void ReadStreamMuxConfig(cBitstream *bs); void ReadAudioSpecificConfig(cBitstream *bs); uint32_t LATMGetValue(cBitstream *bs) { return bs->readBits(bs->readBits(2) * 8); } public: cParserAAC(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserAAC(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); virtual void Reset(); }; #endif // VNSI_DEMUXER_AAC_H vdr-plugin-vnsiserver-1.3.1/parser_AC3.c000066400000000000000000000143101257700704000200760ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "parser_AC3.h" #include "bitstream.h" #define AC3_HEADER_SIZE 7 /** Channel mode (audio coding mode) */ typedef enum { AC3_CHMODE_DUALMONO = 0, AC3_CHMODE_MONO, AC3_CHMODE_STEREO, AC3_CHMODE_3F, AC3_CHMODE_2F1R, AC3_CHMODE_3F1R, AC3_CHMODE_2F2R, AC3_CHMODE_3F2R } AC3ChannelMode; /* possible frequencies */ const uint16_t AC3SampleRateTable[3] = { 48000, 44100, 32000 }; /* possible bitrates */ const uint16_t AC3BitrateTable[19] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 }; const uint8_t AC3ChannelsTable[8] = { 2, 1, 2, 3, 3, 4, 4, 5 }; const uint16_t AC3FrameSizeTable[38][3] = { { 64, 69, 96 }, { 64, 70, 96 }, { 80, 87, 120 }, { 80, 88, 120 }, { 96, 104, 144 }, { 96, 105, 144 }, { 112, 121, 168 }, { 112, 122, 168 }, { 128, 139, 192 }, { 128, 140, 192 }, { 160, 174, 240 }, { 160, 175, 240 }, { 192, 208, 288 }, { 192, 209, 288 }, { 224, 243, 336 }, { 224, 244, 336 }, { 256, 278, 384 }, { 256, 279, 384 }, { 320, 348, 480 }, { 320, 349, 480 }, { 384, 417, 576 }, { 384, 418, 576 }, { 448, 487, 672 }, { 448, 488, 672 }, { 512, 557, 768 }, { 512, 558, 768 }, { 640, 696, 960 }, { 640, 697, 960 }, { 768, 835, 1152 }, { 768, 836, 1152 }, { 896, 975, 1344 }, { 896, 976, 1344 }, { 1024, 1114, 1536 }, { 1024, 1115, 1536 }, { 1152, 1253, 1728 }, { 1152, 1254, 1728 }, { 1280, 1393, 1920 }, { 1280, 1394, 1920 }, }; const uint8_t EAC3Blocks[4] = { 1, 2, 3, 6 }; typedef enum { EAC3_FRAME_TYPE_INDEPENDENT = 0, EAC3_FRAME_TYPE_DEPENDENT, EAC3_FRAME_TYPE_AC3_CONVERT, EAC3_FRAME_TYPE_RESERVED } EAC3FrameType; cParserAC3::cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_PTS = 0; m_DTS = 0; m_FrameSize = 0; m_SampleRate = 0; m_Channels = 0; m_BitRate = 0; m_PesBufferInitialSize = 1920*2; } cParserAC3::~cParserAC3() { } void cParserAC3::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { int p = m_PesParserPtr; int l; while ((l = m_PesBufferPtr - p) > 8) { if (FindHeaders(m_PesBuffer + p, l) < 0) break; p++; } m_PesParserPtr = p; if (m_FoundFrame && l >= m_FrameSize) { bool streamChange = m_Stream->SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0); pkt->id = m_pID; pkt->data = &m_PesBuffer[p]; pkt->size = m_FrameSize; pkt->duration = 90000 * 1536 / m_SampleRate; pkt->dts = m_DTS; pkt->pts = m_PTS; pkt->streamChange = streamChange; m_PesNextFramePtr = p + m_FrameSize; m_PesParserPtr = 0; m_FoundFrame = false; } } int cParserAC3::FindHeaders(uint8_t *buf, int buf_size) { if (m_FoundFrame) return -1; if (buf_size < 9) return -1; uint8_t *buf_ptr = buf; if ((buf_ptr[0] == 0x0b && buf_ptr[1] == 0x77)) { cBitstream bs(buf_ptr + 2, AC3_HEADER_SIZE * 8); // read ahead to bsid to distinguish between AC-3 and E-AC-3 int bsid = bs.showBits(29) & 0x1F; if (bsid > 16) return 0; if (bsid <= 10) { // Normal AC-3 bs.skipBits(16); int fscod = bs.readBits(2); int frmsizecod = bs.readBits(6); bs.skipBits(5); // skip bsid, already got it bs.skipBits(3); // skip bitstream mode int acmod = bs.readBits(3); if (fscod == 3 || frmsizecod > 37) return 0; if (acmod == AC3_CHMODE_STEREO) { bs.skipBits(2); // skip dsurmod } else { if ((acmod & 1) && acmod != AC3_CHMODE_MONO) bs.skipBits(2); if (acmod & 4) bs.skipBits(2); } int lfeon = bs.readBits(1); int srShift = max(bsid, 8) - 8; m_SampleRate = AC3SampleRateTable[fscod] >> srShift; m_BitRate = (AC3BitrateTable[frmsizecod>>1] * 1000) >> srShift; m_Channels = AC3ChannelsTable[acmod] + lfeon; m_FrameSize = AC3FrameSizeTable[frmsizecod][fscod] * 2; } else { // Enhanced AC-3 int frametype = bs.readBits(2); if (frametype == EAC3_FRAME_TYPE_RESERVED) return 0; bs.readBits(3); // int substreamid m_FrameSize = (bs.readBits(11) + 1) << 1; if (m_FrameSize < AC3_HEADER_SIZE) return 0; int numBlocks = 6; int sr_code = bs.readBits(2); if (sr_code == 3) { int sr_code2 = bs.readBits(2); if (sr_code2 == 3) return 0; m_SampleRate = AC3SampleRateTable[sr_code2] / 2; } else { numBlocks = EAC3Blocks[bs.readBits(2)]; m_SampleRate = AC3SampleRateTable[sr_code]; } int channelMode = bs.readBits(3); int lfeon = bs.readBits(1); m_BitRate = (uint32_t)(8.0 * m_FrameSize * m_SampleRate / (numBlocks * 256.0)); m_Channels = AC3ChannelsTable[channelMode] + lfeon; } m_FoundFrame = true; m_DTS = m_curPTS; m_PTS = m_curPTS; m_curPTS += 90000 * 1536 / m_SampleRate; return -1; } return 0; } void cParserAC3::Reset() { cParser::Reset(); } vdr-plugin-vnsiserver-1.3.1/parser_AC3.h000066400000000000000000000030301257700704000201000ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_AC3_H #define VNSI_DEMUXER_AC3_H #include "parser.h" // --- cParserAC3 ------------------------------------------------- class cParserAC3 : public cParser { private: int m_SampleRate; int m_Channels; int m_BitRate; int m_FrameSize; int64_t m_PTS; /* pts of the current frame */ int64_t m_DTS; /* dts of the current frame */ int FindHeaders(uint8_t *buf, int buf_size); public: cParserAC3(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserAC3(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); virtual void Reset(); }; #endif // VNSI_DEMUXER_AC3_H vdr-plugin-vnsiserver-1.3.1/parser_DTS.c000066400000000000000000000022561257700704000201700ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "parser_DTS.h" #include "bitstream.h" cParserDTS::cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { } cParserDTS::~cParserDTS() { } void cParserDTS::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { } vdr-plugin-vnsiserver-1.3.1/parser_DTS.h000066400000000000000000000023371257700704000201750ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_DTS_H #define VNSI_DEMUXER_DTS_H #include "parser.h" // --- cParserDTS ------------------------------------------------- class cParserDTS : public cParser { private: public: cParserDTS(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserDTS(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); }; #endif // VNSI_DEMUXER_DTS_H vdr-plugin-vnsiserver-1.3.1/parser_MPEGAudio.c000066400000000000000000000151271257700704000212510ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "parser_MPEGAudio.h" #include "bitstream.h" #define MAX_RDS_BUFFER_SIZE 100000 const uint16_t FrequencyTable[3] = { 44100, 48000, 32000 }; const uint16_t BitrateTable[2][3][15] = { { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } }, { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160} } }; cParserMPEG2Audio::cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_PTS = 0; m_DTS = 0; m_FrameSize = 0; m_SampleRate = 0; m_Channels = 0; m_BitRate = 0; m_PesBufferInitialSize = 2048; m_RDSEnabled = enableRDS; m_RDSBufferInitialSize = 384; m_RDSBuffer = NULL; m_RDSBufferSize = 0; m_RDSExtPID = 0; } cParserMPEG2Audio::~cParserMPEG2Audio() { if (m_RDSBuffer) { delete m_RDSBuffer; m_RDSBuffer = NULL; } } void cParserMPEG2Audio::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { int p = m_PesParserPtr; int l; while ((l = m_PesBufferPtr - p) > 3) { if (FindHeaders(m_PesBuffer + p, l) < 0) break; p++; } m_PesParserPtr = p; if (m_FoundFrame && l >= m_FrameSize) { bool streamChange = m_Stream->SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0); pkt->id = m_pID; pkt->data = &m_PesBuffer[p]; pkt->size = m_FrameSize; pkt->duration = 90000 * 1152 / m_SampleRate; pkt->dts = m_DTS; pkt->pts = m_PTS; pkt->streamChange = streamChange; m_PesNextFramePtr = p + m_FrameSize; m_PesParserPtr = 0; m_FoundFrame = false; if (m_RDSEnabled) { /* * Reading of RDS Universal Encoder Communication Protocol * If present it starts on end of a mpeg audio stream and goes * backwards. * See ETSI TS 101 154 - C.4.2.18 for details. */ int rdsl = m_PesBuffer[p+m_FrameSize-2]; // RDS DataFieldLength if (m_PesBuffer[p+m_FrameSize-1] == 0xfd && rdsl > 0) // RDS DataSync = 0xfd @ end { if (m_RDSBuffer == NULL) { m_RDSBufferSize = m_RDSBufferInitialSize; m_RDSBuffer = (uint8_t*)malloc(m_RDSBufferSize); if (m_RDSBuffer == NULL) { ERRORLOG("PVR Parser MPEG2-Audio - %s - malloc failed for RDS data", __FUNCTION__); m_RDSEnabled = false; return; } m_RDSExtPID = m_Stream->AddSideDataType(scRDS); if (!m_RDSExtPID) { ERRORLOG("PVR Parser MPEG2-Audio - %s - failed to add RDS data stream", __FUNCTION__); m_RDSEnabled = false; return; } } if (rdsl >= m_RDSBufferSize) { if (rdsl >= MAX_RDS_BUFFER_SIZE) { ERRORLOG("PVR Parser MPEG2-Audio - %s - max buffer size (%i kB) for RDS data reached, pid: %d", __FUNCTION__, MAX_RDS_BUFFER_SIZE/1024, m_pID); m_RDSEnabled = false; return; } m_RDSBufferSize += m_RDSBufferInitialSize / 10; m_RDSBuffer = (uint8_t*)realloc(m_RDSBuffer, m_RDSBufferSize); if (m_RDSBuffer == NULL) { ERRORLOG("PVR Parser MPEG2-Audio - %s - realloc for RDS data failed", __FUNCTION__); m_RDSEnabled = false; return; } } int pes_buffer_ptr = 0; for (int i = m_FrameSize-3; i > m_FrameSize-3-rdsl; i--) // <-- data reverse, from end to start m_RDSBuffer[pes_buffer_ptr++] = m_PesBuffer[p+i]; pkt_side_data->id = m_RDSExtPID; pkt_side_data->data = m_RDSBuffer; pkt_side_data->size = pes_buffer_ptr; pkt_side_data->duration = 0; pkt_side_data->dts = m_curDTS; pkt_side_data->pts = m_curPTS; pkt_side_data->streamChange = false; } } } } int cParserMPEG2Audio::FindHeaders(uint8_t *buf, int buf_size) { if (m_FoundFrame) return -1; if (buf_size < 4) return -1; uint8_t *buf_ptr = buf; if ((buf_ptr[0] == 0xFF && (buf_ptr[1] & 0xE0) == 0xE0)) { cBitstream bs(buf_ptr, 4 * 8); bs.skipBits(11); // syncword int audioVersion = bs.readBits(2); if (audioVersion == 1) return 0; int mpeg2 = !(audioVersion & 1); int mpeg25 = !(audioVersion & 3); int layer = bs.readBits(2); if (layer == 0) return 0; layer = 4 - layer; bs.skipBits(1); // protetion bit int bitrate_index = bs.readBits(4); if (bitrate_index == 15 || bitrate_index == 0) return 0; m_BitRate = BitrateTable[mpeg2][layer - 1][bitrate_index] * 1000; int sample_rate_index = bs.readBits(2); if (sample_rate_index == 3) return 0; m_SampleRate = FrequencyTable[sample_rate_index] >> (mpeg2 + mpeg25); int padding = bs.readBits1(); bs.skipBits(1); // private bit int channel_mode = bs.readBits(2); if (channel_mode == 11) m_Channels = 1; else m_Channels = 2; if (layer == 1) m_FrameSize = (12 * m_BitRate / m_SampleRate + padding) * 4; else m_FrameSize = 144 * m_BitRate / m_SampleRate + padding; m_FoundFrame = true; m_DTS = m_curPTS; m_PTS = m_curPTS; m_curPTS += 90000 * 1152 / m_SampleRate; return -1; } return 0; } vdr-plugin-vnsiserver-1.3.1/parser_MPEGAudio.h000066400000000000000000000032301257700704000212460ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_MPEGAUDIO_H #define VNSI_DEMUXER_MPEGAUDIO_H #include "parser.h" // --- cParserMPEG2Audio ------------------------------------------------- class cParserMPEG2Audio : public cParser { private: int m_SampleRate; int m_Channels; int m_BitRate; int m_FrameSize; int64_t m_PTS; int64_t m_DTS; bool m_RDSEnabled; uint32_t m_RDSExtPID; uint8_t *m_RDSBuffer; int m_RDSBufferSize; int m_RDSBufferPtr; size_t m_RDSBufferInitialSize; int FindHeaders(uint8_t *buf, int buf_size); public: cParserMPEG2Audio(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps, bool enableRDS); virtual ~cParserMPEG2Audio(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); }; #endif // VNSI_DEMUXER_MPEGAUDIO_H vdr-plugin-vnsiserver-1.3.1/parser_MPEGVideo.c000066400000000000000000000140441257700704000212530ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "bitstream.h" #include "parser_MPEGVideo.h" using namespace std; #define MPEG_PICTURE_START 0x00000100 #define MPEG_SEQUENCE_START 0x000001b3 #define MPEG_SEQUENCE_EXTENSION 0x000001b5 #define MPEG_SLICE_S 0x00000101 #define MPEG_SLICE_E 0x000001af /** * MPEG2VIDEO frame duration table (in 90kHz clock domain) */ const unsigned int mpeg2video_framedurations[16] = { 0, 3753, 3750, 3600, 3003, 3000, 1800, 1501, 1500, }; cParserMPEG2Video::cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_FrameDuration = 0; m_vbvDelay = -1; m_vbvSize = 0; m_Height = 0; m_Width = 0; m_Dar = 0.0; m_FpsScale = 0; m_PesBufferInitialSize = 80000; m_IsVideo = true; Reset(); } cParserMPEG2Video::~cParserMPEG2Video() { } void cParserMPEG2Video::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { if (m_PesBufferPtr < 4) return; int p = m_PesParserPtr; uint32_t startcode = m_StartCode; bool frameComplete = false; int l; while ((l = m_PesBufferPtr - p) > 3) { if ((startcode & 0xffffff00) == 0x00000100) { if (Parse_MPEG2Video(startcode, p, frameComplete) < 0) { break; } } startcode = startcode << 8 | m_PesBuffer[p++]; } m_PesParserPtr = p; m_StartCode = startcode; if (frameComplete) { if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid) { if (m_FpsScale == 0) { if (m_FrameDuration != DVD_NOPTS_VALUE) m_FpsScale = m_Stream->Rescale(m_FrameDuration, DVD_TIME_BASE, 90000); else m_FpsScale = 40000; } bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, m_Dar); pkt->id = m_pID; pkt->size = m_PesNextFramePtr; pkt->data = m_PesBuffer; pkt->dts = m_DTS; pkt->pts = m_PTS; pkt->duration = m_FrameDuration; pkt->streamChange = streamChange; } m_StartCode = 0xffffffff; m_PesParserPtr = 0; m_FoundFrame = false; m_FrameValid = true; } } void cParserMPEG2Video::Reset() { cParser::Reset(); m_StartCode = 0xffffffff; m_NeedIFrame = true; m_NeedSPS = true; } int cParserMPEG2Video::Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete) { int len = m_PesBufferPtr - buf_ptr; uint8_t *buf = m_PesBuffer + buf_ptr; switch (startcode & 0xFF) { case 0: // picture start { if (m_NeedSPS) { m_FoundFrame = true; return 0; } if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } if (len < 4) return -1; if (!Parse_MPEG2Video_PicStart(buf)) return 0; if (!m_FoundFrame) { m_AuPrevDTS = m_AuDTS; if (buf_ptr - 4 >= m_PesTimePos) { m_AuDTS = m_curDTS != DVD_NOPTS_VALUE ? m_curDTS : m_curPTS; m_AuPTS = m_curPTS; } else { m_AuDTS = m_prevDTS != DVD_NOPTS_VALUE ? m_prevDTS : m_prevPTS;; m_AuPTS = m_prevPTS; } } if (m_AuPrevDTS == m_AuDTS) { m_DTS = m_AuDTS + m_PicNumber*m_FrameDuration; m_PTS = m_AuPTS + (m_TemporalReference-m_TrLastTime)*m_FrameDuration; } else { m_PTS = m_AuPTS; m_DTS = m_AuDTS; m_PicNumber = 0; m_TrLastTime = m_TemporalReference; } m_PicNumber++; m_FoundFrame = true; break; } case 0xb3: // Sequence start code { if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } if (len < 8) return -1; if (!Parse_MPEG2Video_SeqStart(buf)) return 0; break; } case 0xb7: // sequence end { if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr; return -1; } break; } default: break; } return 0; } bool cParserMPEG2Video::Parse_MPEG2Video_SeqStart(uint8_t *buf) { cBitstream bs(buf, 8 * 8); m_Width = bs.readBits(12); m_Height = bs.readBits(12); // figure out Display Aspect Ratio uint8_t aspect = bs.readBits(4); switch(aspect) { case 1: m_Dar = 1.0; break; case 2: m_Dar = 4.0/3.0; break; case 3: m_Dar = 16.0/9.0; break; case 4: m_Dar = 2.21; break; default: ERRORLOG("invalid / forbidden DAR in sequence header !"); return false; } m_FrameDuration = mpeg2video_framedurations[bs.readBits(4)]; bs.skipBits(18); bs.skipBits(1); m_vbvSize = bs.readBits(10) * 16 * 1024 / 8; m_NeedSPS = false; return true; } bool cParserMPEG2Video::Parse_MPEG2Video_PicStart(uint8_t *buf) { cBitstream bs(buf, 4 * 8); m_TemporalReference = bs.readBits(10); /* temporal reference */ int pct = bs.readBits(3); if (pct < PKT_I_FRAME || pct > PKT_B_FRAME) return true; /* Illegal picture_coding_type */ if (pct == PKT_I_FRAME) m_NeedIFrame = false; int vbvDelay = bs.readBits(16); /* vbv_delay */ if (vbvDelay == 0xffff) m_vbvDelay = -1; else m_vbvDelay = vbvDelay; return true; } vdr-plugin-vnsiserver-1.3.1/parser_MPEGVideo.h000066400000000000000000000040431257700704000212560ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_MPEGVIDEO_H #define VNSI_DEMUXER_MPEGVIDEO_H #include #include "parser.h" class cBitstream; // --- cParserMPEG2Video ------------------------------------------------- class cParserMPEG2Video : public cParser { private: uint32_t m_StartCode; bool m_NeedIFrame; bool m_NeedSPS; int m_FrameDuration; int m_vbvDelay; /* -1 if CBR */ int m_vbvSize; /* Video buffer size (in bytes) */ int m_Width; int m_Height; float m_Dar; int64_t m_DTS; int64_t m_PTS; int64_t m_AuDTS, m_AuPTS, m_AuPrevDTS; int m_TemporalReference; int m_TrLastTime; int m_PicNumber; int m_FpsScale; int Parse_MPEG2Video(uint32_t startcode, int buf_ptr, bool &complete); bool Parse_MPEG2Video_SeqStart(uint8_t *buf); bool Parse_MPEG2Video_PicStart(uint8_t *buf); public: cParserMPEG2Video(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserMPEG2Video(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); virtual void Reset(); }; #endif // VNSI_DEMUXER_MPEGVIDEO_H vdr-plugin-vnsiserver-1.3.1/parser_Subtitle.c000066400000000000000000000032401257700704000213230ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "parser_Subtitle.h" cParserSubtitle::cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_PesBufferInitialSize = 4000; } cParserSubtitle::~cParserSubtitle() { } void cParserSubtitle::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { int l = m_PesBufferPtr; if (l >= m_PesPacketLength) { if (l < 2 || m_PesBuffer[0] != 0x20 || m_PesBuffer[1] != 0x00) { Reset(); return; } if(m_PesBuffer[m_PesPacketLength-1] == 0xff) { pkt->id = m_pID; pkt->data = m_PesBuffer+2; pkt->size = m_PesPacketLength-3; pkt->duration = 0; pkt->dts = m_curDTS; pkt->pts = m_curPTS; } m_PesBufferPtr = 0; } } vdr-plugin-vnsiserver-1.3.1/parser_Subtitle.h000066400000000000000000000023701257700704000213330ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_SUBTITLE_H #define VNSI_DEMUXER_SUBTITLE_H #include "parser.h" // --- cParserSubtitle ------------------------------------------------- class cParserSubtitle : public cParser { public: cParserSubtitle(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserSubtitle(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); }; #endif // VNSI_DEMUXER_SUBTITLE_H vdr-plugin-vnsiserver-1.3.1/parser_Teletext.c000066400000000000000000000031071257700704000213300ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include "config.h" #include "parser_Teletext.h" cParserTeletext::cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_PesBufferInitialSize = 4000; } cParserTeletext::~cParserTeletext() { } void cParserTeletext::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { int l = m_PesBufferPtr; if (l < 1) return; if (m_PesBuffer[0] < 0x10 || m_PesBuffer[0] > 0x1F) { Reset(); return; } if (l >= m_PesPacketLength) { pkt->id = m_pID; pkt->data = m_PesBuffer; pkt->size = m_PesPacketLength; pkt->duration = 0; pkt->dts = m_curDTS; pkt->pts = m_curPTS; m_PesBufferPtr = 0; } } vdr-plugin-vnsiserver-1.3.1/parser_Teletext.h000066400000000000000000000024631257700704000213410ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_TELETEXT_H #define VNSI_DEMUXER_TELETEXT_H #include "parser.h" // --- cParserTeletext ------------------------------------------------- class cParserTeletext : public cParser { private: int64_t m_lastDTS; int64_t m_lastPTS; public: cParserTeletext(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserTeletext(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); }; #endif // VNSI_DEMUXER_TELETEXT_H vdr-plugin-vnsiserver-1.3.1/parser_h264.c000066400000000000000000000400421257700704000202140ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "config.h" #include "bitstream.h" #include "parser_h264.h" static const int h264_lev2cpbsize[][2] = { {10, 175}, {11, 500}, {12, 1000}, {13, 2000}, {20, 2000}, {21, 4000}, {22, 4000}, {30, 10000}, {31, 14000}, {32, 20000}, {40, 25000}, {41, 62500}, {42, 62500}, {50, 135000}, {51, 240000}, {-1, -1}, }; cParserH264::cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps) : cParser(pID, stream, ptsWrap, observePtsWraps) { m_Height = 0; m_Width = 0; m_FPS = 25; m_FpsScale = 0; m_FrameDuration = 0; m_vbvDelay = -1; m_vbvSize = 0; m_PixelAspect.den = 1; m_PixelAspect.num = 0; memset(&m_streamData, 0, sizeof(m_streamData)); m_PesBufferInitialSize = 240000; m_IsVideo = true; Reset(); } cParserH264::~cParserH264() { } void cParserH264::Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data) { if (m_PesBufferPtr < 4) return; int p = m_PesParserPtr; uint32_t startcode = m_StartCode; bool frameComplete = false; int l; while ((l = m_PesBufferPtr - p) > 3) { if ((startcode & 0xffffff00) == 0x00000100) { if (Parse_H264(startcode, p, frameComplete) < 0) { break; } } startcode = startcode << 8 | m_PesBuffer[p++]; } m_PesParserPtr = p; m_StartCode = startcode; if (frameComplete) { if (!m_NeedSPS && !m_NeedIFrame && m_FrameValid) { double PAR = (double)m_PixelAspect.num/(double)m_PixelAspect.den; double DAR = (PAR * m_Width) / m_Height; DEBUGLOG("H.264 SPS: PAR %i:%i", m_PixelAspect.num, m_PixelAspect.den); DEBUGLOG("H.264 SPS: DAR %.2f", DAR); int duration; if (m_curDTS != DVD_NOPTS_VALUE && m_prevDTS != DVD_NOPTS_VALUE && m_curDTS > m_prevDTS) duration = m_curDTS - m_prevDTS; else duration = m_Stream->Rescale(20000, 90000, DVD_TIME_BASE); if (m_FpsScale == 0) { m_FpsScale = m_Stream->Rescale(duration, DVD_TIME_BASE, 90000); } bool streamChange = m_Stream->SetVideoInformation(m_FpsScale, DVD_TIME_BASE, m_Height, m_Width, DAR); pkt->id = m_pID; pkt->size = m_PesNextFramePtr; pkt->data = m_PesBuffer; pkt->dts = m_DTS; pkt->pts = m_PTS; pkt->duration = duration; pkt->streamChange = streamChange; } m_StartCode = 0xffffffff; m_PesParserPtr = 0; m_FoundFrame = false; m_FrameValid = true; } } void cParserH264::Reset() { cParser::Reset(); m_StartCode = 0xffffffff; m_NeedIFrame = true; m_NeedSPS = true; m_NeedPPS = true; memset(&m_streamData, 0, sizeof(m_streamData)); } int cParserH264::Parse_H264(uint32_t startcode, int buf_ptr, bool &complete) { int len = m_PesBufferPtr - buf_ptr; uint8_t *buf = m_PesBuffer + buf_ptr; switch(startcode & 0x9f) { case 1 ... 5: { if (m_NeedSPS || m_NeedPPS) { m_FoundFrame = true; return 0; } // need at least 32 bytes for parsing nal if (len < 32) return -1; h264_private::VCL_NAL vcl; memset(&vcl, 0, sizeof(h264_private::VCL_NAL)); vcl.nal_ref_idc = startcode & 0x60; vcl.nal_unit_type = startcode & 0x1F; if (!Parse_SLH(buf, len, vcl)) return 0; // check for the beginning of a new access unit if (m_FoundFrame && IsFirstVclNal(vcl)) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } if (!m_FoundFrame) { if (buf_ptr - 4 >= m_PesTimePos) { m_DTS = m_curDTS; m_PTS = m_curPTS; } else { m_DTS = m_prevDTS; m_PTS = m_prevPTS; } } m_streamData.vcl_nal = vcl; m_FoundFrame = true; break; } case NAL_SEI: if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } break; case NAL_SPS: { if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } // TODO: how big is SPS? if (len < 256) return -1; if (!Parse_SPS(buf, len)) return 0; m_NeedSPS = false; break; } case NAL_PPS: { if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } // TODO: how big is PPS if (len < 64) return -1; if (!Parse_PPS(buf, len)) return 0; m_NeedPPS = false; break; } case NAL_AUD: if (m_FoundFrame && (m_prevPTS != DVD_NOPTS_VALUE)) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } break; case NAL_END_SEQ: if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr; return -1; } break; case 13 ... 18: if (m_FoundFrame) { complete = true; m_PesNextFramePtr = buf_ptr - 4; return -1; } break; default: break; } return 0; } bool cParserH264::Parse_PPS(uint8_t *buf, int len) { cBitstream bs(buf, len*8); int pps_id = bs.readGolombUE(); int sps_id = bs.readGolombUE(); m_streamData.pps[pps_id].sps = sps_id; bs.readBits1(); m_streamData.pps[pps_id].pic_order_present_flag = bs.readBits1(); return true; } bool cParserH264::Parse_SLH(uint8_t *buf, int len, h264_private::VCL_NAL &vcl) { cBitstream bs(buf, len*8); bs.readGolombUE(); /* first_mb_in_slice */ int slice_type = bs.readGolombUE(); if (slice_type > 4) slice_type -= 5; /* Fixed slice type per frame */ switch (slice_type) { case 0: break; case 1: break; case 2: m_NeedIFrame = false; break; default: return false; } int pps_id = bs.readGolombUE(); int sps_id = m_streamData.pps[pps_id].sps; if (m_streamData.sps[sps_id].cbpsize == 0) return false; m_vbvSize = m_streamData.sps[sps_id].cbpsize; m_vbvDelay = -1; vcl.pic_parameter_set_id = pps_id; vcl.frame_num = bs.readBits(m_streamData.sps[sps_id].log2_max_frame_num); if (!m_streamData.sps[sps_id].frame_mbs_only_flag) { vcl.field_pic_flag = bs.readBits1(); // interlaced // if (vcl.field_pic_flag) // m_FPS *= 2; } if (vcl.field_pic_flag) vcl.bottom_field_flag = bs.readBits1(); if (vcl.nal_unit_type == 5) vcl.idr_pic_id = bs.readGolombUE(); if (m_streamData.sps[sps_id].pic_order_cnt_type == 0) { vcl.pic_order_cnt_lsb = bs.readBits(m_streamData.sps[sps_id].log2_max_pic_order_cnt_lsb); if(m_streamData.pps[pps_id].pic_order_present_flag && !vcl.field_pic_flag) vcl.delta_pic_order_cnt_bottom = bs.readGolombSE(); } if(m_streamData.sps[sps_id].pic_order_cnt_type == 1 && !m_streamData.sps[sps_id].delta_pic_order_always_zero_flag ) { vcl.delta_pic_order_cnt_0 = bs.readGolombSE(); if(m_streamData.pps[pps_id].pic_order_present_flag && !vcl.field_pic_flag ) vcl.delta_pic_order_cnt_1 = bs.readGolombSE(); } vcl.pic_order_cnt_type = m_streamData.sps[sps_id].pic_order_cnt_type; return true; } bool cParserH264::Parse_SPS(uint8_t *buf, int len) { cBitstream bs(buf, len*8); unsigned int tmp, frame_mbs_only; int cbpsize = -1; int profile_idc = bs.readBits(8); /* constraint_set0_flag = bs.readBits1(); */ /* constraint_set1_flag = bs.readBits1(); */ /* constraint_set2_flag = bs.readBits1(); */ /* constraint_set3_flag = bs.readBits1(); */ /* reserved = bs.readBits(4); */ bs.skipBits(8); int level_idc = bs.readBits(8); unsigned int seq_parameter_set_id = bs.readGolombUE(9); unsigned int i = 0; while (h264_lev2cpbsize[i][0] != -1) { if (h264_lev2cpbsize[i][0] >= level_idc) { cbpsize = h264_lev2cpbsize[i][1]; break; } i++; } if (cbpsize < 0) return false; memset(&m_streamData.sps[seq_parameter_set_id], 0, sizeof(h264_private::SPS)); m_streamData.sps[seq_parameter_set_id].cbpsize = cbpsize * 125; /* Convert from kbit to bytes */ if( profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118 || profile_idc == 128 ) { int chroma_format_idc = bs.readGolombUE(9); /* chroma_format_idc */ if(chroma_format_idc == 3) bs.skipBits(1); /* residual_colour_transform_flag */ bs.readGolombUE(); /* bit_depth_luma - 8 */ bs.readGolombUE(); /* bit_depth_chroma - 8 */ bs.skipBits(1); /* transform_bypass */ if (bs.readBits1()) /* seq_scaling_matrix_present */ { for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) { if (bs.readBits1()) /* seq_scaling_list_present */ { int last = 8, next = 8, size = (i<6) ? 16 : 64; for (int j = 0; j < size; j++) { if (next) next = (last + bs.readGolombSE()) & 0xff; last = !next ? last: next; } } } } } int log2_max_frame_num_minus4 = bs.readGolombUE(); /* log2_max_frame_num - 4 */ m_streamData.sps[seq_parameter_set_id].log2_max_frame_num = log2_max_frame_num_minus4 + 4; int pic_order_cnt_type = bs.readGolombUE(9); m_streamData.sps[seq_parameter_set_id].pic_order_cnt_type = pic_order_cnt_type; if (pic_order_cnt_type == 0) { int log2_max_pic_order_cnt_lsb_minus4 = bs.readGolombUE(); /* log2_max_poc_lsb - 4 */ m_streamData.sps[seq_parameter_set_id].log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4; } else if (pic_order_cnt_type == 1) { m_streamData.sps[seq_parameter_set_id].delta_pic_order_always_zero_flag = bs.readBits1(); bs.readGolombSE(); /* offset_for_non_ref_pic */ bs.readGolombSE(); /* offset_for_top_to_bottom_field */ tmp = bs.readGolombUE(); /* num_ref_frames_in_pic_order_cnt_cycle */ for (unsigned int i = 0; i < tmp; i++) bs.readGolombSE(); /* offset_for_ref_frame[i] */ } else if(pic_order_cnt_type != 2) { /* Illegal poc */ return false; } bs.readGolombUE(9); /* ref_frames */ bs.skipBits(1); /* gaps_in_frame_num_allowed */ m_Width /* mbs */ = bs.readGolombUE() + 1; m_Height /* mbs */ = bs.readGolombUE() + 1; frame_mbs_only = bs.readBits1(); m_streamData.sps[seq_parameter_set_id].frame_mbs_only_flag = frame_mbs_only; DEBUGLOG("H.264 SPS: pic_width: %u mbs", (unsigned) m_Width); DEBUGLOG("H.264 SPS: pic_height: %u mbs", (unsigned) m_Height); DEBUGLOG("H.264 SPS: frame only flag: %d", frame_mbs_only); m_Width *= 16; m_Height *= 16 * (2-frame_mbs_only); if (!frame_mbs_only) { if (bs.readBits1()) /* mb_adaptive_frame_field_flag */ DEBUGLOG("H.264 SPS: MBAFF"); } bs.skipBits(1); /* direct_8x8_inference_flag */ if (bs.readBits1()) /* frame_cropping_flag */ { uint32_t crop_left = bs.readGolombUE(); uint32_t crop_right = bs.readGolombUE(); uint32_t crop_top = bs.readGolombUE(); uint32_t crop_bottom = bs.readGolombUE(); DEBUGLOG("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom); m_Width -= 2*(crop_left + crop_right); if (frame_mbs_only) m_Height -= 2*(crop_top + crop_bottom); else m_Height -= 4*(crop_top + crop_bottom); } /* VUI parameters */ m_PixelAspect.num = 0; if (bs.readBits1()) /* vui_parameters_present flag */ { if (bs.readBits1()) /* aspect_ratio_info_present */ { uint32_t aspect_ratio_idc = bs.readBits(8); DEBUGLOG("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc); if (aspect_ratio_idc == 255 /* Extended_SAR */) { m_PixelAspect.num = bs.readBits(16); /* sar_width */ m_PixelAspect.den = bs.readBits(16); /* sar_height */ DEBUGLOG("H.264 SPS: -> sar %dx%d", m_PixelAspect.num, m_PixelAspect.den); } else { static const mpeg_rational_t aspect_ratios[] = { /* page 213: */ /* 0: unknown */ {0, 1}, /* 1...16: */ { 1, 1}, {12, 11}, {10, 11}, {16, 11}, { 40, 33}, {24, 11}, {20, 11}, {32, 11}, {80, 33}, {18, 11}, {15, 11}, {64, 33}, {160, 99}, { 4, 3}, { 3, 2}, { 2, 1} }; if (aspect_ratio_idc < sizeof(aspect_ratios)/sizeof(aspect_ratios[0])) { memcpy(&m_PixelAspect, &aspect_ratios[aspect_ratio_idc], sizeof(mpeg_rational_t)); DEBUGLOG("H.264 SPS: PAR %d / %d", m_PixelAspect.num, m_PixelAspect.den); } else { DEBUGLOG("H.264 SPS: aspect_ratio_idc out of range !"); } } } if (bs.readBits1()) // overscan { bs.readBits1(); // overscan_appropriate_flag } if (bs.readBits1()) // video_signal_type_present_flag { bs.readBits(3); // video_format bs.readBits1(); // video_full_range_flag if (bs.readBits1()) // colour_description_present_flag { bs.readBits(8); // colour_primaries bs.readBits(8); // transfer_characteristics bs.readBits(8); // matrix_coefficients } } if (bs.readBits1()) // chroma_loc_info_present_flag { bs.readGolombUE(); // chroma_sample_loc_type_top_field bs.readGolombUE(); // chroma_sample_loc_type_bottom_field } if (bs.readBits1()) // timing_info_present_flag { // uint32_t num_units_in_tick = bs.readBits(32); // uint32_t time_scale = bs.readBits(32); // int fixed_frame_rate = bs.readBits1(); // if (num_units_in_tick > 0) // m_FPS = time_scale / (num_units_in_tick * 2); } } DEBUGLOG("H.264 SPS: -> video size %dx%d, aspect %d:%d", m_Width, m_Height, m_PixelAspect.num, m_PixelAspect.den); return true; } bool cParserH264::IsFirstVclNal(h264_private::VCL_NAL &vcl) { if (m_streamData.vcl_nal.frame_num != vcl.frame_num) return true; if (m_streamData.vcl_nal.pic_parameter_set_id != vcl.pic_parameter_set_id) return true; if (m_streamData.vcl_nal.field_pic_flag != vcl.field_pic_flag) return true; if (m_streamData.vcl_nal.field_pic_flag && vcl.field_pic_flag) { if (m_streamData.vcl_nal.bottom_field_flag != vcl.bottom_field_flag) return true; } if (m_streamData.vcl_nal.nal_ref_idc == 0 || vcl.nal_ref_idc == 0) { if (m_streamData.vcl_nal.nal_ref_idc != vcl.nal_ref_idc) return true; } if (m_streamData.vcl_nal.pic_order_cnt_type == 0 && vcl.pic_order_cnt_type == 0) { if (m_streamData.vcl_nal.pic_order_cnt_lsb != vcl.pic_order_cnt_lsb) return true; if (m_streamData.vcl_nal.delta_pic_order_cnt_bottom != vcl.delta_pic_order_cnt_bottom) return true; } if (m_streamData.vcl_nal.pic_order_cnt_type == 1 && vcl.pic_order_cnt_type == 1) { if (m_streamData.vcl_nal.delta_pic_order_cnt_0 != vcl.delta_pic_order_cnt_0) return true; if (m_streamData.vcl_nal.delta_pic_order_cnt_1 != vcl.delta_pic_order_cnt_1) return true; } if (m_streamData.vcl_nal.nal_unit_type == 5 || vcl.nal_unit_type == 5) { if (m_streamData.vcl_nal.nal_unit_type != vcl.nal_unit_type) return true; } if (m_streamData.vcl_nal.nal_unit_type == 5 && vcl.nal_unit_type == 5) { if (m_streamData.vcl_nal.idr_pic_id != vcl.idr_pic_id) return true; } return false; } vdr-plugin-vnsiserver-1.3.1/parser_h264.h000066400000000000000000000064231257700704000202260ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_DEMUXER_H264_H #define VNSI_DEMUXER_H264_H #include "parser.h" class cBitstream; // --- cParserH264 ------------------------------------------------- class cParserH264 : public cParser { private: typedef struct h264_private { struct SPS { int frame_duration; int cbpsize; int pic_order_cnt_type; int frame_mbs_only_flag; int log2_max_frame_num; int log2_max_pic_order_cnt_lsb; int delta_pic_order_always_zero_flag; } sps[256]; struct PPS { int sps; int pic_order_present_flag; } pps[256]; struct VCL_NAL { int frame_num; // slice int pic_parameter_set_id; // slice int field_pic_flag; // slice int bottom_field_flag; // slice int delta_pic_order_cnt_bottom; // slice int delta_pic_order_cnt_0; // slice int delta_pic_order_cnt_1; // slice int pic_order_cnt_lsb; // slice int idr_pic_id; // slice int nal_unit_type; int nal_ref_idc; // start code int pic_order_cnt_type; // sps } vcl_nal; } h264_private_t; typedef struct mpeg_rational_s { int num; int den; } mpeg_rational_t; enum { NAL_SLH = 0x01, // Slice Header NAL_SEI = 0x06, // Supplemental Enhancement Information NAL_SPS = 0x07, // Sequence Parameter Set NAL_PPS = 0x08, // Picture Parameter Set NAL_AUD = 0x09, // Access Unit Delimiter NAL_END_SEQ = 0x0A // End of Sequence }; uint32_t m_StartCode; bool m_NeedIFrame; bool m_NeedSPS; bool m_NeedPPS; int m_Width; int m_Height; int m_FPS; int m_FpsScale; mpeg_rational_t m_PixelAspect; int m_FrameDuration; h264_private m_streamData; int m_vbvDelay; /* -1 if CBR */ int m_vbvSize; /* Video buffer size (in bytes) */ int64_t m_DTS; int64_t m_PTS; int Parse_H264(uint32_t startcode, int buf_ptr, bool &complete); bool Parse_PPS(uint8_t *buf, int len); bool Parse_SLH(uint8_t *buf, int len, h264_private::VCL_NAL &vcl); bool Parse_SPS(uint8_t *buf, int len); bool IsFirstVclNal(h264_private::VCL_NAL &vcl); public: cParserH264(int pID, cTSStream *stream, sPtsWrap *ptsWrap, bool observePtsWraps); virtual ~cParserH264(); virtual void Parse(sStreamPacket *pkt, sStreamPacket *pkt_side_data); virtual void Reset(); }; #endif // VNSI_DEMUXER_H264_H vdr-plugin-vnsiserver-1.3.1/po/000077500000000000000000000000001257700704000164275ustar00rootroot00000000000000vdr-plugin-vnsiserver-1.3.1/po/de_DE.po000066400000000000000000000032671257700704000177370ustar00rootroot00000000000000# VDR VNSI plugin language source file. # Copyright (C) 2015 Alwin Esch # This file is distributed under the same license as the VDR package. # msgid "" msgstr "" "Project-Id-Version: VNSI-Server 1.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-04-04 14:32+0200\n" "PO-Revision-Date: 2015-01-23 21:46+0100\n" "Last-Translator: Alwin Esch\n" "Language-Team: German\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "Your scanner version doesnt support services - Please upgrade." msgstr "" msgid "Your scanner version is to old - Please upgrade." msgstr "" msgid "PMT Timeout (0-10)" msgstr "PMT Auszeit (0-10)" msgid "Off" msgstr "Aus" msgid "RAM" msgstr "RAM" msgid "File" msgstr "Datei" msgid "Time Shift Mode" msgstr "Time Shift Modus" msgid "TS Buffersize (RAM) (1-80) x 100MB" msgstr "TS Puffergröße (RAM) (1-80) x 100MB" msgid "TS Buffersize (File) (1-10) x 1GB" msgstr "TS Puffergröße (Datei) (1-10) x 1GB" msgid "TS Buffer Directory" msgstr "TS-Puffer-Verzeichnis" msgid "Play Recording instead of live" msgstr "Wiedergeben als Aufzeichnung statt Live" msgid "Avoid EPG scan while streaming" msgstr "Keine EPG suche während der Wiedergabe durchführen" msgid "Recording with the same name exists" msgstr "Aufnahme mit der selben größe existiert" msgid "Error while read last filenumber" msgstr "Fehler beim lesen der letzen Dateinummer" msgid "Error while accessing vdrfile" msgstr "Fehler beim zugriff auf VDR-Datei" msgid "Error while accessing indexfile" msgstr "Fehler beim Zugriff der Indexdatei" msgid "Deleted recording vanished" msgstr "Gelöschte Aufnahme verschwunden" vdr-plugin-vnsiserver-1.3.1/po/lt_LT.po000066400000000000000000000033351257700704000200110ustar00rootroot00000000000000# VDR VNSI plugin language source file. # Copyright (C) 2015 Alwin Esch # This file is distributed under the same license as the VDR package. # msgid "" msgstr "" "Project-Id-Version: VNSI-Server 1.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-04-04 14:32+0200\n" "PO-Revision-Date: 2015-02-11 22:30+0200\n" "Last-Translator: Valdemaras Pipiras\n" "Language-Team: Lithuanian\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "Your scanner version doesnt support services - Please upgrade." msgstr "" msgid "Your scanner version is to old - Please upgrade." msgstr "" msgid "PMT Timeout (0-10)" msgstr "PMT (0-10)" msgid "Off" msgstr "Išjungta" msgid "RAM" msgstr "RAM" msgid "File" msgstr "Failas" msgid "Time Shift Mode" msgstr "Atidėto žiūrėjimo (TS) būsena" msgid "TS Buffersize (RAM) (1-80) x 100MB" msgstr "TS buferio dydis (RAM) (1-80) x 100MB" msgid "TS Buffersize (File) (1-10) x 1GB" msgstr "TS buferio dydis (Failas) (1-10) x 1GB" msgid "TS Buffer Directory" msgstr "TS katalogas buferiavimui" msgid "Play Recording instead of live" msgstr "Groti įrašą vietoj gyvos transliacijos" msgid "Avoid EPG scan while streaming" msgstr "Vengti EPG skanavimo kol vyksta transliacija" msgid "Recording with the same name exists" msgstr "Jau yra įrašų tokiu pat pavadinimu" msgid "Error while read last filenumber" msgstr "Klaida nuskaitant paskutinių failų skaičių" msgid "Error while accessing vdrfile" msgstr "Klaida bandant atidaryti VDR failą" msgid "Error while accessing indexfile" msgstr "Klaida bandant atidaryti index failą" msgid "Deleted recording vanished" msgstr "Ištrintas įrašas galutinai išvalytas" vdr-plugin-vnsiserver-1.3.1/recordingscache.c000066400000000000000000000042421257700704000213020ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "config.h" #include "recordingscache.h" #include "vnsiclient.h" #include "hash.h" cRecordingsCache::cRecordingsCache() { } cRecordingsCache::~cRecordingsCache() { } cRecordingsCache& cRecordingsCache::GetInstance() { static cRecordingsCache singleton; return singleton; } uint32_t cRecordingsCache::Register(cRecording* recording, bool deleted) { cString filename = recording->FileName(); uint32_t uid = CreateStringHash(filename); m_mutex.Lock(); if(m_recordings.find(uid) == m_recordings.end()) { DEBUGLOG("%s - uid: %08x '%s'", __FUNCTION__, uid, (const char*)filename); m_recordings[uid].filename = filename; m_recordings[uid].isDeleted = deleted; } m_mutex.Unlock(); return uid; } cRecording* cRecordingsCache::Lookup(uint32_t uid) { DEBUGLOG("%s - lookup uid: %08x", __FUNCTION__, uid); if(m_recordings.find(uid) == m_recordings.end()) { DEBUGLOG("%s - not found !", __FUNCTION__); return NULL; } m_mutex.Lock(); cString filename = m_recordings[uid].filename; DEBUGLOG("%s - filename: %s", __FUNCTION__, (const char*)filename); cRecording* r; if (!m_recordings[uid].isDeleted) r = Recordings.GetByName(filename); else r = DeletedRecordings.GetByName(filename); DEBUGLOG("%s - recording %s", __FUNCTION__, (r == NULL) ? "not found !" : "found"); m_mutex.Unlock(); return r; } vdr-plugin-vnsiserver-1.3.1/recordingscache.h000066400000000000000000000026511257700704000213110ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_RECORDINGSCACHE_H #define VNSI_RECORDINGSCACHE_H #include #include #include #include #include class cRecordingsCache { protected: cRecordingsCache(); virtual ~cRecordingsCache(); public: static cRecordingsCache& GetInstance(); uint32_t Register(cRecording* recording, bool deleted = false); cRecording* Lookup(uint32_t uid); private: struct RecordingsInfo { cString filename; bool isDeleted; }; std::map m_recordings; cMutex m_mutex; }; #endif // VNSI_RECORDINGSCACHE_H vdr-plugin-vnsiserver-1.3.1/recplayer.c000066400000000000000000000172701257700704000201520ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2004-2005 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ /* * This code is taken from VOMP for VDR plugin. */ #include "recplayer.h" #include #include #include #include #ifndef O_NOATIME #define O_NOATIME 0 #endif cRecPlayer::cRecPlayer(cRecording* rec, bool inProgress) { m_file = -1; m_fileOpen = -1; m_recordingFilename = strdup(rec->FileName()); m_inProgress = inProgress; // FIXME find out max file path / name lengths #if VDRVERSNUM < 10703 m_pesrecording = true; m_indexFile = new cIndexFile(m_recordingFilename, false); #else m_pesrecording = rec->IsPesRecording(); if(m_pesrecording) INFOLOG("recording '%s' is a PES recording", m_recordingFilename); m_indexFile = new cIndexFile(m_recordingFilename, false, m_pesrecording); #endif scan(); } void cRecPlayer::cleanup() { for(int i = 0; i != m_segments.Size(); i++) { delete m_segments[i]; } m_segments.Clear(); } void cRecPlayer::scan() { struct stat s; closeFile(); m_totalLength = 0; m_fileOpen = -1; m_totalFrames = 0; cleanup(); for(int i = 0; ; i++) // i think we only need one possible loop { fileNameFromIndex(i); if(stat(m_fileName, &s) == -1) { break; } cSegment* segment = new cSegment(); segment->start = m_totalLength; segment->end = segment->start + s.st_size; m_segments.Append(segment); m_totalLength += s.st_size; INFOLOG("File %i found, size: %lu, totalLength now %lu", i, s.st_size, m_totalLength); } m_totalFrames = m_indexFile->Last(); INFOLOG("total frames: %u", m_totalFrames); } void cRecPlayer::reScan() { struct stat s; m_totalLength = 0; for(int i = 0; ; i++) // i think we only need one possible loop { fileNameFromIndex(i); if(stat(m_fileName, &s) == -1) { break; } cSegment* segment; if (m_segments.Size() < i+1) { segment = new cSegment(); m_segments.Append(segment); segment->start = m_totalLength; } else segment = m_segments[i]; segment->end = segment->start + s.st_size; m_totalLength += s.st_size; } m_totalFrames = m_indexFile->Last(); } cRecPlayer::~cRecPlayer() { cleanup(); closeFile(); free(m_recordingFilename); } char* cRecPlayer::fileNameFromIndex(int index) { if (m_pesrecording) snprintf(m_fileName, sizeof(m_fileName), "%s/%03i.vdr", m_recordingFilename, index+1); else snprintf(m_fileName, sizeof(m_fileName), "%s/%05i.ts", m_recordingFilename, index+1); return m_fileName; } bool cRecPlayer::openFile(int index) { if (index == m_fileOpen) return true; closeFile(); fileNameFromIndex(index); INFOLOG("openFile called for index %i string:%s", index, m_fileName); m_file = open(m_fileName, O_RDONLY); if (m_file == -1) { INFOLOG("file failed to open"); m_fileOpen = -1; return false; } m_fileOpen = index; return true; } void cRecPlayer::closeFile() { if(m_file == -1) { return; } INFOLOG("file closed"); close(m_file); m_file = -1; m_fileOpen = -1; } uint64_t cRecPlayer::getLengthBytes() { return m_totalLength; } uint32_t cRecPlayer::getLengthFrames() { return m_totalFrames; } int cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, int amount) { // dont let the block be larger than 256 kb if (amount > 512*1024) amount = 512*1024; if ((uint64_t)amount > m_totalLength) amount = m_totalLength; if (position >= m_totalLength) { reScan(); if (position >= m_totalLength) { return 0; } } if ((position + amount) > m_totalLength) amount = m_totalLength - position; // work out what block "position" is in int segmentNumber = -1; for(int i = 0; i < m_segments.Size(); i++) { if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) { segmentNumber = i; break; } } // segment not found / invalid position if (segmentNumber == -1) return 0; // open file (if not already open) if (!openFile(segmentNumber)) return 0; // work out position in current file uint64_t filePosition = position - m_segments[segmentNumber]->start; // seek to position if(lseek(m_file, filePosition, SEEK_SET) == -1) { ERRORLOG("unable to seek to position: %lu", filePosition); return 0; } // try to read the block int bytes_read = read(m_file, buffer, amount); // we may got stuck at end of segment if ((bytes_read == 0) && (position < m_totalLength)) bytes_read += getBlock(buffer, position+1 , amount); if(bytes_read <= 0) { return 0; } if (!m_inProgress) { #ifndef __FreeBSD__ // Tell linux not to bother keeping the data in the FS cache posix_fadvise(m_file, filePosition, bytes_read, POSIX_FADV_DONTNEED); #endif } return bytes_read; } uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber) { if (!m_indexFile) return 0; #if VDRVERSNUM < 10703 unsigned char retFileNumber; int retFileOffset; unsigned char retPicType; #else uint16_t retFileNumber; off_t retFileOffset; bool retPicType; #endif int retLength; if (!m_indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength)) return 0; if (retFileNumber >= m_segments.Size()) return 0; uint64_t position = m_segments[retFileNumber]->start + retFileOffset; return position; } uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position) { if (!m_indexFile) return 0; if (position >= m_totalLength) { DEBUGLOG("Client asked for data starting past end of recording!"); return m_totalFrames; } int segmentNumber = -1; for(int i = 0; i < m_segments.Size(); i++) { if ((position >= m_segments[i]->start) && (position < m_segments[i]->end)) { segmentNumber = i; break; } } if(segmentNumber == -1) { return m_totalFrames; } uint32_t askposition = position - m_segments[segmentNumber]->start; return m_indexFile->Get((int)segmentNumber, askposition); } bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength) { // 0 = backwards // 1 = forwards if (!m_indexFile) return false; #if VDRVERSNUM < 10703 unsigned char waste1; int waste2; #else uint16_t waste1; off_t waste2; #endif int iframeLength; int indexReturnFrameNumber; indexReturnFrameNumber = (uint32_t)m_indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength); DEBUGLOG("GNIF input framenumber:%u, direction=%u, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength); if (indexReturnFrameNumber == -1) return false; *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber); *rframeNumber = (uint32_t)indexReturnFrameNumber; *rframeLength = (uint32_t)iframeLength; return true; } vdr-plugin-vnsiserver-1.3.1/recplayer.h000066400000000000000000000042101257700704000201450ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2004-2005 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ /* * This code is taken from VOMP for VDR plugin. */ #ifndef VNSI_RECPLAYER_H #define VNSI_RECPLAYER_H #include #include #include #include "config.h" class cSegment { public: uint64_t start; uint64_t end; }; class cRecPlayer { public: cRecPlayer(cRecording* rec, bool inProgress = false); ~cRecPlayer(); uint64_t getLengthBytes(); uint32_t getLengthFrames(); int getBlock(unsigned char* buffer, uint64_t position, int amount); bool openFile(int index); void closeFile(); void scan(); void reScan(); uint64_t positionFromFrameNumber(uint32_t frameNumber); uint32_t frameNumberFromPosition(uint64_t position); bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength); private: void cleanup(); char* fileNameFromIndex(int index); void checkBufferSize(int s); char m_fileName[512]; cIndexFile *m_indexFile; int m_file; int m_fileOpen; cVector m_segments; uint64_t m_totalLength; uint32_t m_totalFrames; char *m_recordingFilename; bool m_pesrecording; bool m_inProgress; }; #endif // VNSI_RECPLAYER_H vdr-plugin-vnsiserver-1.3.1/requestpacket.c000066400000000000000000000072171257700704000210440ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2007 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #ifndef __FreeBSD__ #include #else #include #define __be64_to_cpu be64toh #define __cpu_to_be64 htobe64 #endif #include "config.h" #include "requestpacket.h" #include "vnsicommand.h" cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength) : userData(data), userDataLength(dataLength), opCode(opcode), requestID(requestID) { packetPos = 0; ownBlock = true; channelID = 0; streamID = 0; flag = 0; } cRequestPacket::~cRequestPacket() { if (!ownBlock) return; // don't free if it's a getblock if (userData) free(userData); } bool cRequestPacket::end() { return (packetPos >= userDataLength); } int cRequestPacket::serverError() { if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1; else return 0; } char* cRequestPacket::extract_String() { if (serverError()) return NULL; int length = strlen((char*)&userData[packetPos]); if ((packetPos + length) > userDataLength) return NULL; char* str = new char[length + 1]; strcpy(str, (char*)&userData[packetPos]); packetPos += length + 1; return str; } uint8_t cRequestPacket::extract_U8() { if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0; uint8_t uc = userData[packetPos]; packetPos += sizeof(uint8_t); return uc; } uint32_t cRequestPacket::extract_U32() { if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0; uint32_t ul; memcpy(&ul, &userData[packetPos], sizeof(uint32_t)); ul = ntohl(ul); packetPos += sizeof(uint32_t); return ul; } uint64_t cRequestPacket::extract_U64() { if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0; uint64_t ull; memcpy(&ull, &userData[packetPos], sizeof(uint64_t)); ull = __be64_to_cpu(ull); packetPos += sizeof(uint64_t); return ull; } int64_t cRequestPacket::extract_S64() { if ((packetPos + sizeof(int64_t)) > userDataLength) return 0; int64_t ll; memcpy(&ll, &userData[packetPos], sizeof(int64_t)); ll = __be64_to_cpu(ll); packetPos += sizeof(int64_t); return ll; } double cRequestPacket::extract_Double() { if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0; uint64_t ull; memcpy(&ull, &userData[packetPos], sizeof(uint64_t)); ull = __be64_to_cpu(ull); double d; memcpy(&d, &ull, sizeof(double)); packetPos += sizeof(uint64_t); return d; } int32_t cRequestPacket::extract_S32() { if ((packetPos + sizeof(int32_t)) > userDataLength) return 0; int32_t l; memcpy(&l, &userData[packetPos], sizeof(int32_t)); l = ntohl(l); packetPos += sizeof(int32_t); return l; } uint8_t* cRequestPacket::getData() { ownBlock = false; return userData; } vdr-plugin-vnsiserver-1.3.1/requestpacket.h000066400000000000000000000037621257700704000210520ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2007 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_REQUESTPACKET_H #define VNSI_REQUESTPACKET_H class cRequestPacket { public: cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength); ~cRequestPacket(); int serverError(); uint32_t getDataLength() { return userDataLength; } uint32_t getChannelID() { return channelID; } uint32_t getRequestID() { return requestID; } uint32_t getStreamID() { return streamID; } uint32_t getFlag() { return flag; } uint32_t getOpCode() { return opCode; } char* extract_String(); uint8_t extract_U8(); uint32_t extract_U32(); uint64_t extract_U64(); int64_t extract_S64(); int32_t extract_S32(); double extract_Double(); bool end(); // If you call this, the memory becomes yours. Free with free() uint8_t* getData(); private: uint8_t* userData; uint32_t userDataLength; uint32_t packetPos; uint32_t opCode; uint32_t channelID; uint32_t requestID; uint32_t streamID; uint32_t flag; // stream only bool ownBlock; }; #endif // VNSI_REQUESTPACKET_H vdr-plugin-vnsiserver-1.3.1/responsepacket.c000066400000000000000000000166411257700704000212130ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2007 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ /* * This code is taken from VOMP for VDR plugin. */ #include #include #include #include #ifndef __FreeBSD__ #include #else #include #define __be64_to_cpu be64toh #define __cpu_to_be64 htobe64 #endif #include "responsepacket.h" #include "vnsicommand.h" #include "config.h" /* Packet format for an RR channel response: 4 bytes = channel ID = 1 (request/response channel) 4 bytes = request ID (from serialNumber) 4 bytes = length of the rest of the packet ? bytes = rest of packet. depends on packet */ cResponsePacket::cResponsePacket() { buffer = NULL; bufSize = 0; bufUsed = 0; } cResponsePacket::~cResponsePacket() { if (buffer) free(buffer); } void cResponsePacket::initBuffers() { if (buffer == NULL) { bufSize = 512; buffer = (uint8_t*)malloc(bufSize); } } bool cResponsePacket::init(uint32_t requestID) { initBuffers(); uint32_t ul; ul = htonl(VNSI_CHANNEL_REQUEST_RESPONSE); // RR channel memcpy(&buffer[0], &ul, sizeof(uint32_t)); ul = htonl(requestID); memcpy(&buffer[4], &ul, sizeof(uint32_t)); ul = 0; memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t)); bufUsed = headerLength; return true; } bool cResponsePacket::initScan(uint32_t opCode) { initBuffers(); uint32_t ul; ul = htonl(VNSI_CHANNEL_SCAN); // RR channel memcpy(&buffer[0], &ul, sizeof(uint32_t)); ul = htonl(opCode); memcpy(&buffer[4], &ul, sizeof(uint32_t)); ul = 0; memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t)); bufUsed = headerLength; return true; } bool cResponsePacket::initStatus(uint32_t opCode) { initBuffers(); uint32_t ul; ul = htonl(VNSI_CHANNEL_STATUS); // RR channel memcpy(&buffer[0], &ul, sizeof(uint32_t)); ul = htonl(opCode); memcpy(&buffer[4], &ul, sizeof(uint32_t)); ul = 0; memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t)); bufUsed = headerLength; return true; } bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial) { initBuffers(); uint32_t ul; uint64_t ull; ul = htonl(VNSI_CHANNEL_STREAM); // stream channel memcpy(&buffer[0], &ul, sizeof(uint32_t)); ul = htonl(opCode); // Stream packet operation code memcpy(&buffer[4], &ul, sizeof(uint32_t)); ul = htonl(streamID); // Stream ID memcpy(&buffer[8], &ul, sizeof(uint32_t)); ul = htonl(duration); // Duration memcpy(&buffer[12], &ul, sizeof(uint32_t)); ull = __cpu_to_be64(pts); // PTS memcpy(&buffer[16], &ull, sizeof(uint64_t)); ull = __cpu_to_be64(dts); // DTS memcpy(&buffer[24], &ull, sizeof(uint64_t)); ul = htonl(serial); memcpy(&buffer[32], &ul, sizeof(uint32_t)); ul = 0; memcpy(&buffer[userDataLenPosStream], &ul, sizeof(uint32_t)); bufUsed = headerLengthStream; return true; } bool cResponsePacket::initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1) { initBuffers(); uint32_t ul; int32_t l; ul = htonl(VNSI_CHANNEL_OSD); // stream OSD memcpy(&buffer[0], &ul, sizeof(uint32_t)); ul = htonl(opCode); // OSD operation code memcpy(&buffer[4], &ul, sizeof(uint32_t)); l = htonl(wnd); // Window memcpy(&buffer[8], &l, sizeof(int32_t)); l = htonl(color); // Color memcpy(&buffer[12], &l, sizeof(int32_t)); l = htonl(x0); // x0 memcpy(&buffer[16], &l, sizeof(int32_t)); l = htonl(y0); // y0 memcpy(&buffer[20], &l, sizeof(int32_t)); l = htonl(x1); // x1 memcpy(&buffer[24], &l, sizeof(int32_t)); l = htonl(y1); // y1 memcpy(&buffer[28], &l, sizeof(int32_t)); ul = 0; memcpy(&buffer[userDataLenPosOSD], &ul, sizeof(uint32_t)); bufUsed = headerLengthOSD; return true; } void cResponsePacket::finalise() { uint32_t ul = htonl(bufUsed - headerLength); memcpy(&buffer[userDataLenPos], &ul, sizeof(uint32_t)); } void cResponsePacket::finaliseStream() { uint32_t ul = htonl(bufUsed - headerLengthStream); memcpy(&buffer[userDataLenPosStream], &ul, sizeof(uint32_t)); } void cResponsePacket::finaliseOSD() { uint32_t ul = htonl(bufUsed - headerLengthOSD); memcpy(&buffer[userDataLenPosOSD], &ul, sizeof(uint32_t)); } bool cResponsePacket::copyin(const uint8_t* src, uint32_t len) { if (!checkExtend(len)) return false; memcpy(buffer + bufUsed, src, len); bufUsed += len; return true; } uint8_t* cResponsePacket::reserve(uint32_t len) { if (!checkExtend(len)) return 0; uint8_t* result = buffer + bufUsed; bufUsed += len; return result; } bool cResponsePacket::unreserve(uint32_t len) { if(bufUsed < len) return false; bufUsed -= len; return true; } bool cResponsePacket::add_String(const char* string) { uint32_t len = strlen(string) + 1; if (!checkExtend(len)) return false; memcpy(buffer + bufUsed, string, len); bufUsed += len; return true; } bool cResponsePacket::add_U32(uint32_t ul) { if (!checkExtend(sizeof(uint32_t))) return false; uint32_t tmp = htonl(ul); memcpy(&buffer[bufUsed], &tmp, sizeof(uint32_t)); bufUsed += sizeof(uint32_t); return true; } bool cResponsePacket::add_U8(uint8_t c) { if (!checkExtend(sizeof(uint8_t))) return false; buffer[bufUsed] = c; bufUsed += sizeof(uint8_t); return true; } bool cResponsePacket::add_S32(int32_t l) { if (!checkExtend(sizeof(int32_t))) return false; int32_t tmp = htonl(l); memcpy(&buffer[bufUsed], &tmp, sizeof(int32_t)); bufUsed += sizeof(int32_t); return true; } bool cResponsePacket::add_U64(uint64_t ull) { if (!checkExtend(sizeof(uint64_t))) return false; uint64_t tmp = __cpu_to_be64(ull); memcpy(&buffer[bufUsed], &tmp, sizeof(uint64_t)); bufUsed += sizeof(uint64_t); return true; } bool cResponsePacket::add_double(double d) { if (!checkExtend(sizeof(double))) return false; uint64_t ull; memcpy(&ull, &d, sizeof(double)); ull = __cpu_to_be64(ull); memcpy(&buffer[bufUsed], &ull, sizeof(uint64_t)); bufUsed += sizeof(uint64_t); return true; } bool cResponsePacket::checkExtend(uint32_t by) { if ((bufUsed + by) < bufSize) return true; if (512 > by) by = 512; uint8_t* newBuf = (uint8_t*)realloc(buffer, bufSize + by); if (!newBuf) return false; buffer = newBuf; bufSize += by; return true; } vdr-plugin-vnsiserver-1.3.1/responsepacket.h000066400000000000000000000047671257700704000212260ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2007 Chris Tallon * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ /* * This code is taken from VOMP for VDR plugin. */ #ifndef VNSI_RESPONSEPACKET_H #define VNSI_RESPONSEPACKET_H class cResponsePacket { public: cResponsePacket(); ~cResponsePacket(); bool init(uint32_t requestID); bool initScan(uint32_t opCode); bool initStatus(uint32_t opCode); bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t pts, int64_t dts, uint32_t serial); bool initOsd(uint32_t opCode, int32_t wnd, int32_t color, int32_t x0, int32_t y0, int32_t x1, int32_t y1); void finalise(); void finaliseStream(); void finaliseOSD(); bool copyin(const uint8_t* src, uint32_t len); uint8_t* reserve(uint32_t len); bool unreserve(uint32_t len); bool add_String(const char* string); bool add_U32(uint32_t ul); bool add_S32(int32_t l); bool add_U8(uint8_t c); bool add_U64(uint64_t ull); bool add_double(double d); uint8_t* getPtr() { return buffer; } uint32_t getLen() { return bufUsed; } uint32_t getStreamHeaderLength() { return headerLengthStream; } ; uint32_t getOSDHeaderLength() { return headerLengthOSD; } ; void setLen(uint32_t len) { bufUsed = len; } private: uint8_t* buffer; uint32_t bufSize; uint32_t bufUsed; void initBuffers(); bool checkExtend(uint32_t by); const static uint32_t headerLength = 12; const static uint32_t userDataLenPos = 8; const static uint32_t headerLengthStream = 40; const static uint32_t userDataLenPosStream = 36; const static uint32_t headerLengthOSD = 36; const static uint32_t userDataLenPosOSD = 32; }; #endif // VNSI_RESPONSEPACKET_H vdr-plugin-vnsiserver-1.3.1/setup.c000066400000000000000000000062061257700704000173210ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "setup.h" #include "vnsicommand.h" int PmtTimeout = 0; int TimeshiftMode = 0; int TimeshiftBufferSize = 5; int TimeshiftBufferFileSize = 6; char TimeshiftBufferDir[PATH_MAX] = "\0"; int PlayRecording = 0; int AvoidEPGScan = 1; cMenuSetupVNSI::cMenuSetupVNSI(void) { newPmtTimeout = PmtTimeout; Add(new cMenuEditIntItem( tr("PMT Timeout (0-10)"), &newPmtTimeout)); timeshiftModesTexts[0] = tr("Off"); timeshiftModesTexts[1] = tr("RAM"); timeshiftModesTexts[2] = tr("File"); newTimeshiftMode = TimeshiftMode; Add(new cMenuEditStraItem( tr("Time Shift Mode"), &newTimeshiftMode, 3, timeshiftModesTexts)); newTimeshiftBufferSize = TimeshiftBufferSize; Add(new cMenuEditIntItem( tr("TS Buffersize (RAM) (1-80) x 100MB"), &newTimeshiftBufferSize)); newTimeshiftBufferFileSize = TimeshiftBufferFileSize; Add(new cMenuEditIntItem( tr("TS Buffersize (File) (1-10) x 1GB"), &newTimeshiftBufferFileSize)); strn0cpy(newTimeshiftBufferDir, TimeshiftBufferDir, sizeof(newTimeshiftBufferDir)); Add(new cMenuEditStrItem(tr("TS Buffer Directory"), newTimeshiftBufferDir, sizeof(newTimeshiftBufferDir))); newPlayRecording = PlayRecording; Add(new cMenuEditBoolItem( tr("Play Recording instead of live"), &newPlayRecording)); newAvoidEPGScan = AvoidEPGScan; Add(new cMenuEditBoolItem( tr("Avoid EPG scan while streaming"), &newAvoidEPGScan)); } void cMenuSetupVNSI::Store(void) { if (newPmtTimeout > 10 || newPmtTimeout < 0) newPmtTimeout = 2; SetupStore(CONFNAME_PMTTIMEOUT, PmtTimeout = newPmtTimeout); SetupStore(CONFNAME_TIMESHIFT, TimeshiftMode = newTimeshiftMode); if (newTimeshiftBufferSize > 80) newTimeshiftBufferSize = 80; else if (newTimeshiftBufferSize < 1) newTimeshiftBufferSize = 1; SetupStore(CONFNAME_TIMESHIFTBUFFERSIZE, TimeshiftBufferSize = newTimeshiftBufferSize); if (newTimeshiftBufferFileSize > 20) newTimeshiftBufferFileSize = 20; else if (newTimeshiftBufferFileSize < 1) newTimeshiftBufferFileSize = 1; SetupStore(CONFNAME_TIMESHIFTBUFFERFILESIZE, TimeshiftBufferFileSize = newTimeshiftBufferFileSize); SetupStore(CONFNAME_TIMESHIFTBUFFERDIR, strn0cpy(TimeshiftBufferDir, newTimeshiftBufferDir, sizeof(TimeshiftBufferDir))); SetupStore(CONFNAME_PLAYRECORDING, PlayRecording = newPlayRecording); SetupStore(CONFNAME_AVOIDEPGSCAN, AvoidEPGScan = newAvoidEPGScan); } vdr-plugin-vnsiserver-1.3.1/setup.h000066400000000000000000000024111257700704000173200ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_SETUP_H #define VNSI_SETUP_H #include class cMenuSetupVNSI : public cMenuSetupPage { private: int newPmtTimeout; int newTimeshiftMode; const char *timeshiftModesTexts[3]; int newTimeshiftBufferSize; int newTimeshiftBufferFileSize; char newTimeshiftBufferDir[PATH_MAX]; int newPlayRecording; int newAvoidEPGScan; protected: virtual void Store(void); public: cMenuSetupVNSI(void); }; #endif // VNSI_SETUP_H vdr-plugin-vnsiserver-1.3.1/status.c000066400000000000000000000110071257700704000174770ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2014 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "vnsi.h" #include "status.h" #include "vnsiclient.h" #include #include #include #include cVNSIStatus::~cVNSIStatus() { Shutdown(); } void cVNSIStatus::Shutdown() { Cancel(5); cMutexLock lock(&m_mutex); for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) { delete (*i); } m_clients.erase(m_clients.begin(), m_clients.end()); } void cVNSIStatus::AddClient(cVNSIClient* client) { cMutexLock lock(&m_mutex); m_clients.push_back(client); } void cVNSIStatus::Action(void) { cTimeMs chanTimer(0); // get initial state of the recordings int recState = -1; Recordings.StateChanged(recState); // get initial state of the timers int timerState = -1; Timers.Modified(timerState); // last update of epg time_t epgUpdate = cSchedules::Modified(); // delete old timeshift file cString cmd; struct stat sb; if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode)) { if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/') cmd = cString::sprintf("rm -f %s*.vnsi", TimeshiftBufferDir); else cmd = cString::sprintf("rm -f %s/*.vnsi", TimeshiftBufferDir); } else { #if VDRVERSNUM >= 20102 cmd = cString::sprintf("rm -f %s/*.vnsi", cVideoDirectory::Name()); #else cmd = cString::sprintf("rm -f %s/*.vnsi", VideoDirectory); #endif } int ret = system(cmd); // set thread priority SetPriority(1); while (Running()) { m_mutex.Lock(); // remove disconnected clients for (ClientList::iterator i = m_clients.begin(); i != m_clients.end();) { if (!(*i)->Active()) { INFOLOG("Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID()); delete (*i); i = m_clients.erase(i); } else { i++; } } /*! * Don't to updates during running channel scan, KODI's PVR manager becomes * restarted of finished scan. */ if (!cVNSIClient::InhibidDataUpdates()) { // trigger clients to reload the modified channel list if(m_clients.size() > 0 && chanTimer.TimedOut()) { int modified = Channels.Modified(); if (modified) { Channels.SetModified((modified == CHANNELSMOD_USER) ? true : false); INFOLOG("Requesting clients to reload channel list"); for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) (*i)->ChannelsChange(); } chanTimer.Set(5000); } // reset inactivity timeout as long as there are clients connected if(m_clients.size() > 0) { ShutdownHandler.SetUserInactiveTimeout(); } // update recordings if(Recordings.StateChanged(recState)) { INFOLOG("Recordings state changed (%i)", recState); INFOLOG("Requesting clients to reload recordings list"); for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) (*i)->RecordingsChange(); } // update timers if(Timers.Modified(timerState)) { INFOLOG("Timers state changed (%i)", timerState); INFOLOG("Requesting clients to reload timers"); for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) { (*i)->TimerChange(); } } // update epg if((cSchedules::Modified() > epgUpdate + 10) || time(NULL) > epgUpdate + 300) { for (ClientList::iterator i = m_clients.begin(); i != m_clients.end(); i++) { (*i)->EpgChange(); } epgUpdate = time(NULL); } } m_mutex.Unlock(); usleep(250*1000); } } vdr-plugin-vnsiserver-1.3.1/status.h000066400000000000000000000022211257700704000175020ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2014 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #pragma once #include #include class cVNSIClient; typedef std::list ClientList; class cVNSIStatus : public cThread { public: virtual ~cVNSIStatus(); void Shutdown(); void AddClient(cVNSIClient* client); protected: virtual void Action(void); ClientList m_clients; cMutex m_mutex; }; vdr-plugin-vnsiserver-1.3.1/streamer.c000066400000000000000000000463031257700704000200050ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include #include #include "config.h" #include "streamer.h" #include "cxsocket.h" #include "vnsicommand.h" #include "responsepacket.h" #include "vnsi.h" #include "videobuffer.h" // --- cLiveStreamer ------------------------------------------------- cLiveStreamer::cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout) : cThread("cLiveStreamer stream processor") , m_ClientID(clientID) , m_scanTimeout(timeout) , m_Demuxer(bAllowRDS) , m_VideoInput(m_Event, m_Mutex, m_IsRetune) { m_Channel = NULL; m_Socket = NULL; m_Frontend = -1; m_IsAudioOnly = false; m_IsMPEGPS = false; m_startup = true; m_SignalLost = false; m_IFrameSeen = false; m_VideoBuffer = NULL; m_Timeshift = timeshift; m_IsRetune = false; memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo)); if(m_scanTimeout == 0) m_scanTimeout = VNSIServerConfig.stream_timeout; } cLiveStreamer::~cLiveStreamer() { DEBUGLOG("Started to delete live streamer"); Cancel(5); Close(); DEBUGLOG("Finished to delete live streamer"); } bool cLiveStreamer::Open(int serial) { Close(); #if APIVERSNUM >= 10725 m_Device = cDevice::GetDevice(m_Channel, m_Priority, true, true); #else m_Device = cDevice::GetDevice(m_Channel, m_Priority, true); #endif if (!m_Device) return false; bool recording = false; if (VNSIServerConfig.testStreamActive) // test harness { recording = true; m_VideoBuffer = cVideoBuffer::Create(VNSIServerConfig.testStreamFile); } else if (PlayRecording && serial == -1) { for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) { if (timer && timer->Recording() && timer->Channel() == m_Channel) { Recordings.Load(); cRecording matchRec(timer, timer->Event()); cRecording *rec; { cThreadLock RecordingsLock(&Recordings); rec = Recordings.GetByName(matchRec.FileName()); if (!rec) { return false; } } m_VideoBuffer = cVideoBuffer::Create(rec); recording = true; break; } } } if (!recording) { m_VideoBuffer = cVideoBuffer::Create(m_ClientID, m_Timeshift); } if (!m_VideoBuffer) return false; if (!recording) { if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true; m_IsRetune = false; if (!m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer)) { ERRORLOG("Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name()); return false; } } m_Demuxer.Open(*m_Channel, m_VideoBuffer); if (serial >= 0) m_Demuxer.SetSerial(serial); return true; } void cLiveStreamer::Close(void) { INFOLOG("LiveStreamer::Close - close"); m_VideoInput.Close(); m_Demuxer.Close(); if (m_VideoBuffer) { delete m_VideoBuffer; m_VideoBuffer = NULL; } if (m_Frontend >= 0) { close(m_Frontend); m_Frontend = -1; } } void cLiveStreamer::Action(void) { int ret; sStreamPacket pkt_data; sStreamPacket pkt_side_data; // Additional data memset(&pkt_data, 0, sizeof(sStreamPacket)); memset(&pkt_side_data, 0, sizeof(sStreamPacket)); bool requestStreamChangeData = false; bool requestStreamChangeSideData = false; cTimeMs last_info(1000); cTimeMs bufferStatsTimer(1000); while (Running()) { if (m_IsRetune) ret = -1; else ret = m_Demuxer.Read(&pkt_data, &pkt_side_data); if (ret > 0) { if (pkt_data.pmtChange) { requestStreamChangeData = true; requestStreamChangeSideData = true; } // Process normal data if present if (pkt_data.data) { if (pkt_data.streamChange || requestStreamChangeData) sendStreamChange(); requestStreamChangeData = false; if (pkt_data.reftime) { sendRefTime(&pkt_data); pkt_data.reftime = 0; } sendStreamPacket(&pkt_data); } // If some additional data is present inside the stream, it is written there (currently RDS inside MPEG2-Audio) if (pkt_side_data.data) { if (pkt_side_data.streamChange || requestStreamChangeSideData) sendStreamChange(); requestStreamChangeSideData = false; sendStreamPacket(&pkt_side_data); pkt_side_data.data = NULL; } // send signal info every 10 sec. if(last_info.TimedOut()) { last_info.Set(10000); sendSignalInfo(); // prevent EPG scan (activity timeout is 60s) // EPG scan can cause artifacts on dual tuner cards if (AvoidEPGScan) { EITScanner.Activity(); } } // send buffer stats if(bufferStatsTimer.TimedOut()) { sendBufferStatus(); bufferStatsTimer.Set(1000); } } else if (ret == -1) { // no data { bool retune = false; { cMutexLock lock(&m_Mutex); retune = m_IsRetune; if (!retune) m_Event.TimedWait(m_Mutex, 10); } if (retune) { m_VideoInput.Close(); if (m_VideoInput.Open(m_Channel, m_Priority, m_VideoBuffer)) { cMutexLock lock(&m_Mutex); m_IsRetune = false; } else { cMutexLock lock(&m_Mutex); m_Event.TimedWait(m_Mutex, 100); } } } if(m_last_tick.Elapsed() >= (uint64_t)(m_scanTimeout*1000)) { sendStreamStatus(); m_last_tick.Set(0); m_SignalLost = true; } } else if (ret == -2) { if (!Open(m_Demuxer.GetSerial())) { m_Socket->Shutdown(); break; } } } INFOLOG("exit streamer thread"); } bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket *resp) { if (channel == NULL) { ERRORLOG("Starting streaming of channel without valid channel"); return false; } m_Channel = channel; m_Priority = priority; m_Socket = Socket; if (!Open()) return false; // Send the OK response here, that it is before the Stream end message resp->add_U32(VNSI_RET_OK); resp->finalise(); m_Socket->write(resp->getPtr(), resp->getLen()); Activate(true); INFOLOG("Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name()); return true; } inline void cLiveStreamer::Activate(bool On) { if (On) { DEBUGLOG("VDR active, sending stream start message"); Start(); } else { DEBUGLOG("VDR inactive, sending stream end message"); Cancel(5); } } void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt) { if(pkt == NULL) return; if(pkt->size == 0) return; if (!m_streamHeader.initStream(VNSI_STREAM_MUXPKT, pkt->id, pkt->duration, pkt->pts, pkt->dts, pkt->serial)) { ERRORLOG("stream response packet init fail"); return; } m_streamHeader.setLen(m_streamHeader.getStreamHeaderLength() + pkt->size); m_streamHeader.finaliseStream(); m_Socket->LockWrite(); m_Socket->write(m_streamHeader.getPtr(), m_streamHeader.getStreamHeaderLength(), -1, true); m_Socket->write(pkt->data, pkt->size); m_Socket->UnlockWrite(); m_last_tick.Set(0); m_SignalLost = false; } void cLiveStreamer::sendStreamChange() { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_CHANGE, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } uint32_t FpsScale, FpsRate, Height, Width; double Aspect; uint32_t Channels, SampleRate, BitRate, BitsPerSample, BlockAlign; for (cTSStream* stream = m_Demuxer.GetFirstStream(); stream; stream = m_Demuxer.GetNextStream()) { resp->add_U32(stream->GetPID()); if (stream->Type() == stMPEG2AUDIO) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("MPEG2AUDIO"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); for (unsigned int i = 0; i < stream->GetSideDataTypes()->size(); i++) { resp->add_U32(stream->GetSideDataTypes()->at(i).first); if (stream->GetSideDataTypes()->at(i).second == scRDS) { resp->add_String("RDS"); resp->add_String(stream->GetLanguage()); resp->add_U32(stream->GetPID()); } } } else if (stream->Type() == stMPEG2VIDEO) { stream->GetVideoInformation(FpsScale, FpsRate, Height, Width, Aspect); resp->add_String("MPEG2VIDEO"); resp->add_U32(FpsScale); resp->add_U32(FpsRate); resp->add_U32(Height); resp->add_U32(Width); resp->add_double(Aspect); } else if (stream->Type() == stAC3) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("AC3"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); } else if (stream->Type() == stH264) { stream->GetVideoInformation(FpsScale, FpsRate, Height, Width, Aspect); resp->add_String("H264"); resp->add_U32(FpsScale); resp->add_U32(FpsRate); resp->add_U32(Height); resp->add_U32(Width); resp->add_double(Aspect); } else if (stream->Type() == stDVBSUB) { resp->add_String("DVBSUB"); resp->add_String(stream->GetLanguage()); resp->add_U32(stream->CompositionPageId()); resp->add_U32(stream->AncillaryPageId()); } else if (stream->Type() == stTELETEXT) { resp->add_String("TELETEXT"); resp->add_String(stream->GetLanguage()); resp->add_U32(stream->CompositionPageId()); resp->add_U32(stream->AncillaryPageId()); } else if (stream->Type() == stAACADTS) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("AAC"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); } else if (stream->Type() == stAACLATM) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("AAC_LATM"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); } else if (stream->Type() == stEAC3) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("EAC3"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); } else if (stream->Type() == stDTS) { stream->GetAudioInformation(Channels, SampleRate, BitRate, BitsPerSample, BlockAlign); resp->add_String("DTS"); resp->add_String(stream->GetLanguage()); resp->add_U32(Channels); resp->add_U32(SampleRate); resp->add_U32(BlockAlign); resp->add_U32(BitRate); resp->add_U32(BitsPerSample); } } resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } void cLiveStreamer::sendSignalInfo() { /* If no frontend is found m_Frontend is set to -2, in this case return a empty signalinfo package */ if (m_Frontend == -2) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } resp->add_String(*cString::sprintf("Unknown")); resp->add_String(*cString::sprintf("Unknown")); resp->add_U32(0); resp->add_U32(0); resp->add_U32(0); resp->add_U32(0); resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; return; } if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) { if (m_Frontend < 0) { for (int i = 0; i < 8; i++) { m_DeviceString = cString::sprintf("/dev/video%d", i); m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK); if (m_Frontend >= 0) { if (ioctl(m_Frontend, VIDIOC_QUERYCAP, &m_vcap) < 0) { ERRORLOG("cannot read analog frontend info."); close(m_Frontend); m_Frontend = -1; memset(&m_vcap, 0, sizeof(m_vcap)); continue; } break; } } if (m_Frontend < 0) m_Frontend = -2; } if (m_Frontend >= 0) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } resp->add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver)); resp->add_String(""); resp->add_U32(0); resp->add_U32(0); resp->add_U32(0); resp->add_U32(0); resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } } else { if (m_Frontend < 0) { m_DeviceString = cString::sprintf(FRONTEND_DEVICE, m_Device->CardIndex(), 0); m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK); if (m_Frontend >= 0) { if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0) { ERRORLOG("cannot read frontend info."); close(m_Frontend); m_Frontend = -2; memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo)); return; } } } if (m_Frontend >= 0) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_SIGNALINFO, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } fe_status_t status; uint16_t fe_snr; uint16_t fe_signal; uint32_t fe_ber; uint32_t fe_unc; memset(&status, 0, sizeof(status)); ioctl(m_Frontend, FE_READ_STATUS, &status); if (ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &fe_signal) == -1) fe_signal = -2; if (ioctl(m_Frontend, FE_READ_SNR, &fe_snr) == -1) fe_snr = -2; if (ioctl(m_Frontend, FE_READ_BER, &fe_ber) == -1) fe_ber = -2; if (ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &fe_unc) == -1) fe_unc = -2; switch (m_Channel->Source() & cSource::st_Mask) { case cSource::stSat: resp->add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name)); break; case cSource::stCable: resp->add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name)); break; case cSource::stTerr: resp->add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name)); break; } resp->add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-")); resp->add_U32(fe_snr); resp->add_U32(fe_signal); resp->add_U32(fe_ber); resp->add_U32(fe_unc); resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } } } void cLiveStreamer::sendStreamStatus() { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_STATUS, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } uint16_t error = m_Demuxer.GetError(); if (error & ERROR_PES_SCRAMBLE) { INFOLOG("Channel: scrambled %d", error); resp->add_String(cString::sprintf("Channel: scrambled (%d)", error)); } else if (error & ERROR_PES_STARTCODE) { INFOLOG("Channel: startcode %d", error); resp->add_String(cString::sprintf("Channel: encrypted? (%d)", error)); } else if (error & ERROR_DEMUX_NODATA) { INFOLOG("Channel: no data %d", error); resp->add_String(cString::sprintf("Channel: no data")); } else { INFOLOG("Channel: unknown error %d", error); resp->add_String(cString::sprintf("Channel: unknown error (%d)", error)); } resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } void cLiveStreamer::sendBufferStatus() { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_BUFFERSTATS, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } uint32_t start, end; bool timeshift; m_Demuxer.BufferStatus(timeshift, start, end); resp->add_U8(timeshift); resp->add_U32(start); resp->add_U32(end); resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } void cLiveStreamer::sendRefTime(sStreamPacket *pkt) { if(pkt == NULL) return; cResponsePacket *resp = new cResponsePacket(); if (!resp->initStream(VNSI_STREAM_REFTIME, 0, 0, 0, 0, 0)) { ERRORLOG("stream response packet init fail"); delete resp; return; } resp->add_U32(pkt->reftime); resp->add_U64(pkt->pts); resp->finaliseStream(); m_Socket->write(resp->getPtr(), resp->getLen()); delete resp; } bool cLiveStreamer::SeekTime(int64_t time, uint32_t &serial) { bool ret = m_Demuxer.SeekTime(time); serial = m_Demuxer.GetSerial(); return ret; } void cLiveStreamer::RetuneChannel(const cChannel *channel) { if (m_Channel != channel || !m_VideoInput.IsOpen()) return; INFOLOG("re-tune to channel %s", m_Channel->Name()); cMutexLock lock(&m_Mutex); m_IsRetune = true; m_Event.Broadcast(); } vdr-plugin-vnsiserver-1.3.1/streamer.h000066400000000000000000000071201257700704000200040ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_RECEIVER_H #define VNSI_RECEIVER_H #include #include #include #include #include #include #include #include "parser.h" #include "responsepacket.h" #include "demuxer.h" #include "videoinput.h" class cxSocket; class cChannel; class cTSParser; class cResponsePacket; class cVideoBuffer; class cVideoInput; class cLiveStreamer : public cThread { private: friend class cParser; friend class cLivePatFilter; friend class cLiveReceiver; void sendStreamPacket(sStreamPacket *pkt); void sendStreamChange(); void sendSignalInfo(); void sendStreamStatus(); void sendBufferStatus(); void sendRefTime(sStreamPacket *pkt); int m_ClientID; const cChannel *m_Channel; /*!> Channel to stream */ cDevice *m_Device; cxSocket *m_Socket; /*!> The socket class to communicate with client */ int m_Frontend; /*!> File descriptor to access used receiving device */ dvb_frontend_info m_FrontendInfo; /*!> DVB Information about the receiving device (DVB only) */ v4l2_capability m_vcap; /*!> PVR Information about the receiving device (pvrinput only) */ cString m_DeviceString; /*!> The name of the receiving device */ bool m_startup; bool m_IsAudioOnly; /*!> Set to true if streams contains only audio */ bool m_IsMPEGPS; /*!> TS Stream contains MPEG PS data like from pvrinput */ uint32_t m_scanTimeout; /*!> Channel scanning timeout (in seconds) */ cTimeMs m_last_tick; bool m_SignalLost; bool m_IFrameSeen; cResponsePacket m_streamHeader; cVNSIDemuxer m_Demuxer; cVideoBuffer *m_VideoBuffer; cVideoInput m_VideoInput; int m_Priority; uint8_t m_Timeshift; cCondVar m_Event; cMutex m_Mutex; bool m_IsRetune; protected: virtual void Action(void); bool Open(int serial = -1); void Close(); public: cLiveStreamer(int clientID, bool bAllowRDS, uint8_t timeshift, uint32_t timeout = 0); virtual ~cLiveStreamer(); void Activate(bool On); bool StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket* resp); bool IsStarting() { return m_startup; } bool IsAudioOnly() { return m_IsAudioOnly; } bool IsMPEGPS() { return m_IsMPEGPS; } bool SeekTime(int64_t time, uint32_t &serial); void RetuneChannel(const cChannel *channel); }; #endif // VNSI_RECEIVER_H vdr-plugin-vnsiserver-1.3.1/videobuffer.c000066400000000000000000000510041257700704000204550ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "videobuffer.h" #include "config.h" #include "vnsi.h" #include "recplayer.h" #include #include #include #include #include #include #include class cVideoBufferSimple : public cVideoBuffer { friend class cVideoBuffer; public: virtual void Put(uint8_t *buf, unsigned int size); virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime); protected: cVideoBufferSimple(); virtual ~cVideoBufferSimple(); cRingBufferLinear *m_Buffer; int m_BytesConsumed; }; cVideoBufferSimple::cVideoBufferSimple() { m_Buffer = new cRingBufferLinear(MEGABYTE(3), TS_SIZE * 2, false); m_Buffer->SetTimeouts(0, 100); m_BytesConsumed = 0; } cVideoBufferSimple::~cVideoBufferSimple() { if (m_Buffer) delete m_Buffer; } void cVideoBufferSimple::Put(uint8_t *buf, unsigned int size) { m_Buffer->Put(buf, size); } int cVideoBufferSimple::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) { int readBytes; if (m_BytesConsumed) { m_Buffer->Del(m_BytesConsumed); } m_BytesConsumed = 0; *buf = m_Buffer->Get(readBytes); if (!(*buf) || readBytes < TS_SIZE) { usleep(100); return 0; } /* Make sure we are looking at a TS packet */ while (readBytes > TS_SIZE) { if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE) break; m_BytesConsumed++; (*buf)++; readBytes--; } if ((*buf)[0] != TS_SYNC_BYTE) { m_Buffer->Del(m_BytesConsumed); m_BytesConsumed = 0; return 0; } m_BytesConsumed += TS_SIZE; endTime = 0; wrapTime = 0; return TS_SIZE; } //----------------------------------------------------------------------------- #define MARGIN 40000 class cVideoBufferTimeshift : public cVideoBuffer { friend class cVideoBuffer; public: virtual off_t GetPosMin(); virtual off_t GetPosMax(); virtual off_t GetPosCur(); virtual void GetPositions(off_t *cur, off_t *min, off_t *max); virtual bool HasBuffer() { return true; }; protected: cVideoBufferTimeshift(); virtual bool Init() = 0; virtual off_t Available(); off_t m_BufferSize; off_t m_WritePtr; off_t m_ReadPtr; bool m_BufferFull; unsigned int m_Margin; unsigned int m_BytesConsumed; cMutex m_Mutex; }; cVideoBufferTimeshift::cVideoBufferTimeshift() { m_Margin = TS_SIZE*2; m_BufferFull = false; m_ReadPtr = 0; m_WritePtr = 0; m_BytesConsumed = 0; } off_t cVideoBufferTimeshift::GetPosMin() { off_t ret; if (!m_BufferFull) return 0; ret = m_WritePtr + MARGIN * 2; if (ret >= m_BufferSize) ret -= m_BufferSize; return ret; } off_t cVideoBufferTimeshift::GetPosMax() { off_t ret = m_WritePtr; if (ret < GetPosMin()) ret += m_BufferSize; return ret; } off_t cVideoBufferTimeshift::GetPosCur() { off_t ret = m_ReadPtr; if (ret < GetPosMin()) ret += m_BufferSize; return ret; } void cVideoBufferTimeshift::GetPositions(off_t *cur, off_t *min, off_t *max) { cMutexLock lock(&m_Mutex); *cur = GetPosCur(); *min = GetPosMin(); *min = (*min > *cur) ? *cur : *min; *max = GetPosMax(); } off_t cVideoBufferTimeshift::Available() { cMutexLock lock(&m_Mutex); off_t ret; if (m_ReadPtr <= m_WritePtr) ret = m_WritePtr - m_ReadPtr; else ret = m_BufferSize - (m_ReadPtr - m_WritePtr); return ret; } //----------------------------------------------------------------------------- class cVideoBufferRAM : public cVideoBufferTimeshift { friend class cVideoBuffer; public: virtual void Put(uint8_t *buf, unsigned int size); virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime); virtual void SetPos(off_t pos); protected: cVideoBufferRAM(); virtual ~cVideoBufferRAM(); virtual bool Init(); uint8_t *m_Buffer; uint8_t *m_BufferPtr; }; cVideoBufferRAM::cVideoBufferRAM() { m_Buffer = 0; } cVideoBufferRAM::~cVideoBufferRAM() { if (m_Buffer) free(m_Buffer); } bool cVideoBufferRAM::Init() { m_BufferSize = (off_t)TimeshiftBufferSize*100*1000*1000; INFOLOG("allocated timeshift buffer with size: %ld", m_BufferSize); m_Buffer = (uint8_t*)malloc(m_BufferSize + m_Margin); m_BufferPtr = m_Buffer + m_Margin; if (!m_Buffer) return false; else return true; } void cVideoBufferRAM::SetPos(off_t pos) { cMutexLock lock(&m_Mutex); m_ReadPtr = pos; if (m_ReadPtr >= m_BufferSize) m_ReadPtr -= m_BufferSize; m_BytesConsumed = 0; } void cVideoBufferRAM::Put(uint8_t *buf, unsigned int size) { if (Available() + MARGIN >= m_BufferSize) { return; } if ((m_BufferSize - m_WritePtr) <= size) { int bytes = m_BufferSize - m_WritePtr; memcpy(m_BufferPtr+m_WritePtr, buf, bytes); size -= bytes; buf += bytes; cMutexLock lock(&m_Mutex); m_WritePtr = 0; } memcpy(m_BufferPtr+m_WritePtr, buf, size); cMutexLock lock(&m_Mutex); m_WritePtr += size; if (!m_BufferFull) { if ((m_WritePtr + 2*MARGIN) > m_BufferSize) { m_BufferFull = true; time(&m_bufferWrapTime); } } time(&m_bufferEndTime); } int cVideoBufferRAM::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) { // move read pointer if (m_BytesConsumed) { cMutexLock lock(&m_Mutex); m_ReadPtr += m_BytesConsumed; if (m_ReadPtr >= m_BufferSize) m_ReadPtr -= m_BufferSize; endTime = m_bufferEndTime; wrapTime = m_bufferWrapTime; } m_BytesConsumed = 0; // check if we have anything to read off_t readBytes = Available(); if (readBytes < m_Margin) { return 0; } // if we are close to end, copy margin to front if (m_ReadPtr > (m_BufferSize - m_Margin)) { int bytesToCopy = m_BufferSize - m_ReadPtr; memmove(m_Buffer + (m_Margin - bytesToCopy), m_Buffer + m_ReadPtr, bytesToCopy); *buf = m_Buffer + (m_Margin - bytesToCopy); } else *buf = m_BufferPtr + m_ReadPtr; // Make sure we are looking at a TS packet while (readBytes > TS_SIZE) { if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE) break; m_BytesConsumed++; (*buf)++; readBytes--; } if ((*buf)[0] != TS_SYNC_BYTE) { return 0; } m_BytesConsumed += TS_SIZE; return TS_SIZE; } //----------------------------------------------------------------------------- class cVideoBufferFile : public cVideoBufferTimeshift { friend class cVideoBuffer; public: virtual off_t GetPosMax(); virtual void Put(uint8_t *buf, unsigned int size); virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime); virtual void SetPos(off_t pos); protected: cVideoBufferFile(); cVideoBufferFile(int clientID); virtual ~cVideoBufferFile(); virtual bool Init(); virtual int ReadBytes(uint8_t *buf, off_t pos, unsigned int size); int m_ClientID; cString m_Filename; int m_Fd; uint8_t *m_ReadCache; unsigned int m_ReadCachePtr; unsigned int m_ReadCacheSize; unsigned int m_ReadCacheMaxSize; }; cVideoBufferFile::cVideoBufferFile() { } cVideoBufferFile::cVideoBufferFile(int clientID) { m_ClientID = clientID; m_Fd = 0; m_ReadCacheSize = 0; m_ReadCache = 0; } cVideoBufferFile::~cVideoBufferFile() { if (m_Fd) { close(m_Fd); unlink(m_Filename); m_Fd = 0; } if (m_ReadCache) free(m_ReadCache); } bool cVideoBufferFile::Init() { m_ReadCache = 0; m_ReadCacheMaxSize = 32000; m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize); if (!m_ReadCache) return false; m_BufferSize = (off_t)TimeshiftBufferFileSize*1000*1000*1000; struct stat sb; if ((*TimeshiftBufferDir) && stat(TimeshiftBufferDir, &sb) == 0 && S_ISDIR(sb.st_mode)) { if (TimeshiftBufferDir[strlen(TimeshiftBufferDir)-1] == '/') m_Filename = cString::sprintf("%sTimeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID); else m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", TimeshiftBufferDir, m_ClientID); } else #if VDRVERSNUM >= 20102 m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", cVideoDirectory::Name(), m_ClientID); #else m_Filename = cString::sprintf("%s/Timeshift-%d.vnsi", VideoDirectory, m_ClientID); #endif m_Fd = open(m_Filename, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU); if (m_Fd == -1) { ERRORLOG("Could not open file: %s", (const char*)m_Filename); return false; } m_WritePtr = lseek(m_Fd, m_BufferSize - 1, SEEK_SET); if (m_WritePtr == -1) { ERRORLOG("(Init) Could not seek file: %s", (const char*)m_Filename); return false; } char tmp = '0'; if (safe_write(m_Fd, &tmp, 1) < 0) { ERRORLOG("(Init) Could not write to file: %s", (const char*)m_Filename); return false; } m_WritePtr = 0; m_ReadPtr = 0; m_ReadCacheSize = 0; return true; } void cVideoBufferFile::SetPos(off_t pos) { cMutexLock lock(&m_Mutex); m_ReadPtr = pos; if (m_ReadPtr >= m_BufferSize) m_ReadPtr -= m_BufferSize; m_BytesConsumed = 0; m_ReadCacheSize = 0; } off_t cVideoBufferFile::GetPosMax() { off_t posMax = cVideoBufferTimeshift::GetPosMax(); if (posMax >= m_ReadCacheMaxSize) posMax -= m_ReadCacheMaxSize; else posMax = 0; return posMax; } void cVideoBufferFile::Put(uint8_t *buf, unsigned int size) { if (Available() + MARGIN >= m_BufferSize) { return; } if ((m_BufferSize - m_WritePtr) <= size) { int bytes = m_BufferSize - m_WritePtr; int p = 0; off_t ptr = m_WritePtr; while(bytes > 0) { p = pwrite(m_Fd, buf, bytes, ptr); if (p < 0) { ERRORLOG("Could not write to file: %s", (const char*)m_Filename); return; } size -= p; bytes -= p; buf += p; ptr += p; } cMutexLock lock(&m_Mutex); m_WritePtr = 0; } off_t ptr = m_WritePtr; int bytes = size; int p; while(bytes > 0) { p = pwrite(m_Fd, buf, bytes, ptr); if (p < 0) { ERRORLOG("Could not write to file: %s", (const char*)m_Filename); return; } bytes -= p; buf += p; ptr += p; } cMutexLock lock(&m_Mutex); m_WritePtr += size; if (!m_BufferFull) { if ((m_WritePtr + 2*MARGIN) > m_BufferSize) { m_BufferFull = true; time(&m_bufferWrapTime); } } time(&m_bufferEndTime); } int cVideoBufferFile::ReadBytes(uint8_t *buf, off_t pos, unsigned int size) { int p; for (;;) { p = pread(m_Fd, buf, size, pos); if (p < 0 && errno == EINTR) { continue; } return p; } } int cVideoBufferFile::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) { // move read pointer if (m_BytesConsumed) { cMutexLock lock(&m_Mutex); m_ReadPtr += m_BytesConsumed; if (m_ReadPtr >= m_BufferSize) m_ReadPtr -= m_BufferSize; m_ReadCachePtr += m_BytesConsumed; endTime = m_bufferEndTime; wrapTime = m_bufferWrapTime; } m_BytesConsumed = 0; // check if we have anything to read off_t readBytes; if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize)) { readBytes = m_ReadCacheSize - m_ReadCachePtr; *buf = m_ReadCache + m_ReadCachePtr; } else if ((readBytes = Available()) >= m_ReadCacheMaxSize) { if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize) { m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize); if (m_ReadCacheSize < 0) { ERRORLOG("Could not read file: %s", (const char*)m_Filename); return 0; } if (m_ReadCacheSize < m_Margin) { ERRORLOG("Could not read file (margin): %s , read: %d", (const char*)m_Filename, m_ReadCacheSize); m_ReadCacheSize = 0; return 0; } readBytes = m_ReadCacheSize; *buf = m_ReadCache; m_ReadCachePtr = 0; } else { m_ReadCacheSize = ReadBytes(m_ReadCache, m_ReadPtr, m_BufferSize - m_ReadPtr); if ((m_ReadCacheSize < m_Margin) && (m_ReadCacheSize != (m_BufferSize - m_ReadPtr))) { ERRORLOG("Could not read file (end): %s", (const char*)m_Filename); m_ReadCacheSize = 0; return 0; } readBytes = ReadBytes(m_ReadCache + m_ReadCacheSize, 0, m_ReadCacheMaxSize - m_ReadCacheSize); if (readBytes < 0) { ERRORLOG("Could not read file (end): %s", (const char*)m_Filename); m_ReadCacheSize = 0; return 0; } m_ReadCacheSize += readBytes; if (m_ReadCacheSize < m_Margin) { ERRORLOG("Could not read file (margin): %s", (const char*)m_Filename); m_ReadCacheSize = 0; return 0; } readBytes = m_ReadCacheSize; *buf = m_ReadCache; m_ReadCachePtr = 0; } } else return 0; // Make sure we are looking at a TS packet while (readBytes > TS_SIZE) { if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE) break; m_BytesConsumed++; (*buf)++; readBytes--; } if ((*buf)[0] != TS_SYNC_BYTE) { return 0; } m_BytesConsumed += TS_SIZE; return TS_SIZE; } //----------------------------------------------------------------------------- class cVideoBufferRecording : public cVideoBufferFile { friend class cVideoBuffer; public: virtual off_t GetPosMax(); virtual void Put(uint8_t *buf, unsigned int size); virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime); virtual time_t GetRefTime(); protected: cVideoBufferRecording(cRecording *rec); virtual ~cVideoBufferRecording(); virtual bool Init(); virtual off_t Available(); off_t GetPosEnd(); cRecPlayer *m_RecPlayer; cRecording *m_Recording; cTimeMs m_ScanTimer; }; cVideoBufferRecording::cVideoBufferRecording(cRecording *rec) { m_Recording = rec; m_ReadCacheSize = 0; m_ReadCache = 0; } cVideoBufferRecording::~cVideoBufferRecording() { INFOLOG("delete cVideoBufferRecording"); if (m_RecPlayer) delete m_RecPlayer; } off_t cVideoBufferRecording::GetPosMax() { m_RecPlayer->reScan(); m_WritePtr = m_RecPlayer->getLengthBytes(); return cVideoBufferFile::GetPosMax(); } void cVideoBufferRecording::Put(uint8_t *buf, unsigned int size) { } bool cVideoBufferRecording::Init() { m_ReadCacheMaxSize = 32000; m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize); if (!m_ReadCache) return false; m_RecPlayer = new cRecPlayer(m_Recording, true); if (!m_RecPlayer) return false; m_WritePtr = 0; m_ReadPtr = 0; m_ReadCacheSize = 0; m_InputAttached = false; m_ScanTimer.Set(0); return true; } time_t cVideoBufferRecording::GetRefTime() { return m_Recording->Start(); } off_t cVideoBufferRecording::Available() { if (m_ScanTimer.TimedOut()) { m_RecPlayer->reScan(); m_ScanTimer.Set(1000); } m_BufferSize = m_WritePtr = m_RecPlayer->getLengthBytes(); return cVideoBufferTimeshift::Available(); } int cVideoBufferRecording::ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) { // move read pointer if (m_BytesConsumed) { m_ReadPtr += m_BytesConsumed; if (m_ReadPtr >= m_BufferSize) { m_ReadPtr -= m_BufferSize; ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error"); } m_ReadCachePtr += m_BytesConsumed; } m_BytesConsumed = 0; // check if we have anything to read off_t readBytes; if (m_ReadCacheSize && ((m_ReadCachePtr + m_Margin) <= m_ReadCacheSize)) { readBytes = m_ReadCacheSize - m_ReadCachePtr; *buf = m_ReadCache + m_ReadCachePtr; } else if ((readBytes = Available()) >= m_ReadCacheMaxSize) { if (m_ReadPtr + m_ReadCacheMaxSize <= m_BufferSize) { m_ReadCacheSize = m_RecPlayer->getBlock(m_ReadCache, m_ReadPtr, m_ReadCacheMaxSize); if (m_ReadCacheSize < 0) { ERRORLOG("Could not read file, size: %d", m_ReadCacheSize); m_ReadCacheSize = 0; return 0; } readBytes = m_ReadCacheSize; *buf = m_ReadCache; m_ReadCachePtr = 0; } else { ERRORLOG("cVideoBufferRecording::ReadBlock - unknown error"); return 0; } } else return 0; // Make sure we are looking at a TS packet while (readBytes > TS_SIZE) { if ((*buf)[0] == TS_SYNC_BYTE && (*buf)[TS_SIZE] == TS_SYNC_BYTE) break; m_BytesConsumed++; (*buf)++; readBytes--; } if ((*buf)[0] != TS_SYNC_BYTE) { return 0; } m_BytesConsumed += TS_SIZE; time(&endTime); wrapTime = 0; return TS_SIZE; } //----------------------------------------------------------------------------- class cVideoBufferTest : public cVideoBufferFile { friend class cVideoBuffer; public: virtual off_t GetPosMax(); virtual void Put(uint8_t *buf, unsigned int size); protected: cVideoBufferTest(cString filename); virtual ~cVideoBufferTest(); virtual bool Init(); virtual off_t Available(); off_t GetPosEnd(); }; cVideoBufferTest::cVideoBufferTest(cString filename) { m_Filename = filename; m_Fd = 0; m_ReadCacheSize = 0; } cVideoBufferTest::~cVideoBufferTest() { if (m_Fd) { close(m_Fd); m_Fd = 0; } } off_t cVideoBufferTest::GetPosMax() { m_WritePtr = GetPosEnd(); return cVideoBufferTimeshift::GetPosMax(); } off_t cVideoBufferTest::GetPosEnd() { off_t cur = lseek(m_Fd, 0, SEEK_CUR); off_t end = lseek(m_Fd, 0, SEEK_END); lseek(m_Fd, cur, SEEK_SET); return end; } void cVideoBufferTest::Put(uint8_t *buf, unsigned int size) { } bool cVideoBufferTest::Init() { m_ReadCache = 0; m_ReadCacheMaxSize = 8000; m_ReadCache = (uint8_t*)malloc(m_ReadCacheMaxSize); if (!m_ReadCache) return false; m_Fd = open(m_Filename, O_RDONLY); if (m_Fd == -1) { ERRORLOG("Could not open file: %s", (const char*)m_Filename); return false; } m_WritePtr = 0; m_ReadPtr = 0; m_ReadCacheSize = 0; m_InputAttached = false; return true; } off_t cVideoBufferTest::Available() { m_BufferSize = m_WritePtr = GetPosEnd(); return cVideoBufferTimeshift::Available(); } //----------------------------------------------------------------------------- cVideoBuffer::cVideoBuffer() { m_CheckEof = false; m_InputAttached = true; m_bufferEndTime = 0; m_bufferWrapTime = 0; } cVideoBuffer::~cVideoBuffer() { } cVideoBuffer* cVideoBuffer::Create(int clientID, uint8_t timeshift) { // no time shift if (TimeshiftMode == 0 || timeshift == 0) { cVideoBufferSimple *buffer = new cVideoBufferSimple(); return buffer; } // buffer in ram else if (TimeshiftMode == 1) { cVideoBufferRAM *buffer = new cVideoBufferRAM(); if (!buffer->Init()) { delete buffer; return NULL; } else return buffer; } // buffer in file else if (TimeshiftMode == 2) { cVideoBufferFile *buffer = new cVideoBufferFile(clientID); if (!buffer->Init()) { delete buffer; return NULL; } else return buffer; } else return NULL; } cVideoBuffer* cVideoBuffer::Create(cString filename) { INFOLOG("Open recording: %s", (const char*)filename); cVideoBufferTest *buffer = new cVideoBufferTest(filename); if (!buffer->Init()) { delete buffer; return NULL; } else return buffer; } cVideoBuffer* cVideoBuffer::Create(cRecording *rec) { INFOLOG("Open recording: %s", rec->FileName()); cVideoBufferRecording *buffer = new cVideoBufferRecording(rec); if (!buffer->Init()) { delete buffer; return NULL; } else return buffer; } int cVideoBuffer::Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) { int count = ReadBlock(buf, size, endTime, wrapTime); // check for end of file if (!m_InputAttached && count != TS_SIZE) { if (m_CheckEof && m_Timer.TimedOut()) { INFOLOG("Recoding - end of file"); return -2; } else if (!m_CheckEof) { m_CheckEof = true; m_Timer.Set(3000); } } else m_CheckEof = false; return count; } void cVideoBuffer::AttachInput(bool attach) { m_InputAttached = attach; } time_t cVideoBuffer::GetRefTime() { time_t t; time(&t); return t; } vdr-plugin-vnsiserver-1.3.1/videobuffer.h000066400000000000000000000035671257700704000204750ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #pragma once #include #include #include class cRecording; class cVideoBuffer { public: virtual ~cVideoBuffer(); static cVideoBuffer* Create(int clientID, uint8_t timeshift); static cVideoBuffer* Create(cString filename); static cVideoBuffer* Create(cRecording *rec); virtual void Put(uint8_t *buf, unsigned int size) = 0; virtual int ReadBlock(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime) = 0; virtual off_t GetPosMin() { return 0; }; virtual off_t GetPosMax() { return 0; }; virtual off_t GetPosCur() { return 0; }; virtual void GetPositions(off_t *cur, off_t *min, off_t *max) {}; virtual void SetPos(off_t pos) {}; virtual void SetCache(bool on) {}; virtual bool HasBuffer() { return false; }; virtual time_t GetRefTime(); int Read(uint8_t **buf, unsigned int size, time_t &endTime, time_t &wrapTime); void AttachInput(bool attach); protected: cVideoBuffer(); cTimeMs m_Timer; bool m_CheckEof; bool m_InputAttached; time_t m_bufferEndTime; time_t m_bufferWrapTime; }; vdr-plugin-vnsiserver-1.3.1/videoinput.c000066400000000000000000000500661257700704000203520ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * * */ #include "config.h" #include "videoinput.h" #include "videobuffer.h" #include "vnsi.h" #include #include #include #include #include #include #include #include // --- cLiveReceiver ------------------------------------------------- class cLiveReceiver: public cReceiver { public: cLiveReceiver(cVideoInput *VideoInput, const cChannel *Channel, int Priority); virtual ~cLiveReceiver(); cChannel m_PmtChannel; protected: virtual void Activate(bool On); virtual void Receive(uchar *Data, int Length); cVideoInput *m_VideoInput; }; cLiveReceiver::cLiveReceiver(cVideoInput *VideoInput, const cChannel *Channel, int Priority) : cReceiver(Channel, Priority) , m_VideoInput(VideoInput) { SetPids(NULL); } cLiveReceiver::~cLiveReceiver() { } //void cLiveReceiver void cLiveReceiver::Receive(uchar *Data, int Length) { m_VideoInput->Receive(Data, Length); } inline void cLiveReceiver::Activate(bool On) { DEBUGLOG("activate live receiver: %d", On); if (!On && !m_VideoInput->m_PmtChange) { m_VideoInput->Retune(); } } // --- cLivePatFilter ---------------------------------------------------- class cLivePatFilter : public cFilter { private: int m_pmtPid; int m_pmtSid; int m_pmtVersion; const cChannel *m_Channel; cVideoInput *m_VideoInput; virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); public: cLivePatFilter(cVideoInput *VideoInput, const cChannel *Channel); }; cLivePatFilter::cLivePatFilter(cVideoInput *VideoInput, const cChannel *Channel) { DEBUGLOG("cStreamdevPatFilter(\"%s\")", Channel->Name()); m_Channel = Channel; m_VideoInput = VideoInput; m_pmtPid = 0; m_pmtSid = 0; m_pmtVersion = -1; Set(0x00, 0x00); // PAT } void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { if (Pid == 0x00) { if (Tid == 0x00) { SI::PAT pat(Data, false); if (!pat.CheckCRCAndParse()) return; SI::PAT::Association assoc; for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { if (!assoc.isNITPid()) { const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId()); if (Channel && (Channel == m_Channel)) { int prevPmtPid = m_pmtPid; if (0 != (m_pmtPid = assoc.getPid())) { if (m_pmtPid != prevPmtPid) { m_pmtSid = assoc.getServiceId(); Add(m_pmtPid, 0x02); m_pmtVersion = -1; break; } return; } } } } } } else if (Pid == m_pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) { SI::PMT pmt(Data, false); if (!pmt.CheckCRCAndParse()) return; if (pmt.getServiceId() != m_pmtSid) return; // skip broken PMT records if (m_pmtVersion != -1) { if (m_pmtVersion != pmt.getVersionNumber()) { cFilter::Del(m_pmtPid, 0x02); m_pmtPid = 0; // this triggers PAT scan } return; } m_pmtVersion = pmt.getVersionNumber(); cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId()); if (Channel) { // Scan the stream-specific loop: SI::PMT::Stream stream; int Vpid = 0; int Ppid = 0; int Vtype = 0; int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated int Atypes[MAXAPIDS + 1] = { 0 }; int Dpids[MAXDPIDS + 1] = { 0 }; int Dtypes[MAXDPIDS + 1] = { 0 }; int Spids[MAXSPIDS + 1] = { 0 }; uchar SubtitlingTypes[MAXSPIDS + 1] = { 0 }; uint16_t CompositionPageIds[MAXSPIDS + 1] = { 0 }; uint16_t AncillaryPageIds[MAXSPIDS + 1] = { 0 }; char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" }; char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" }; char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" }; int Tpid = 0; int NumApids = 0; int NumDpids = 0; int NumSpids = 0; for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) { bool ProcessCaDescriptors = false; int esPid = stream.getPid(); switch (stream.getStreamType()) { case 1: // STREAMTYPE_11172_VIDEO case 2: // STREAMTYPE_13818_VIDEO case 0x1B: // MPEG4 Vpid = esPid; Ppid = pmt.getPCRPid(); Vtype = stream.getStreamType(); ProcessCaDescriptors = true; break; case 3: // STREAMTYPE_11172_AUDIO case 4: // STREAMTYPE_13818_AUDIO case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax { if (NumApids < MAXAPIDS) { Apids[NumApids] = esPid; Atypes[NumApids] = stream.getStreamType(); SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; SI::ISO639LanguageDescriptor::Language l; char *s = ALangs[NumApids]; int n = 0; for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) { if (*ld->languageCode != '-') { // some use "---" to indicate "none" if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } } break; default: ; } delete d; } NumApids++; } ProcessCaDescriptors = true; } break; case 5: // STREAMTYPE_13818_PRIVATE case 6: // STREAMTYPE_13818_PES_PRIVATE //XXX case 8: // STREAMTYPE_13818_DSMCC { int dpid = 0; int dtype = 0; char lang[MAXLANGCODE1] = { 0 }; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::AC3DescriptorTag: case SI::EnhancedAC3DescriptorTag: dpid = esPid; dtype = d->getDescriptorTag(); ProcessCaDescriptors = true; break; case SI::SubtitlingDescriptorTag: if (NumSpids < MAXSPIDS) { Spids[NumSpids] = esPid; SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d; SI::SubtitlingDescriptor::Subtitling sub; char *s = SLangs[NumSpids]; int n = 0; for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) { if (sub.languageCode[0]) { SubtitlingTypes[NumSpids] = sub.getSubtitlingType(); CompositionPageIds[NumSpids] = sub.getCompositionPageId(); AncillaryPageIds[NumSpids] = sub.getAncillaryPageId(); if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } NumSpids++; } break; case SI::TeletextDescriptorTag: Tpid = esPid; break; case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (dpid) { if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = dpid; Dtypes[NumDpids] = dtype; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } } } break; case 0x80: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // DigiCipher II VIDEO (ANSI/SCTE 57) Vpid = esPid; Ppid = pmt.getPCRPid(); Vtype = 0x02; // compression based upon MPEG-2 ProcessCaDescriptors = true; break; } // fall through case 0x81: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // ATSC A/53 AUDIO (ANSI/SCTE 57) char lang[MAXLANGCODE1] = { 0 }; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = esPid; Dtypes[NumDpids] = SI::AC3DescriptorTag; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } ProcessCaDescriptors = true; break; } // fall through case 0x82: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // STANDARD SUBTITLE (ANSI/SCTE 27) //TODO break; } // fall through case 0x83 ... 0xFF: // STREAMTYPE_USER_PRIVATE { char lang[MAXLANGCODE1] = { 0 }; bool IsAc3 = false; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::RegistrationDescriptorTag: { SI::RegistrationDescriptor *rd = (SI::RegistrationDescriptor *)d; // http://www.smpte-ra.org/mpegreg/mpegreg.html switch (rd->getFormatIdentifier()) { case 0x41432D33: // 'AC-3' IsAc3 = true; break; default: //printf("Format identifier: 0x%08X (pid: %d)\n", rd->getFormatIdentifier(), esPid); break; } } break; case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (IsAc3) { if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = esPid; Dtypes[NumDpids] = SI::AC3DescriptorTag; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } ProcessCaDescriptors = true; } } break; default: ;//printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number()); } } DEBUGLOG("Pat/Pmt Filter received pmt change"); cChannel *pmtChannel = m_VideoInput->PmtChannel(); pmtChannel->Modification(); pmtChannel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); pmtChannel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds); m_VideoInput->PmtChange(pmtChannel->Modification(CHANNELMOD_PIDS)); } } } // ---------------------------------------------------------------------------- cVideoInput::cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune) : m_Event(condVar), m_Mutex(mutex), m_IsRetune(retune) { m_Device = NULL;; m_PatFilter = NULL; m_Receiver = NULL;; m_Channel = NULL; m_VideoBuffer = NULL; m_Priority = 0; m_PmtChange = false; } cVideoInput::~cVideoInput() { Close(); } bool cVideoInput::Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer) { m_VideoBuffer = videoBuffer; m_Channel = channel; m_Priority = priority; m_Device = cDevice::GetDevice(m_Channel, m_Priority, false); if (m_Device != NULL) { INFOLOG("Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0); if (m_Device->SwitchChannel(m_Channel, false)) { DEBUGLOG("Creating new live Receiver"); //m_Device->SetCurrentChannel(m_Channel); m_SeenPmt = false; m_PatFilter = new cLivePatFilter(this, m_Channel); m_Receiver0 = new cLiveReceiver(this, m_Channel, m_Priority); m_Receiver = new cLiveReceiver(this, m_Channel, m_Priority); m_Device->AttachReceiver(m_Receiver0); m_Device->AttachFilter(m_PatFilter); cCamSlot *cs = m_Device->CamSlot(); if (m_Priority <= MINPRIORITY && cs) cs->StartDecrypting(); m_VideoBuffer->AttachInput(true); Start(); return true; } } return false; } void cVideoInput::Close() { INFOLOG("close video input ..."); Cancel(5); if (m_Device) { if (m_Receiver) { DEBUGLOG("Detaching Live Receiver"); m_Device->Detach(m_Receiver); } else { DEBUGLOG("No live receiver present"); } if (m_Receiver0) { DEBUGLOG("Detaching Live Receiver0"); m_Device->Detach(m_Receiver0); } else { DEBUGLOG("No live receiver present"); } if (m_PatFilter) { DEBUGLOG("Detaching Live Filter"); m_Device->Detach(m_PatFilter); } else { DEBUGLOG("No live filter present"); } if (m_Receiver) { DEBUGLOG("Deleting Live Receiver"); DELETENULL(m_Receiver); } if (m_Receiver0) { DEBUGLOG("Deleting Live Receiver0"); DELETENULL(m_Receiver0); } if (m_PatFilter) { DEBUGLOG("Deleting Live Filter"); DELETENULL(m_PatFilter); } //m_Device->SetCurrentChannel(NULL); cCamSlot *cs = m_Device->CamSlot(); if (m_Priority <= MINPRIORITY && cs) { cs->StartDecrypting(); if (!cs->IsDecrypting()) cs->Assign(NULL); } } m_Channel = NULL; m_Device = NULL; if (m_VideoBuffer) { m_VideoBuffer->AttachInput(false); m_VideoBuffer = NULL; } } bool cVideoInput::IsOpen() { if (m_Channel) return true; else return false; } cChannel *cVideoInput::PmtChannel() { return &m_Receiver->m_PmtChannel; } void cVideoInput::PmtChange(int pidChange) { if (pidChange) { INFOLOG("Video Input - new pmt, attaching receiver"); m_PmtChange = true; m_Device->Detach(m_Receiver); m_Receiver->SetPids(NULL); m_Receiver->SetPids(&m_Receiver->m_PmtChannel); m_Receiver->AddPid(m_Receiver->m_PmtChannel.Tpid()); m_Device->AttachReceiver(m_Receiver); cCamSlot *cs = m_Device->CamSlot(); if (m_Priority <= MINPRIORITY && cs) cs->StartDecrypting(); m_SeenPmt = true; } } inline void cVideoInput::Receive(uchar *data, int length) { if (m_PmtChange) { // generate pat/pmt so we can configure parsers later cPatPmtGenerator patPmtGenerator(&m_Receiver->m_PmtChannel); m_VideoBuffer->Put(patPmtGenerator.GetPat(), TS_SIZE); int Index = 0; while (uchar *pmt = patPmtGenerator.GetPmt(Index)) m_VideoBuffer->Put(pmt, TS_SIZE); m_PmtChange = false; } m_VideoBuffer->Put(data, length); } void cVideoInput::Retune() { INFOLOG("call retune ..."); cMutexLock lock(&m_Mutex); m_IsRetune = true; m_Event.Broadcast(); } void cVideoInput::Action() { cTimeMs starttime; while (Running()) { if (starttime.Elapsed() > (unsigned int)PmtTimeout*1000) { INFOLOG("VideoInput: no pat/pmt within timeout, falling back to channel pids"); m_Receiver->m_PmtChannel = *m_Channel; PmtChange(true); } if (m_SeenPmt) break; usleep(1000); } } vdr-plugin-vnsiserver-1.3.1/videoinput.h000066400000000000000000000034261257700704000203550ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #pragma once #include class cLivePatFilter; class cLiveReceiver; class cVideoBuffer; class cChannel; class cDevice; class cVideoInput : public cThread { friend class cLivePatFilter; friend class cLiveReceiver; public: cVideoInput(cCondVar &condVar, cMutex &mutex, bool &retune); virtual ~cVideoInput(); bool Open(const cChannel *channel, int priority, cVideoBuffer *videoBuffer); void Close(); bool IsOpen(); protected: virtual void Action(void); void PmtChange(int pidChange); cChannel *PmtChannel(); void Receive(uchar *data, int length); void Retune(); cDevice *m_Device; cLivePatFilter *m_PatFilter; cLiveReceiver *m_Receiver; cLiveReceiver *m_Receiver0; const cChannel *m_Channel; cVideoBuffer *m_VideoBuffer; int m_Priority; bool m_PmtChange; bool m_SeenPmt; cCondVar &m_Event; cMutex &m_Mutex; bool &m_IsRetune; }; vdr-plugin-vnsiserver-1.3.1/vnsi.c000066400000000000000000000150571257700704000171440ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include "vnsi.h" #include "vnsicommand.h" #include "setup.h" cPluginVNSIServer* cPluginVNSIServer::VNSIServer = NULL; cPluginVNSIServer::cPluginVNSIServer(void) { Server = NULL; VNSIServer = NULL; probe = new cDvbVsniDeviceProbe(); } cPluginVNSIServer::~cPluginVNSIServer() { // Clean up after yourself! delete probe; } const char *cPluginVNSIServer::CommandLineHelp(void) { return " -t n, --timeout=n stream data timeout in seconds (default: 10)\n" " -d , --device act as the primary device\n" " -s n, --test=n TS stream test file to simulate as channel\n" " -p n, --port=n tcp port to listen on\n"; } bool cPluginVNSIServer::ProcessArgs(int argc, char *argv[]) { // Implement command line argument processing here if applicable. static struct option long_options[] = { { "port", required_argument, NULL, 'p' }, { "timeout", required_argument, NULL, 't' }, { "device", no_argument, NULL, 'd' }, { "test", required_argument, NULL, 'T' }, { NULL, no_argument, NULL, 0 } }; int c; while ((c = getopt_long(argc, argv, "t:dT:p:", long_options, NULL)) != -1) { switch (c) { case 'p': if(optarg != NULL) VNSIServerConfig.listen_port = atoi(optarg); break; case 't': if(optarg != NULL) VNSIServerConfig.stream_timeout = atoi(optarg); break; case 'd': VNSIServerConfig.device = true; break; case 'T': if(optarg != NULL) { VNSIServerConfig.testStreamFile = optarg; struct stat file_stat; if (stat(VNSIServerConfig.testStreamFile, &file_stat) == 0) { VNSIServerConfig.testStreamActive = true; printf("vnsiserver: requested test stream file '%s' played now on all channels\n", *VNSIServerConfig.testStreamFile); } else printf("vnsiserver: requested test stream file '%s' not present, started without\n", *VNSIServerConfig.testStreamFile); } break; default: return false; } } return true; } bool cPluginVNSIServer::Initialize(void) { // Initialize any background activities the plugin shall perform. VNSIServerConfig.ConfigDirectory = ConfigDirectory(PLUGIN_NAME_I18N); VNSIServer = this; return true; } bool cPluginVNSIServer::Start(void) { INFOLOG("Starting vnsi server at port=%d\n", VNSIServerConfig.listen_port); Server = new cVNSIServer(VNSIServerConfig.listen_port); return true; } void cPluginVNSIServer::Stop(void) { delete Server; Server = NULL; } void cPluginVNSIServer::Housekeeping(void) { // Perform any cleanup or other regular tasks. } void cPluginVNSIServer::MainThreadHook(void) { // Perform actions in the context of the main program thread. // WARNING: Use with great care - see PLUGINS.html! } cString cPluginVNSIServer::Active(void) { // Return a message string if shutdown should be postponed return NULL; } time_t cPluginVNSIServer::WakeupTime(void) { // Return custom wakeup time for shutdown script return 0; } cMenuSetupPage *cPluginVNSIServer::SetupMenu(void) { // Return a setup menu in case the plugin supports one. return new cMenuSetupVNSI; } bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value) { // Parse your own setup parameters and store their values. if (!strcasecmp(Name, CONFNAME_PMTTIMEOUT)) PmtTimeout = atoi(Value); else if (!strcasecmp(Name, CONFNAME_TIMESHIFT)) TimeshiftMode = atoi(Value); else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERSIZE)) TimeshiftBufferSize = atoi(Value); else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERFILESIZE)) TimeshiftBufferFileSize = atoi(Value); else if (!strcasecmp(Name, CONFNAME_TIMESHIFTBUFFERDIR)) strn0cpy(TimeshiftBufferDir, Value, sizeof(TimeshiftBufferDir)); else if (!strcasecmp(Name, CONFNAME_PLAYRECORDING)) PlayRecording = atoi(Value); else if (!strcasecmp(Name, CONFNAME_AVOIDEPGSCAN)) AvoidEPGScan = atoi(Value); else return false; return true; } bool cPluginVNSIServer::Service(const char *Id, void *Data) { // Handle custom service requests from other plugins return false; } const char **cPluginVNSIServer::SVDRPHelpPages(void) { // Return help text for SVDRP commands this plugin implements return NULL; } cString cPluginVNSIServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) { // Process SVDRP commands this plugin implements return NULL; } void cPluginVNSIServer::StoreSetup(const char *Name, int Value) { if (VNSIServer) { if (VNSIServer->SetupParse(Name, itoa(Value))) { VNSIServer->SetupStore(Name, Value); Setup.Save(); } } } bool cDvbVsniDeviceProbe::Probe(int Adapter, int Frontend) { if (VNSIServerConfig.device) { new cDvbVnsiDevice(Adapter, Frontend); return true; } else return false; } cDvbVnsiDevice::cDvbVnsiDevice(int Adapter, int Frontend) :cDvbDevice(Adapter, Frontend) { VNSIServerConfig.pDevice = this; m_hasDecoder = false; } cDvbVnsiDevice::~cDvbVnsiDevice() { } bool cDvbVnsiDevice::HasDecoder(void) const { cMutexLock lock((cMutex*)&m_mutex); return m_hasDecoder; } int cDvbVnsiDevice::PlayVideo(const uchar *Data, int Length) { return Length; } int cDvbVnsiDevice::PlayAudio(const uchar *Data, int Length, uchar Id) { return Length; } void cDvbVnsiDevice::ActivateDecoder(bool active) { cMutexLock lock(&m_mutex); m_hasDecoder = active; } VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this! vdr-plugin-vnsiserver-1.3.1/vnsi.h000066400000000000000000000056251257700704000171510ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include "vnsiserver.h" static const char *VERSION = "1.3.1"; static const char *DESCRIPTION = "VDR-Network-Streaming-Interface (VNSI) Server"; extern int PmtTimeout; extern int TimeshiftMode; extern int TimeshiftBufferSize; extern int TimeshiftBufferFileSize; extern char TimeshiftBufferDir[PATH_MAX]; extern int PlayRecording; extern int AvoidEPGScan; class cDvbVsniDeviceProbe; class cPluginVNSIServer : public cPlugin { private: cVNSIServer *Server; static cPluginVNSIServer *VNSIServer; cDvbVsniDeviceProbe *probe; public: cPluginVNSIServer(void); virtual ~cPluginVNSIServer(); virtual const char *Version(void) { return VERSION; } virtual const char *Description(void) { return DESCRIPTION; } virtual const char *CommandLineHelp(void); virtual bool ProcessArgs(int argc, char *argv[]); virtual bool Initialize(void); virtual bool Start(void); virtual void Stop(void); virtual void Housekeeping(void); virtual void MainThreadHook(void); virtual cString Active(void); virtual time_t WakeupTime(void); virtual const char *MainMenuEntry(void) { return NULL; } virtual cOsdObject *MainMenuAction(void) { return NULL; } virtual cMenuSetupPage *SetupMenu(void); virtual bool SetupParse(const char *Name, const char *Value); virtual bool Service(const char *Id, void *Data = NULL); virtual const char **SVDRPHelpPages(void); virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); static void StoreSetup(const char *Name, int Value); }; class cDvbVsniDeviceProbe : public cDvbDeviceProbe { public: virtual bool Probe(int Adapter, int Frontend); }; class cDvbVnsiDevice : public cDvbDevice { public: cDvbVnsiDevice(int Adapter, int Frontend); virtual ~cDvbVnsiDevice(); virtual bool HasDecoder(void) const; int PlayVideo(const uchar *Data, int Length); int PlayAudio(const uchar *Data, int Length, uchar Id); void ActivateDecoder(bool active); protected: bool m_hasDecoder; cMutex m_mutex; }; vdr-plugin-vnsiserver-1.3.1/vnsiclient.c000066400000000000000000002062641257700704000203450ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include #include #include #include #include #include #include #include "vnsi.h" #include "config.h" #include "vnsicommand.h" #include "recordingscache.h" #include "vnsiclient.h" #include "streamer.h" #include "vnsiserver.h" #include "recplayer.h" #include "vnsiosd.h" #include "requestpacket.h" #include "responsepacket.h" #include "hash.h" #include "channelfilter.h" #include "channelscancontrol.h" cMutex cVNSIClient::m_timerLock; bool cVNSIClient::m_inhibidDataUpdates = false; cVNSIClient::cVNSIClient(int fd, unsigned int id, const char *ClientAdr) : m_Id(id), m_loggedIn(false), m_StatusInterfaceEnabled(false), m_Streamer(NULL), m_isStreaming(false), m_bSupportRDS(false), m_ClientAddress(ClientAdr), m_RecPlayer(NULL), m_req(NULL), m_resp(NULL), m_Osd(NULL), m_ChannelScanControl(this) { m_socket.SetHandle(fd); Start(); } cVNSIClient::~cVNSIClient() { DEBUGLOG("%s", __FUNCTION__); StopChannelStreaming(); m_ChannelScanControl.StopScan(); m_socket.close(); // force closing connection Cancel(10); DEBUGLOG("done"); } void cVNSIClient::Action(void) { uint32_t channelID; uint32_t requestID; uint32_t opcode; uint32_t dataLength; uint8_t* data; while (Running()) { if (!m_socket.read((uint8_t*)&channelID, sizeof(uint32_t))) break; channelID = ntohl(channelID); if (channelID == 1) { if (!m_socket.read((uint8_t*)&requestID, sizeof(uint32_t), 10000)) break; requestID = ntohl(requestID); if (!m_socket.read((uint8_t*)&opcode, sizeof(uint32_t), 10000)) break; opcode = ntohl(opcode); if (!m_socket.read((uint8_t*)&dataLength, sizeof(uint32_t), 10000)) break; dataLength = ntohl(dataLength); if (dataLength > 200000) // a random sanity limit { ERRORLOG("dataLength > 200000!"); break; } if (dataLength) { data = (uint8_t*)malloc(dataLength); if (!data) { ERRORLOG("Extra data buffer malloc error"); break; } if (!m_socket.read(data, dataLength, 10000)) { ERRORLOG("Could not read data"); free(data); break; } } else { data = NULL; } DEBUGLOG("Received chan=%u, ser=%u, op=%u, edl=%u", channelID, requestID, opcode, dataLength); if (!m_loggedIn && (opcode != VNSI_LOGIN)) { ERRORLOG("Clients must be logged in before sending commands! Aborting."); if (data) free(data); break; } cRequestPacket* req = new cRequestPacket(requestID, opcode, data, dataLength); processRequest(req); } else { ERRORLOG("Incoming channel number unknown"); break; } } /* If thread is ended due to closed connection delete a possible running stream here */ StopChannelStreaming(); m_ChannelScanControl.StopScan(); // Shutdown OSD if (m_Osd) { delete m_Osd; m_Osd = NULL; } } bool cVNSIClient::StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout) { m_Streamer = new cLiveStreamer(m_Id, m_bSupportRDS, timeshift, timeout); m_isStreaming = m_Streamer->StreamChannel(channel, priority, &m_socket, m_resp); return m_isStreaming; } void cVNSIClient::StopChannelStreaming() { m_isStreaming = false; if (m_Streamer) { delete m_Streamer; m_Streamer = NULL; } } void cVNSIClient::TimerChange(const cTimer *Timer, eTimerChange Change) { TimerChange(); } void cVNSIClient::TimerChange() { cMutexLock lock(&m_msgLock); if (m_StatusInterfaceEnabled) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_TIMERCHANGE)) { delete resp; return; } resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } } void cVNSIClient::ChannelsChange() { cMutexLock lock(&m_msgLock); if (!m_StatusInterfaceEnabled) return; cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_CHANNELCHANGE)) { delete resp; return; } resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::RecordingsChange() { cMutexLock lock(&m_msgLock); if (!m_StatusInterfaceEnabled) return; cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_RECORDINGSCHANGE)) { delete resp; return; } resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::EpgChange() { cMutexLock lock(&m_msgLock); if (!m_StatusInterfaceEnabled) return; cSchedulesLock MutexLock; const cSchedules *schedules = cSchedules::Schedules(MutexLock); if (!schedules) return; std::map::iterator it; for (const cSchedule *schedule = schedules->First(); schedule; schedule = schedules->Next(schedule)) { cEvent *lastEvent = schedule->Events()->Last(); if (!lastEvent) continue; Channels.Lock(false); const cChannel *channel = Channels.GetByChannelID(schedule->ChannelID()); Channels.Unlock(); if (!channel) continue; if (!VNSIChannelFilter.PassFilter(*channel)) continue; uint32_t channelId = CreateStringHash(schedule->ChannelID().ToString()); it = m_epgUpdate.find(channelId); if (it != m_epgUpdate.end() && it->second.lastEvent >= lastEvent->StartTime()) { continue; } if (it->second.attempts > 3) { continue; } it->second.attempts++; INFOLOG("Trigger EPG update for channel %s, id: %d", channel->Name(), channelId); cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_EPGCHANGE)) { delete resp; return; } resp->add_U32(channelId); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } } void cVNSIClient::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) { cMutexLock lock(&m_msgLock); if (m_StatusInterfaceEnabled) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_RECORDING)) { delete resp; return; } resp->add_U32(Device->CardIndex()); resp->add_U32(On); if (Name) resp->add_String(Name); else resp->add_String(""); if (FileName) resp->add_String(FileName); else resp->add_String(""); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } } void cVNSIClient::OsdStatusMessage(const char *Message) { cMutexLock lock(&m_msgLock); if (m_StatusInterfaceEnabled && Message) { /* Ignore this messages */ if (strcasecmp(Message, trVDR("Channel not available!")) == 0) return; else if (strcasecmp(Message, trVDR("Delete timer?")) == 0) return; else if (strcasecmp(Message, trVDR("Delete recording?")) == 0) return; else if (strcasecmp(Message, trVDR("Press any key to cancel shutdown")) == 0) return; else if (strcasecmp(Message, trVDR("Press any key to cancel restart")) == 0) return; else if (strcasecmp(Message, trVDR("Editing - shut down anyway?")) == 0) return; else if (strcasecmp(Message, trVDR("Recording - shut down anyway?")) == 0) return; else if (strcasecmp(Message, trVDR("shut down anyway?")) == 0) return; else if (strcasecmp(Message, trVDR("Recording - restart anyway?")) == 0) return; else if (strcasecmp(Message, trVDR("Editing - restart anyway?")) == 0) return; else if (strcasecmp(Message, trVDR("Delete channel?")) == 0) return; else if (strcasecmp(Message, trVDR("Timer still recording - really delete?")) == 0) return; else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return; else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return; else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return; else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return; else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return; else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return; else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return; else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return; cResponsePacket *resp = new cResponsePacket(); if (!resp->initStatus(VNSI_STATUS_MESSAGE)) { delete resp; return; } resp->add_U32(0); resp->add_String(Message); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } } void cVNSIClient::ChannelChange(const cChannel *Channel) { cMutexLock lock(&m_msgLock); if (m_isStreaming && m_Streamer) { m_Streamer->RetuneChannel(Channel); } } bool cVNSIClient::processRequest(cRequestPacket* req) { cMutexLock lock(&m_msgLock); m_req = req; m_resp = new cResponsePacket(); if (!m_resp->init(m_req->getRequestID())) { ERRORLOG("Response packet init fail"); delete m_resp; delete m_req; m_resp = NULL; m_req = NULL; return false; } bool result = false; switch(m_req->getOpCode()) { /** OPCODE 1 - 19: VNSI network functions for general purpose */ case VNSI_LOGIN: result = process_Login(); break; case VNSI_GETTIME: result = process_GetTime(); break; case VNSI_ENABLESTATUSINTERFACE: result = process_EnableStatusInterface(); break; case VNSI_PING: result = process_Ping(); break; case VNSI_GETSETUP: result = process_GetSetup(); break; case VNSI_STORESETUP: result = process_StoreSetup(); break; /** OPCODE 20 - 39: VNSI network functions for live streaming */ case VNSI_CHANNELSTREAM_OPEN: result = processChannelStream_Open(); break; case VNSI_CHANNELSTREAM_CLOSE: result = processChannelStream_Close(); break; case VNSI_CHANNELSTREAM_SEEK: result = processChannelStream_Seek(); break; /** OPCODE 40 - 59: VNSI network functions for recording streaming */ case VNSI_RECSTREAM_OPEN: result = processRecStream_Open(); break; case VNSI_RECSTREAM_CLOSE: result = processRecStream_Close(); break; case VNSI_RECSTREAM_GETBLOCK: result = processRecStream_GetBlock(); break; case VNSI_RECSTREAM_POSTOFRAME: result = processRecStream_PositionFromFrameNumber(); break; case VNSI_RECSTREAM_FRAMETOPOS: result = processRecStream_FrameNumberFromPosition(); break; case VNSI_RECSTREAM_GETIFRAME: result = processRecStream_GetIFrame(); break; case VNSI_RECSTREAM_GETLENGTH: result = processRecStream_GetLength(); break; /** OPCODE 60 - 79: VNSI network functions for channel access */ case VNSI_CHANNELS_GETCOUNT: result = processCHANNELS_ChannelsCount(); break; case VNSI_CHANNELS_GETCHANNELS: result = processCHANNELS_GetChannels(); break; case VNSI_CHANNELGROUP_GETCOUNT: result = processCHANNELS_GroupsCount(); break; case VNSI_CHANNELGROUP_LIST: result = processCHANNELS_GroupList(); break; case VNSI_CHANNELGROUP_MEMBERS: result = processCHANNELS_GetGroupMembers(); break; case VNSI_CHANNELS_GETCAIDS: result = processCHANNELS_GetCaids(); break; case VNSI_CHANNELS_GETWHITELIST: result = processCHANNELS_GetWhitelist(); break; case VNSI_CHANNELS_GETBLACKLIST: result = processCHANNELS_GetBlacklist(); break; case VNSI_CHANNELS_SETWHITELIST: result = processCHANNELS_SetWhitelist(); break; case VNSI_CHANNELS_SETBLACKLIST: result = processCHANNELS_SetBlacklist(); break; /** OPCODE 80 - 99: VNSI network functions for timer access */ case VNSI_TIMER_GETCOUNT: result = processTIMER_GetCount(); break; case VNSI_TIMER_GET: result = processTIMER_Get(); break; case VNSI_TIMER_GETLIST: result = processTIMER_GetList(); break; case VNSI_TIMER_ADD: result = processTIMER_Add(); break; case VNSI_TIMER_DELETE: result = processTIMER_Delete(); break; case VNSI_TIMER_UPDATE: result = processTIMER_Update(); break; /** OPCODE 100 - 119: VNSI network functions for recording access */ case VNSI_RECORDINGS_DISKSIZE: result = processRECORDINGS_GetDiskSpace(); break; case VNSI_RECORDINGS_GETCOUNT: result = processRECORDINGS_GetCount(); break; case VNSI_RECORDINGS_GETLIST: result = processRECORDINGS_GetList(); break; case VNSI_RECORDINGS_RENAME: result = processRECORDINGS_Rename(); break; case VNSI_RECORDINGS_DELETE: result = processRECORDINGS_Delete(); break; case VNSI_RECORDINGS_GETEDL: result = processRECORDINGS_GetEdl(); break; /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */ case VNSI_EPG_GETFORCHANNEL: result = processEPG_GetForChannel(); break; /** OPCODE 140 - 159: VNSI network functions for channel scanning */ case VNSI_SCAN_SUPPORTED: result = processSCAN_ScanSupported(); break; case VNSI_SCAN_GETCOUNTRIES: result = processSCAN_GetCountries(); break; case VNSI_SCAN_GETSATELLITES: result = processSCAN_GetSatellites(); break; case VNSI_SCAN_START: result = processSCAN_Start(); break; case VNSI_SCAN_STOP: result = processSCAN_Stop(); break; case VNSI_SCAN_SUPPORTED_TYPES: result = processSCAN_GetSupportedTypes(); break; /** OPCODE 160 - 179: VNSI network functions for OSD */ case VNSI_OSD_CONNECT: result = processOSD_Connect(); break; case VNSI_OSD_DISCONNECT: result = processOSD_Disconnect(); break; case VNSI_OSD_HITKEY: result = processOSD_Hitkey(); break; /** OPCODE 180 - 189: VNSI network functions for deleted recording access */ case VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED: result = processRECORDINGS_DELETED_Supported(); break; case VNSI_RECORDINGS_DELETED_GETCOUNT: result = processRECORDINGS_DELETED_GetCount(); break; case VNSI_RECORDINGS_DELETED_GETLIST: result = processRECORDINGS_DELETED_GetList(); break; case VNSI_RECORDINGS_DELETED_DELETE: result = processRECORDINGS_DELETED_Delete(); break; case VNSI_RECORDINGS_DELETED_UNDELETE: result = processRECORDINGS_DELETED_Undelete(); break; case VNSI_RECORDINGS_DELETED_DELETE_ALL: result = processRECORDINGS_DELETED_DeleteAll(); break; } delete m_resp; m_resp = NULL; delete m_req; m_req = NULL; return result; } /** OPCODE 1 - 19: VNSI network functions for general purpose */ bool cVNSIClient::process_Login() /* OPCODE 1 */ { if (m_req->getDataLength() <= 4) return false; m_protocolVersion = m_req->extract_U32(); m_req->extract_U8(); const char *clientName = m_req->extract_String(); INFOLOG("Welcome client '%s' with protocol version '%u'", clientName, m_protocolVersion); // Send the login reply time_t timeNow = time(NULL); struct tm* timeStruct = localtime(&timeNow); int timeOffset = timeStruct->tm_gmtoff; m_resp->add_U32(VNSI_PROTOCOLVERSION); m_resp->add_U32(timeNow); m_resp->add_S32(timeOffset); m_resp->add_String("VDR-Network-Streaming-Interface (VNSI) Server"); m_resp->add_String(VNSI_SERVER_VERSION); m_resp->finalise(); if (m_protocolVersion < VNSI_MIN_PROTOCOLVERSION) ERRORLOG("Client '%s' have a not allowed protocol version '%u', terminating client", clientName, m_protocolVersion); else SetLoggedIn(true); if (m_protocolVersion < VNSI_RDS_PROTOCOLVERSION) { INFOLOG("RDS not supported on client '%s' and stream type disabled", clientName); m_bSupportRDS = false; } else { m_bSupportRDS = true; } m_socket.write(m_resp->getPtr(), m_resp->getLen()); delete[] clientName; return true; } bool cVNSIClient::process_GetTime() /* OPCODE 2 */ { time_t timeNow = time(NULL); struct tm* timeStruct = localtime(&timeNow); int timeOffset = timeStruct->tm_gmtoff; m_resp->add_U32(timeNow); m_resp->add_S32(timeOffset); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::process_EnableStatusInterface() { bool enabled = m_req->extract_U8(); SetStatusInterface(enabled); SetPriority(1); m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::process_Ping() /* OPCODE 7 */ { m_resp->add_U32(1); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::process_GetSetup() /* OPCODE 8 */ { char* name = m_req->extract_String(); if (!strcasecmp(name, CONFNAME_PMTTIMEOUT)) m_resp->add_U32(PmtTimeout); else if (!strcasecmp(name, CONFNAME_TIMESHIFT)) m_resp->add_U32(TimeshiftMode); else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE)) m_resp->add_U32(TimeshiftBufferSize); else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE)) m_resp->add_U32(TimeshiftBufferFileSize); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::process_StoreSetup() /* OPCODE 9 */ { char* name = m_req->extract_String(); if (!strcasecmp(name, CONFNAME_PMTTIMEOUT)) { int value = m_req->extract_U32(); cPluginVNSIServer::StoreSetup(CONFNAME_PMTTIMEOUT, value); } else if (!strcasecmp(name, CONFNAME_TIMESHIFT)) { int value = m_req->extract_U32(); cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFT, value); } else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERSIZE)) { int value = m_req->extract_U32(); cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERSIZE, value); } else if (!strcasecmp(name, CONFNAME_TIMESHIFTBUFFERFILESIZE)) { int value = m_req->extract_U32(); cPluginVNSIServer::StoreSetup(CONFNAME_TIMESHIFTBUFFERFILESIZE, value); } else if (!strcasecmp(name, CONFNAME_PLAYRECORDING)) { int value = m_req->extract_U32(); cPluginVNSIServer::StoreSetup(CONFNAME_PLAYRECORDING, value); } m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } /** OPCODE 20 - 39: VNSI network functions for live streaming */ bool cVNSIClient::processChannelStream_Open() /* OPCODE 20 */ { uint32_t uid = m_req->extract_U32(); int32_t priority = m_req->extract_S32(); uint8_t timeshift = m_req->extract_U8(); uint32_t timeout = m_req->extract_U32(); if(timeout == 0) timeout = VNSIServerConfig.stream_timeout; if (m_isStreaming) StopChannelStreaming(); Channels.Lock(false); const cChannel *channel = NULL; // try to find channel by uid first channel = FindChannelByUID(uid); Channels.Unlock(); // try channelnumber if (channel == NULL) channel = Channels.GetByNumber(uid); if (channel == NULL) { ERRORLOG("Can't find channel %08x", uid); m_resp->add_U32(VNSI_RET_DATAINVALID); } else { if (StartChannelStreaming(channel, priority, timeshift, timeout)) { INFOLOG("Started streaming of channel %s (timeout %i seconds)", channel->Name(), timeout); // return here without sending the response // (was already done in cLiveStreamer::StreamChannel) return true; } DEBUGLOG("Can't stream channel %s", channel->Name()); m_resp->add_U32(VNSI_RET_DATALOCKED); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return false; } bool cVNSIClient::processChannelStream_Close() /* OPCODE 21 */ { if (m_isStreaming) StopChannelStreaming(); return true; } bool cVNSIClient::processChannelStream_Seek() /* OPCODE 22 */ { uint32_t serial = 0; if (m_isStreaming && m_Streamer) { int64_t time = m_req->extract_S64(); if (m_Streamer->SeekTime(time, serial)) m_resp->add_U32(VNSI_RET_OK); else m_resp->add_U32(VNSI_RET_ERROR); } else m_resp->add_U32(VNSI_RET_ERROR); m_resp->add_U32(serial); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } /** OPCODE 40 - 59: VNSI network functions for recording streaming */ bool cVNSIClient::processRecStream_Open() /* OPCODE 40 */ { cRecording *recording = NULL; uint32_t uid = m_req->extract_U32(); recording = cRecordingsCache::GetInstance().Lookup(uid); if (recording && m_RecPlayer == NULL) { m_RecPlayer = new cRecPlayer(recording); m_resp->add_U32(VNSI_RET_OK); m_resp->add_U32(m_RecPlayer->getLengthFrames()); m_resp->add_U64(m_RecPlayer->getLengthBytes()); #if VDRVERSNUM < 10703 m_resp->add_U8(true);//added for TS #else m_resp->add_U8(recording->IsPesRecording());//added for TS #endif } else { m_resp->add_U32(VNSI_RET_DATAUNKNOWN); ERRORLOG("%s - unable to start recording !", __FUNCTION__); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRecStream_Close() /* OPCODE 41 */ { if (m_RecPlayer) { delete m_RecPlayer; m_RecPlayer = NULL; } m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRecStream_GetBlock() /* OPCODE 42 */ { if (m_isStreaming) { ERRORLOG("Get block called during live streaming"); return false; } if (!m_RecPlayer) { ERRORLOG("Get block called when no recording open"); return false; } uint64_t position = m_req->extract_U64(); uint32_t amount = m_req->extract_U32(); uint8_t* p = m_resp->reserve(amount); uint32_t amountReceived = m_RecPlayer->getBlock(p, position, amount); if(amount > amountReceived) m_resp->unreserve(amount - amountReceived); if (!amountReceived) { m_resp->add_U32(0); DEBUGLOG("written 4(0) as getblock got 0"); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRecStream_PositionFromFrameNumber() /* OPCODE 43 */ { uint64_t retval = 0; uint32_t frameNumber = m_req->extract_U32(); if (m_RecPlayer) retval = m_RecPlayer->positionFromFrameNumber(frameNumber); m_resp->add_U64(retval); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); DEBUGLOG("Wrote posFromFrameNum reply to client"); return true; } bool cVNSIClient::processRecStream_FrameNumberFromPosition() /* OPCODE 44 */ { uint32_t retval = 0; uint64_t position = m_req->extract_U64(); if (m_RecPlayer) retval = m_RecPlayer->frameNumberFromPosition(position); m_resp->add_U32(retval); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); DEBUGLOG("Wrote frameNumFromPos reply to client"); return true; } bool cVNSIClient::processRecStream_GetIFrame() /* OPCODE 45 */ { bool success = false; uint32_t frameNumber = m_req->extract_U32(); uint32_t direction = m_req->extract_U32(); uint64_t rfilePosition = 0; uint32_t rframeNumber = 0; uint32_t rframeLength = 0; if (m_RecPlayer) success = m_RecPlayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength); // returns file position, frame number, length if (success) { m_resp->add_U64(rfilePosition); m_resp->add_U32(rframeNumber); m_resp->add_U32(rframeLength); } else { m_resp->add_U32(0); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); DEBUGLOG("Wrote GNIF reply to client %lu %u %u", rfilePosition, rframeNumber, rframeLength); return true; } bool cVNSIClient::processRecStream_GetLength() /* OPCODE 46 */ { uint64_t length = 0; if (m_RecPlayer) { m_RecPlayer->reScan(); length = m_RecPlayer->getLengthBytes(); } m_resp->add_U64(length); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } /** OPCODE 60 - 79: VNSI network functions for channel access */ bool cVNSIClient::processCHANNELS_ChannelsCount() /* OPCODE 61 */ { Channels.Lock(false); int count = Channels.MaxNumber(); Channels.Unlock(); m_resp->add_U32(count); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GetChannels() /* OPCODE 63 */ { if (m_req->getDataLength() != 5) return false; bool radio = m_req->extract_U32(); bool filter = m_req->extract_U8(); Channels.Lock(false); cString caids; int caid; int caid_idx; for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { if (radio != cVNSIChannelFilter::IsRadio(channel)) continue; // skip invalid channels if (channel->Sid() == 0) continue; // check filter if (filter && !VNSIChannelFilter.PassFilter(*channel)) continue; uint32_t uuid = CreateChannelUID(channel); m_resp->add_U32(channel->Number()); m_resp->add_String(m_toUTF8.Convert(channel->Name())); m_resp->add_String(m_toUTF8.Convert(channel->Provider())); m_resp->add_U32(uuid); m_resp->add_U32(channel->Ca(0)); caid_idx = 0; caids = "caids:"; while((caid = channel->Ca(caid_idx)) != 0) { caids = cString::sprintf("%s%d;", (const char*)caids, caid); caid_idx++; } m_resp->add_String((const char*)caids); if (m_protocolVersion >= 6) { m_resp->add_String(CreatePiconRef(channel)); } // create entry in EPG map on first query std::map::iterator it; it = m_epgUpdate.find(uuid); if (it == m_epgUpdate.end()) { m_epgUpdate[uuid].lastEvent = 0; m_epgUpdate[uuid].attempts = 0; } } Channels.Unlock(); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GroupsCount() { uint32_t type = m_req->extract_U32(); Channels.Lock(false); m_channelgroups[0].clear(); m_channelgroups[1].clear(); switch(type) { // get groups defined in channels.conf default: case 0: CreateChannelGroups(false); break; // automatically create groups case 1: CreateChannelGroups(true); break; } Channels.Unlock(); uint32_t count = m_channelgroups[0].size() + m_channelgroups[1].size(); m_resp->add_U32(count); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GroupList() { uint32_t radio = m_req->extract_U8(); std::map::iterator i; for(i = m_channelgroups[radio].begin(); i != m_channelgroups[radio].end(); i++) { m_resp->add_String(i->second.name.c_str()); m_resp->add_U8(i->second.radio); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GetGroupMembers() { char* groupname = m_req->extract_String(); uint32_t radio = m_req->extract_U8(); bool filter = m_req->extract_U8(); int index = 0; // unknown group if(m_channelgroups[radio].find(groupname) == m_channelgroups[radio].end()) { delete[] groupname; m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool automatic = m_channelgroups[radio][groupname].automatic; std::string name; Channels.Lock(false); for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { if(automatic && !channel->GroupSep()) name = channel->Provider(); else { if(channel->GroupSep()) { name = channel->Name(); continue; } } if(name.empty()) continue; if(cVNSIChannelFilter::IsRadio(channel) != radio) continue; // check filter if (filter && !VNSIChannelFilter.PassFilter(*channel)) continue; if(name == groupname) { m_resp->add_U32(CreateChannelUID(channel)); m_resp->add_U32(++index); } } Channels.Unlock(); delete[] groupname; m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GetCaids() { uint32_t uid = m_req->extract_U32(); Channels.Lock(false); const cChannel *channel = NULL; channel = FindChannelByUID(uid); Channels.Unlock(); if (channel != NULL) { int caid; int idx = 0; while((caid = channel->Ca(idx)) != 0) { m_resp->add_U32(caid); idx++; } } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GetWhitelist() { bool radio = m_req->extract_U8(); std::vector *providers; if(radio) providers = &VNSIChannelFilter.m_providersRadio; else providers = &VNSIChannelFilter.m_providersVideo; VNSIChannelFilter.m_Mutex.Lock(); for(unsigned int i=0; isize(); i++) { m_resp->add_String((*providers)[i].m_name.c_str()); m_resp->add_U32((*providers)[i].m_caid); } VNSIChannelFilter.m_Mutex.Unlock(); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_GetBlacklist() { bool radio = m_req->extract_U8(); std::vector *channels; if(radio) channels = &VNSIChannelFilter.m_channelsRadio; else channels = &VNSIChannelFilter.m_channelsVideo; VNSIChannelFilter.m_Mutex.Lock(); for(unsigned int i=0; isize(); i++) { m_resp->add_U32((*channels)[i]); } VNSIChannelFilter.m_Mutex.Unlock(); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_SetWhitelist() { bool radio = m_req->extract_U8(); cVNSIProvider provider; std::vector *providers; if(radio) providers = &VNSIChannelFilter.m_providersRadio; else providers = &VNSIChannelFilter.m_providersVideo; VNSIChannelFilter.m_Mutex.Lock(); providers->clear(); while(!m_req->end()) { char *str = m_req->extract_String(); provider.m_name = str; provider.m_caid = m_req->extract_U32(); delete [] str; providers->push_back(provider); } VNSIChannelFilter.StoreWhitelist(radio); VNSIChannelFilter.m_Mutex.Unlock(); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processCHANNELS_SetBlacklist() { bool radio = m_req->extract_U8(); cVNSIProvider provider; std::vector *channels; if(radio) channels = &VNSIChannelFilter.m_channelsRadio; else channels = &VNSIChannelFilter.m_channelsVideo; VNSIChannelFilter.m_Mutex.Lock(); channels->clear(); int id; while(!m_req->end()) { id = m_req->extract_U32(); channels->push_back(id); } VNSIChannelFilter.StoreBlacklist(radio); VNSIChannelFilter.m_Mutex.Unlock(); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } void cVNSIClient::CreateChannelGroups(bool automatic) { std::string groupname; for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { bool isRadio = cVNSIChannelFilter::IsRadio(channel); if(automatic && !channel->GroupSep()) groupname = channel->Provider(); else if(!automatic && channel->GroupSep()) groupname = channel->Name(); if(groupname.empty()) continue; if(m_channelgroups[isRadio].find(groupname) == m_channelgroups[isRadio].end()) { ChannelGroup group; group.name = groupname; group.radio = isRadio; group.automatic = automatic; m_channelgroups[isRadio][groupname] = group; } } } /** OPCODE 80 - 99: VNSI network functions for timer access */ bool cVNSIClient::processTIMER_GetCount() /* OPCODE 80 */ { cMutexLock lock(&m_timerLock); int count = Timers.Count(); m_resp->add_U32(count); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processTIMER_Get() /* OPCODE 81 */ { cMutexLock lock(&m_timerLock); uint32_t number = m_req->extract_U32(); int numTimers = Timers.Count(); if (numTimers > 0) { cTimer *timer = Timers.Get(number-1); if (timer) { m_resp->add_U32(VNSI_RET_OK); m_resp->add_U32(timer->Index()+1); m_resp->add_U32(timer->HasFlags(tfActive)); m_resp->add_U32(timer->Recording()); m_resp->add_U32(timer->Pending()); m_resp->add_U32(timer->Priority()); m_resp->add_U32(timer->Lifetime()); m_resp->add_U32(timer->Channel()->Number()); m_resp->add_U32(CreateChannelUID(timer->Channel())); m_resp->add_U32(timer->StartTime()); m_resp->add_U32(timer->StopTime()); m_resp->add_U32(timer->Day()); m_resp->add_U32(timer->WeekDays()); m_resp->add_String(m_toUTF8.Convert(timer->File())); } else m_resp->add_U32(VNSI_RET_DATAUNKNOWN); } else m_resp->add_U32(VNSI_RET_DATAUNKNOWN); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processTIMER_GetList() /* OPCODE 82 */ { cMutexLock lock(&m_timerLock); cTimer *timer; int numTimers = Timers.Count(); m_resp->add_U32(numTimers); for (int i = 0; i < numTimers; i++) { timer = Timers.Get(i); if (!timer) continue; m_resp->add_U32(timer->Index()+1); m_resp->add_U32(timer->HasFlags(tfActive)); m_resp->add_U32(timer->Recording()); m_resp->add_U32(timer->Pending()); m_resp->add_U32(timer->Priority()); m_resp->add_U32(timer->Lifetime()); m_resp->add_U32(timer->Channel()->Number()); m_resp->add_U32(CreateChannelUID(timer->Channel())); m_resp->add_U32(timer->StartTime()); m_resp->add_U32(timer->StopTime()); m_resp->add_U32(timer->Day()); m_resp->add_U32(timer->WeekDays()); m_resp->add_String(m_toUTF8.Convert(timer->File())); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processTIMER_Add() /* OPCODE 83 */ { cMutexLock lock(&m_timerLock); uint32_t flags = m_req->extract_U32() > 0 ? tfActive : tfNone; uint32_t priority = m_req->extract_U32(); uint32_t lifetime = m_req->extract_U32(); uint32_t channelid = m_req->extract_U32(); time_t startTime = m_req->extract_U32(); time_t stopTime = m_req->extract_U32(); time_t day = m_req->extract_U32(); uint32_t weekdays = m_req->extract_U32(); const char *file = m_req->extract_String(); const char *aux = m_req->extract_String(); // handle instant timers if(startTime == -1 || startTime == 0) { startTime = time(NULL); } struct tm tm_r; struct tm *time = localtime_r(&startTime, &tm_r); if (day <= 0) day = cTimer::SetTime(startTime, 0); int start = time->tm_hour * 100 + time->tm_min; time = localtime_r(&stopTime, &tm_r); int stop = time->tm_hour * 100 + time->tm_min; cString buffer; const cChannel* channel = FindChannelByUID(channelid); if(channel != NULL) { buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux); } delete[] file; delete[] aux; cTimer *timer = new cTimer; if (timer->Parse(buffer)) { cTimer *t = Timers.GetTimer(timer); if (!t) { Timers.Add(timer); Timers.SetModified(); INFOLOG("Timer %s added", *timer->ToDescr()); m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } else { ERRORLOG("Timer already defined: %d %s", t->Index() + 1, *t->ToText()); m_resp->add_U32(VNSI_RET_DATALOCKED); } } else { ERRORLOG("Error in timer settings"); m_resp->add_U32(VNSI_RET_DATAINVALID); } delete timer; m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processTIMER_Delete() /* OPCODE 84 */ { cMutexLock lock(&m_timerLock); uint32_t number = m_req->extract_U32(); bool force = m_req->extract_U32(); if (number <= 0 || number > (uint32_t)Timers.Count()) { ERRORLOG("Unable to delete timer - invalid timer identifier"); m_resp->add_U32(VNSI_RET_DATAINVALID); } else { cTimer *timer = Timers.Get(number-1); if (timer) { if (!Timers.BeingEdited()) { if (timer->Recording()) { if (force) { timer->Skip(); cRecordControls::Process(time(NULL)); } else { ERRORLOG("Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number); m_resp->add_U32(VNSI_RET_RECRUNNING); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } } INFOLOG("Deleting timer %s", *timer->ToDescr()); Timers.Del(timer); Timers.SetModified(); m_resp->add_U32(VNSI_RET_OK); } else { ERRORLOG("Unable to delete timer - timers being edited at VDR"); m_resp->add_U32(VNSI_RET_DATALOCKED); } } else { ERRORLOG("Unable to delete timer - invalid timer identifier"); m_resp->add_U32(VNSI_RET_DATAINVALID); } } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processTIMER_Update() /* OPCODE 85 */ { cMutexLock lock(&m_timerLock); int length = m_req->getDataLength(); uint32_t index = m_req->extract_U32(); bool active = m_req->extract_U32(); cTimer *timer = Timers.Get(index - 1); if (!timer) { ERRORLOG("Timer \"%u\" not defined", index); m_resp->add_U32(VNSI_RET_DATAUNKNOWN); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } cTimer t = *timer; if (length == 8) { if (active) t.SetFlags(tfActive); else t.ClrFlags(tfActive); } else { uint32_t flags = active ? tfActive : tfNone; uint32_t priority = m_req->extract_U32(); uint32_t lifetime = m_req->extract_U32(); uint32_t channelid = m_req->extract_U32(); time_t startTime = m_req->extract_U32(); time_t stopTime = m_req->extract_U32(); time_t day = m_req->extract_U32(); uint32_t weekdays = m_req->extract_U32(); const char *file = m_req->extract_String(); const char *aux = m_req->extract_String(); struct tm tm_r; struct tm *time = localtime_r(&startTime, &tm_r); if (day <= 0) day = cTimer::SetTime(startTime, 0); int start = time->tm_hour * 100 + time->tm_min; time = localtime_r(&stopTime, &tm_r); int stop = time->tm_hour * 100 + time->tm_min; cString buffer; const cChannel* channel = FindChannelByUID(channelid); if(channel != NULL) { buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, (const char*)channel->GetChannelID().ToString(), *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux); } delete[] file; delete[] aux; if (!t.Parse(buffer)) { ERRORLOG("Error in timer settings"); m_resp->add_U32(VNSI_RET_DATAINVALID); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } } *timer = t; Timers.SetModified(); m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } /** OPCODE 100 - 119: VNSI network functions for recording access */ bool cVNSIClient::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */ { int FreeMB; int UsedMB; #if VDRVERSNUM >= 20102 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB, &UsedMB); #else int Percent = VideoDiskSpace(&FreeMB, &UsedMB); #endif int Total = FreeMB + UsedMB; m_resp->add_U32(Total); m_resp->add_U32(FreeMB); m_resp->add_U32(Percent); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_GetCount() /* OPCODE 101 */ { m_resp->add_U32(Recordings.Count()); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_GetList() /* OPCODE 102 */ { cMutexLock lock(&m_timerLock); cThreadLock RecordingsLock(&Recordings); for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { #if APIVERSNUM >= 10705 const cEvent *event = recording->Info()->GetEvent(); #else const cEvent *event = NULL; #endif time_t recordingStart = 0; int recordingDuration = 0; if (event) { recordingStart = event->StartTime(); recordingDuration = event->Duration(); } else { cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); if (rc) { recordingStart = rc->Timer()->StartTime(); recordingDuration = rc->Timer()->StopTime() - recordingStart; } else { #if APIVERSNUM >= 10727 recordingStart = recording->Start(); #else recordingStart = recording->start; #endif } } DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration); // recording_time m_resp->add_U32(recordingStart); // duration m_resp->add_U32(recordingDuration); // priority #if APIVERSNUM >= 10727 m_resp->add_U32(recording->Priority()); #else m_resp->add_U32(recording->priority); #endif // lifetime #if APIVERSNUM >= 10727 m_resp->add_U32(recording->Lifetime()); #else m_resp->add_U32(recording->lifetime); #endif // channel_name m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : ""); char* fullname = strdup(recording->Name()); char* recname = strrchr(fullname, FOLDERDELIMCHAR); char* directory = NULL; if(recname == NULL) { recname = fullname; } else { *recname = 0; recname++; directory = fullname; } // title m_resp->add_String(m_toUTF8.Convert(recname)); // subtitle if (!isempty(recording->Info()->ShortText())) m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText())); else m_resp->add_String(""); // description if (!isempty(recording->Info()->Description())) m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description())); else m_resp->add_String(""); // directory if(directory != NULL) { char* p = directory; while(*p != 0) { if(*p == FOLDERDELIMCHAR) *p = '/'; if(*p == '_') *p = ' '; p++; } while(*directory == '/') directory++; } m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory)); // filename / uid of recording uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false); m_resp->add_U32(uid); free(fullname); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_Rename() /* OPCODE 103 */ { uint32_t uid = m_req->extract_U32(); char* newtitle = m_req->extract_String(); cRecording* recording = cRecordingsCache::GetInstance().Lookup(uid); int r = VNSI_RET_DATAINVALID; if(recording != NULL) { // get filename and remove last part (recording time) char* filename_old = strdup((const char*)recording->FileName()); char* sep = strrchr(filename_old, '/'); if(sep != NULL) { *sep = 0; } // replace spaces in newtitle strreplace(newtitle, ' ', '_'); char* filename_new = new char[1024]; strncpy(filename_new, filename_old, 512); sep = strrchr(filename_new, '/'); if(sep != NULL) { sep++; *sep = 0; } strncat(filename_new, newtitle, 512); INFOLOG("renaming recording '%s' to '%s'", filename_old, filename_new); r = rename(filename_old, filename_new); Recordings.Update(); free(filename_old); delete[] filename_new; } m_resp->add_U32(r); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_Delete() /* OPCODE 104 */ { cString recName; cRecording* recording = NULL; uint32_t uid = m_req->extract_U32(); recording = cRecordingsCache::GetInstance().Lookup(uid); if (recording) { DEBUGLOG("deleting recording: %s", recording->Name()); cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); if (!rc) { if (recording->Delete()) { // Copy svdrdeveldevelp's way of doing this, see if it works Recordings.DelByName(recording->FileName()); INFOLOG("Recording \"%s\" deleted", recording->FileName()); m_resp->add_U32(VNSI_RET_OK); } else { ERRORLOG("Error while deleting recording!"); m_resp->add_U32(VNSI_RET_ERROR); } } else { ERRORLOG("Recording \"%s\" is in use by timer %d", recording->Name(), rc->Timer()->Index() + 1); m_resp->add_U32(VNSI_RET_DATALOCKED); } } else { ERRORLOG("Error in recording name \"%s\"", (const char*)recName); m_resp->add_U32(VNSI_RET_DATAUNKNOWN); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_GetEdl() /* OPCODE 105 */ { cString recName; cRecording* recording = NULL; uint32_t uid = m_req->extract_U32(); recording = cRecordingsCache::GetInstance().Lookup(uid); if (recording) { cMarks marks; if(marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording())) { #if VDRVERSNUM >= 10732 cMark* mark = NULL; double fps = recording->FramesPerSecond(); while((mark = marks.GetNextBegin(mark)) != NULL) { m_resp->add_U64(mark->Position() *1000 / fps); m_resp->add_U64(mark->Position() *1000 / fps); m_resp->add_S32(2); } #endif } } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */ bool cVNSIClient::processEPG_GetForChannel() /* OPCODE 120 */ { uint32_t channelUID = 0; channelUID = m_req->extract_U32(); uint32_t startTime = m_req->extract_U32(); uint32_t duration = m_req->extract_U32(); Channels.Lock(false); const cChannel* channel = NULL; channel = FindChannelByUID(channelUID); if(channel != NULL) { DEBUGLOG("get schedule called for channel '%s'", (const char*)channel->GetChannelID().ToString()); } if (!channel) { m_resp->add_U32(0); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); Channels.Unlock(); ERRORLOG("written 0 because channel = NULL"); return true; } cSchedulesLock MutexLock; const cSchedules *Schedules = cSchedules::Schedules(MutexLock); if (!Schedules) { m_resp->add_U32(0); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); Channels.Unlock(); DEBUGLOG("written 0 because Schedule!s! = NULL"); return true; } const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); if (!Schedule) { m_resp->add_U32(0); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); Channels.Unlock(); DEBUGLOG("written 0 because Schedule = NULL"); return true; } bool atLeastOneEvent = false; uint32_t thisEventID; uint32_t thisEventTime; uint32_t thisEventDuration; uint32_t thisEventContent; uint32_t thisEventRating; const char* thisEventTitle; const char* thisEventSubTitle; const char* thisEventDescription; for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event)) { thisEventID = event->EventID(); thisEventTitle = event->Title(); thisEventSubTitle = event->ShortText(); thisEventDescription = event->Description(); thisEventTime = event->StartTime(); thisEventDuration = event->Duration(); #if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) thisEventContent = event->Contents(); thisEventRating = 0; #elif APIVERSNUM >= 10711 thisEventContent = event->Contents(); thisEventRating = event->ParentalRating(); #else thisEventContent = 0; thisEventRating = 0; #endif //in the past filter if ((thisEventTime + thisEventDuration) < (uint32_t)time(NULL)) continue; //start time filter if ((thisEventTime + thisEventDuration) <= startTime) continue; //duration filter if (duration != 0 && thisEventTime >= (startTime + duration)) continue; if (!thisEventTitle) thisEventTitle = ""; if (!thisEventSubTitle) thisEventSubTitle = ""; if (!thisEventDescription) thisEventDescription = ""; m_resp->add_U32(thisEventID); m_resp->add_U32(thisEventTime); m_resp->add_U32(thisEventDuration); m_resp->add_U32(thisEventContent); m_resp->add_U32(thisEventRating); m_resp->add_String(m_toUTF8.Convert(thisEventTitle)); m_resp->add_String(m_toUTF8.Convert(thisEventSubTitle)); m_resp->add_String(m_toUTF8.Convert(thisEventDescription)); atLeastOneEvent = true; } Channels.Unlock(); DEBUGLOG("Got all event data"); if (!atLeastOneEvent) { m_resp->add_U32(0); DEBUGLOG("Written 0 because no data"); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); cEvent *lastEvent = Schedule->Events()->Last(); if (lastEvent) { m_epgUpdate[channelUID].lastEvent = lastEvent->StartTime(); m_epgUpdate[channelUID].attempts = 0; } DEBUGLOG("written schedules packet"); return true; } /*! * OPCODE 140 - 169: * VNSI network functions for channel scanning */ bool cVNSIClient::processSCAN_ScanSupported() /* OPCODE 140 */ { uint32_t retValue = VNSI_RET_NOTSUPPORTED; if (!m_inhibidDataUpdates && m_ChannelScanControl.IsSupported()) retValue = VNSI_RET_OK; m_resp->add_U32(retValue); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processSCAN_GetSupportedTypes() { uint32_t retValue = 0; if (m_ChannelScanControl.IsSupported()) { retValue |= m_ChannelScanControl.SupportsDVB_T() ? VNSI_SCAN_SUPPORT_DVB_T : 0; retValue |= m_ChannelScanControl.SupportsDVB_C() ? VNSI_SCAN_SUPPORT_DVB_C : 0; retValue |= m_ChannelScanControl.SupportsDVB_S() ? VNSI_SCAN_SUPPORT_DVB_S : 0; retValue |= m_ChannelScanControl.SupportsAnalogTV() ? VNSI_SCAN_SUPPORT_ANALOG_TV : 0; retValue |= m_ChannelScanControl.SupportsAnalogRadio() ? VNSI_SCAN_SUPPORT_ANALOG_RADIO : 0; retValue |= m_ChannelScanControl.SupportsATSC() ? VNSI_SCAN_SUPPORT_ATSC : 0; } m_resp->add_U32(retValue); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processSCAN_GetCountries() /* OPCODE 141 */ { scannerEntryList list; if (m_ChannelScanControl.GetCountries(list)) { m_resp->add_U32(VNSI_RET_OK); for (scannerEntryList::const_iterator it = list.begin(); it != list.end(); ++it) { m_resp->add_U32(it->index); m_resp->add_String(it->name); m_resp->add_String(it->longName); } } else { m_resp->add_U32(VNSI_RET_NOTSUPPORTED); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processSCAN_GetSatellites() /* OPCODE 142 */ { scannerEntryList list; if (m_ChannelScanControl.GetSatellites(list)) { m_resp->add_U32(VNSI_RET_OK); for (scannerEntryList::const_iterator it = list.begin(); it != list.end(); ++it) { m_resp->add_U32(it->index); m_resp->add_String(it->name); m_resp->add_String(it->longName); } } else { m_resp->add_U32(VNSI_RET_NOTSUPPORTED); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processSCAN_Start() /* OPCODE 143 */ { sScanServiceData svc; svc.type = (int)m_req->extract_U32(); svc.scan_tv = (bool)m_req->extract_U8(); svc.scan_radio = (bool)m_req->extract_U8(); svc.scan_fta = (bool)m_req->extract_U8(); svc.scan_scrambled = (bool)m_req->extract_U8(); svc.scan_hd = (bool)m_req->extract_U8(); svc.CountryIndex = (int)m_req->extract_U32(); svc.DVBC_Inversion = (int)m_req->extract_U32(); svc.DVBC_Symbolrate = (int)m_req->extract_U32(); svc.DVBC_QAM = (int)m_req->extract_U32(); svc.DVBT_Inversion = (int)m_req->extract_U32(); svc.SatIndex = (int)m_req->extract_U32(); svc.ATSC_Type = (int)m_req->extract_U32(); if (!m_inhibidDataUpdates && m_ChannelScanControl.IsSupported()) { if (m_ChannelScanControl.StartScan(svc)) { m_resp->add_U32(VNSI_RET_OK); m_inhibidDataUpdates = true; } else m_resp->add_U32(VNSI_RET_ERROR); } else m_resp->add_U32(VNSI_RET_NOTSUPPORTED); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processSCAN_Stop() /* OPCODE 144 */ { m_inhibidDataUpdates = false; if (m_ChannelScanControl.IsSupported()) { if (m_ChannelScanControl.StopScan()) m_resp->add_U32(VNSI_RET_OK); else m_resp->add_U32(VNSI_RET_ERROR); } else m_resp->add_U32(VNSI_RET_NOTSUPPORTED); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } void cVNSIClient::processSCAN_SetPercentage(int percent) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_PERCENTAGE)) { delete resp; return; } resp->add_U32(percent); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_SetSignalStrength(int strength, bool locked) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_SIGNAL)) { delete resp; return; } resp->add_U32(strength); resp->add_U32(locked); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_SetDeviceInfo(const char *Info) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_DEVICE)) { delete resp; return; } resp->add_String(Info); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_SetTransponder(const char *Info) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_TRANSPONDER)) { delete resp; return; } resp->add_String(Info); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_NEWCHANNEL)) { delete resp; return; } resp->add_U32(isRadio); resp->add_U32(isEncrypted); resp->add_U32(isHD); resp->add_String(Name); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_IsFinished() { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_FINISHED)) { delete resp; return; } resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } void cVNSIClient::processSCAN_SetStatus(int status) { cResponsePacket *resp = new cResponsePacket(); if (!resp->initScan(VNSI_SCANNER_STATUS)) { delete resp; return; } resp->add_U32(status); resp->finalise(); m_socket.write(resp->getPtr(), resp->getLen()); delete resp; } bool cVNSIClient::processOSD_Connect() /* OPCODE 160 */ { m_Osd = new cVnsiOsdProvider(&m_socket); int osdWidth, osdHeight; double aspect; cDevice::PrimaryDevice()->GetOsdSize(osdWidth, osdHeight, aspect); m_resp->add_U32(osdWidth); m_resp->add_U32(osdHeight); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processOSD_Disconnect() /* OPCODE 161 */ { if (m_Osd) { delete m_Osd; m_Osd = NULL; } return true; } bool cVNSIClient::processOSD_Hitkey() /* OPCODE 162 */ { if (m_Osd) { unsigned int key = m_req->extract_U32(); cVnsiOsdProvider::SendKey(key); } return true; } /** OPCODE 180 - 189: VNSI network functions for deleted recording access */ bool cVNSIClient::processRECORDINGS_DELETED_Supported() /* OPCODE 180 */ { m_resp->add_U32(VNSI_RET_OK); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_DELETED_GetCount() /* OPCODE 181 */ { DeletedRecordings.Load(); m_resp->add_U32(DeletedRecordings.Count()); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_DELETED_GetList() /* OPCODE 182 */ { cMutexLock lock(&m_timerLock); cThreadLock RecordingsLock(&Recordings); for (cRecording *recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording)) { #if APIVERSNUM >= 10705 const cEvent *event = recording->Info()->GetEvent(); #else const cEvent *event = NULL; #endif time_t recordingStart = 0; int recordingDuration = 0; if (event) { recordingStart = event->StartTime(); recordingDuration = event->Duration(); } else { cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); if (rc) { recordingStart = rc->Timer()->StartTime(); recordingDuration = rc->Timer()->StopTime() - recordingStart; } else { #if APIVERSNUM >= 10727 recordingStart = recording->Start(); #else recordingStart = recording->start; #endif } } DEBUGLOG("GRI: RC: recordingStart=%lu recordingDuration=%i", recordingStart, recordingDuration); // recording_time m_resp->add_U32(recordingStart); // duration m_resp->add_U32(recordingDuration); // priority #if APIVERSNUM >= 10727 m_resp->add_U32(recording->Priority()); #else m_resp->add_U32(recording->priority); #endif // lifetime #if APIVERSNUM >= 10727 m_resp->add_U32(recording->Lifetime()); #else m_resp->add_U32(recording->lifetime); #endif // channel_name m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : ""); char* fullname = strdup(recording->Name()); char* recname = strrchr(fullname, FOLDERDELIMCHAR); char* directory = NULL; if(recname == NULL) { recname = fullname; } else { *recname = 0; recname++; directory = fullname; } // title m_resp->add_String(m_toUTF8.Convert(recname)); // subtitle if (!isempty(recording->Info()->ShortText())) m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText())); else m_resp->add_String(""); // description if (!isempty(recording->Info()->Description())) m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description())); else m_resp->add_String(""); // directory if(directory != NULL) { char* p = directory; while(*p != 0) { if(*p == FOLDERDELIMCHAR) *p = '/'; if(*p == '_') *p = ' '; p++; } while(*directory == '/') directory++; } m_resp->add_String((isempty(directory)) ? "" : m_toUTF8.Convert(directory)); // filename / uid of recording uint32_t uid = cRecordingsCache::GetInstance().Register(recording, false); m_resp->add_U32(uid); free(fullname); } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_DELETED_Delete() /* OPCODE 183 */ { cString recName; cRecording* recording = NULL; #if VDRVERSNUM >= 20102 cLockFile LockFile(cVideoDirectory::Name()); #else cLockFile LockFile(VideoDirectory); #endif if (LockFile.Lock()) { uint32_t uid = m_req->extract_U32(); cThreadLock DeletedRecordingsLock(&DeletedRecordings); for (recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording)) { if (uid == CreateStringHash(recording->FileName())) { #if VDRVERSNUM >= 20102 if (!cVideoDirectory::RemoveVideoFile(recording->FileName())) #else if (!RemoveVideoFile(recording->FileName())) #endif { ERRORLOG("Error while remove deleted recording (%s)", recording->FileName()); m_resp->add_U32(VNSI_RET_ERROR); } else { DeletedRecordings.Del(recording); DeletedRecordings.Update(); INFOLOG("Recording \"%s\" permanent deleted", recording->FileName()); m_resp->add_U32(VNSI_RET_OK); } break; } } } m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::Undelete(cRecording* recording) { DEBUGLOG("undelete recording: %s", recording->Name()); char *NewName = strdup(recording->FileName()); char *ext = strrchr(NewName, '.'); if (ext && strcmp(ext, ".del") == 0) { strncpy(ext, ".rec", strlen(ext)); if (!access(NewName, F_OK)) { ERRORLOG("Recording with the same name exists (%s)", NewName); OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Recording with the same name exists"), NewName)); } else { if (access(recording->FileName(), F_OK) == 0) { #if VDRVERSNUM >= 20102 if (!cVideoDirectory::RenameVideoFile(recording->FileName(), NewName)) #else if (!RenameVideoFile(recording->FileName(), NewName)) #endif { ERRORLOG("Error while rename deleted recording (%s) to (%s)", recording->FileName(), NewName); } cIndexFile *index = new cIndexFile(NewName, false, recording->IsPesRecording()); int LastFrame = index->Last() - 1; if (LastFrame > 0) { uint16_t FileNumber = 0; off_t FileOffset = 0; index->Get(LastFrame, &FileNumber, &FileOffset); delete index; if (FileNumber == 0) { ERRORLOG("while read last filenumber (%s)", NewName); OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while read last filenumber"), NewName)); } else { for (int i = 1; i <= FileNumber; i++) { cString temp = cString::sprintf(recording->IsPesRecording() ? "%s/%03d.vdr" : "%s/%05d.ts", (const char *)NewName, i); if (access(*temp, R_OK) != 0) { i = FileNumber; OsdStatusMessage(*cString::sprintf("%s %03d (%s)", tr("Error while accessing vdrfile"), i, NewName)); } } } } else { delete index; ERRORLOG("accessing indexfile (%s)", NewName); OsdStatusMessage(*cString::sprintf("%s (%s)", tr("Error while accessing indexfile"), NewName)); } DeletedRecordings.Del(recording); Recordings.Update(); DeletedRecordings.Update(); } else { ERRORLOG("deleted recording '%s' vanished", recording->FileName()); OsdStatusMessage(*cString::sprintf("%s \"%s\"", tr("Deleted recording vanished"), recording->FileName())); } } } free(NewName); return true; } bool cVNSIClient::processRECORDINGS_DELETED_Undelete() /* OPCODE 184 */ { int ret = VNSI_RET_DATAUNKNOWN; #if VDRVERSNUM >= 20102 cLockFile LockFile(cVideoDirectory::Name()); #else cLockFile LockFile(VideoDirectory); #endif if (LockFile.Lock()) { uint32_t uid = m_req->extract_U32(); cThreadLock DeletedRecordingsLock(&DeletedRecordings); for (cRecording* recording = DeletedRecordings.First(); recording; recording = DeletedRecordings.Next(recording)) { if (uid == CreateStringHash(recording->FileName())) { if (Undelete(recording)) { INFOLOG("Recording \"%s\" undeleted", recording->FileName()); ret = VNSI_RET_OK; } else ret = VNSI_RET_ERROR; break; } } } m_resp->add_U32(ret); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } bool cVNSIClient::processRECORDINGS_DELETED_DeleteAll() /* OPCODE 185 */ { int ret = VNSI_RET_OK; #if VDRVERSNUM >= 20102 cLockFile LockFile(cVideoDirectory::Name()); #else cLockFile LockFile(VideoDirectory); #endif if (LockFile.Lock()) { cThreadLock DeletedRecordingsLock(&DeletedRecordings); for (cRecording *recording = DeletedRecordings.First(); recording; ) { cRecording *next = DeletedRecordings.Next(recording); #if VDRVERSNUM >= 20102 if (!cVideoDirectory::RemoveVideoFile(recording->FileName())) #else if (!RemoveVideoFile(recording->FileName())) #endif { ERRORLOG("Error while remove deleted recording (%s)", recording->FileName()); ret = VNSI_RET_ERROR; break; } else INFOLOG("Recording \"%s\" permanent deleted", recording->FileName()); recording = next; } DeletedRecordings.Clear(); DeletedRecordings.Update(); } m_resp->add_U32(ret); m_resp->finalise(); m_socket.write(m_resp->getPtr(), m_resp->getLen()); return true; } // part of this method is taken from XVDR cString cVNSIClient::CreatePiconRef(cChannel* channel) { int hash = 0; if(cSource::IsSat(channel->Source())) { int16_t pos = channel->Source() & cSource::st_Pos; hash = pos; #if VDRVERSNUM >= 20101 if(hash < 0) { hash = 3600 + hash; } #endif hash = hash << 16; } else if(cSource::IsCable(channel->Source())) hash = 0xFFFF0000; else if(cSource::IsTerr(channel->Source())) hash = 0xEEEE0000; else if(cSource::IsAtsc(channel->Source())) hash = 0xDDDD0000; cString serviceref = cString::sprintf("1_0_%i_%X_%X_%X_%X_0_0_0", cVNSIChannelFilter::IsRadio(channel) ? 2 : (channel->Vtype() == 27) ? 19 : 1, channel->Sid(), channel->Tid(), channel->Nid(), hash); return serviceref; } vdr-plugin-vnsiserver-1.3.1/vnsiclient.h000066400000000000000000000134321257700704000203430ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_CLIENT_H #define VNSI_CLIENT_H #include #include #include #include #include "config.h" #include "cxsocket.h" #include "channelscancontrol.h" #include #include class cChannel; class cDevice; class cLiveStreamer; class cRequestPacket; class cResponsePacket; class cRecPlayer; class cCmdControl; class cVnsiOsdProvider; class cVNSIClient : public cThread , public cStatus { private: unsigned int m_Id; cxSocket m_socket; bool m_loggedIn; bool m_StatusInterfaceEnabled; cLiveStreamer *m_Streamer; bool m_isStreaming; bool m_bSupportRDS; cString m_ClientAddress; cRecPlayer *m_RecPlayer; cRequestPacket *m_req; cResponsePacket *m_resp; cCharSetConv m_toUTF8; uint32_t m_protocolVersion; cMutex m_msgLock; static cMutex m_timerLock; cVnsiOsdProvider *m_Osd; CScanControl m_ChannelScanControl; static bool m_inhibidDataUpdates; typedef struct { int attempts; time_t lastEvent; } sEpgUpdate; std::map m_epgUpdate; protected: bool processRequest(cRequestPacket* req); virtual void Action(void); virtual void TimerChange(const cTimer *Timer, eTimerChange Change); virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); virtual void OsdStatusMessage(const char *Message); virtual void ChannelChange(const cChannel *Channel); public: cVNSIClient(int fd, unsigned int id, const char *ClientAdr); virtual ~cVNSIClient(); void ChannelsChange(); void RecordingsChange(); void TimerChange(); void EpgChange(); static bool InhibidDataUpdates() { return m_inhibidDataUpdates; } unsigned int GetID() { return m_Id; } protected: void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; } void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; } bool StartChannelStreaming(const cChannel *channel, int32_t priority, uint8_t timeshift, uint32_t timeout); void StopChannelStreaming(); private: typedef struct { bool automatic; bool radio; std::string name; } ChannelGroup; std::map m_channelgroups[2]; bool process_Login(); bool process_GetTime(); bool process_EnableStatusInterface(); bool process_Ping(); bool process_GetSetup(); bool process_StoreSetup(); bool processChannelStream_Open(); bool processChannelStream_Close(); bool processChannelStream_Seek(); bool processRecStream_Open(); bool processRecStream_Close(); bool processRecStream_GetBlock(); bool processRecStream_PositionFromFrameNumber(); bool processRecStream_FrameNumberFromPosition(); bool processRecStream_GetIFrame(); bool processRecStream_GetLength(); bool processCHANNELS_GroupsCount(); bool processCHANNELS_ChannelsCount(); bool processCHANNELS_GroupList(); bool processCHANNELS_GetChannels(); bool processCHANNELS_GetGroupMembers(); bool processCHANNELS_GetCaids(); bool processCHANNELS_GetWhitelist(); bool processCHANNELS_GetBlacklist(); bool processCHANNELS_SetWhitelist(); bool processCHANNELS_SetBlacklist(); void CreateChannelGroups(bool automatic); bool processTIMER_GetCount(); bool processTIMER_Get(); bool processTIMER_GetList(); bool processTIMER_Add(); bool processTIMER_Delete(); bool processTIMER_Update(); bool processRECORDINGS_GetDiskSpace(); bool processRECORDINGS_GetCount(); bool processRECORDINGS_GetList(); bool processRECORDINGS_GetInfo(); bool processRECORDINGS_Rename(); bool processRECORDINGS_Delete(); bool processRECORDINGS_Move(); bool processRECORDINGS_GetEdl(); bool processRECORDINGS_DELETED_Supported(); bool processRECORDINGS_DELETED_GetCount(); bool processRECORDINGS_DELETED_GetList(); bool processRECORDINGS_DELETED_Delete(); bool processRECORDINGS_DELETED_Undelete(); bool processRECORDINGS_DELETED_DeleteAll(); bool processEPG_GetForChannel(); bool processSCAN_ScanSupported(); bool processSCAN_GetSupportedTypes(); bool processSCAN_GetCountries(); bool processSCAN_GetSatellites(); bool processSCAN_Start(); bool processSCAN_Stop(); bool Undelete(cRecording* recording); bool processOSD_Connect(); bool processOSD_Disconnect(); bool processOSD_Hitkey(); cString CreatePiconRef(cChannel* channel); private: /** Static callback functions to interact with wirbelscan plugin over the plugin service interface */ friend class CScanControl; void processSCAN_SetPercentage(int percent); void processSCAN_SetSignalStrength(int strength, bool locked); void processSCAN_SetDeviceInfo(const char *Info); void processSCAN_SetTransponder(const char *Info); void processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD); void processSCAN_IsFinished(); void processSCAN_SetStatus(int status); }; #endif // VNSI_CLIENT_H vdr-plugin-vnsiserver-1.3.1/vnsicommand.h000066400000000000000000000145211257700704000205030ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_COMMAND_H #define VNSI_COMMAND_H /** Current VNSI Protocol Version number */ #define VNSI_PROTOCOLVERSION 8 /** Start of RDS support protocol Version */ #define VNSI_RDS_PROTOCOLVERSION 8 /** Minimum VNSI Protocol Version number */ #define VNSI_MIN_PROTOCOLVERSION 5 /** Packet types */ #define VNSI_CHANNEL_REQUEST_RESPONSE 1 #define VNSI_CHANNEL_STREAM 2 #define VNSI_CHANNEL_KEEPALIVE 3 #define VNSI_CHANNEL_NETLOG 4 #define VNSI_CHANNEL_STATUS 5 #define VNSI_CHANNEL_SCAN 6 #define VNSI_CHANNEL_OSD 7 /** Response packets operation codes */ #define CONFNAME_PMTTIMEOUT "PmtTimeout" #define CONFNAME_TIMESHIFT "Timeshift" #define CONFNAME_TIMESHIFTBUFFERSIZE "TimeshiftBufferSize" #define CONFNAME_TIMESHIFTBUFFERFILESIZE "TimeshiftBufferFileSize" #define CONFNAME_TIMESHIFTBUFFERDIR "TimeshiftBufferDir" #define CONFNAME_PLAYRECORDING "PlayRecording" #define CONFNAME_AVOIDEPGSCAN "AvoidEPGScan" /* OPCODE 1 - 19: VNSI network functions for general purpose */ #define VNSI_LOGIN 1 #define VNSI_GETTIME 2 #define VNSI_ENABLESTATUSINTERFACE 3 #define VNSI_PING 7 #define VNSI_GETSETUP 8 #define VNSI_STORESETUP 9 /* OPCODE 20 - 39: VNSI network functions for live streaming */ #define VNSI_CHANNELSTREAM_OPEN 20 #define VNSI_CHANNELSTREAM_CLOSE 21 #define VNSI_CHANNELSTREAM_SEEK 22 /* OPCODE 40 - 59: VNSI network functions for recording streaming */ #define VNSI_RECSTREAM_OPEN 40 #define VNSI_RECSTREAM_CLOSE 41 #define VNSI_RECSTREAM_GETBLOCK 42 #define VNSI_RECSTREAM_POSTOFRAME 43 #define VNSI_RECSTREAM_FRAMETOPOS 44 #define VNSI_RECSTREAM_GETIFRAME 45 #define VNSI_RECSTREAM_GETLENGTH 46 /* OPCODE 60 - 79: VNSI network functions for channel access */ #define VNSI_CHANNELS_GETCOUNT 61 #define VNSI_CHANNELS_GETCHANNELS 63 #define VNSI_CHANNELGROUP_GETCOUNT 65 #define VNSI_CHANNELGROUP_LIST 66 #define VNSI_CHANNELGROUP_MEMBERS 67 #define VNSI_CHANNELS_GETCAIDS 68 #define VNSI_CHANNELS_GETWHITELIST 69 #define VNSI_CHANNELS_GETBLACKLIST 70 #define VNSI_CHANNELS_SETWHITELIST 71 #define VNSI_CHANNELS_SETBLACKLIST 72 /* OPCODE 80 - 99: VNSI network functions for timer access */ #define VNSI_TIMER_GETCOUNT 80 #define VNSI_TIMER_GET 81 #define VNSI_TIMER_GETLIST 82 #define VNSI_TIMER_ADD 83 #define VNSI_TIMER_DELETE 84 #define VNSI_TIMER_UPDATE 85 /* OPCODE 100 - 119: VNSI network functions for recording access */ #define VNSI_RECORDINGS_DISKSIZE 100 #define VNSI_RECORDINGS_GETCOUNT 101 #define VNSI_RECORDINGS_GETLIST 102 #define VNSI_RECORDINGS_RENAME 103 #define VNSI_RECORDINGS_DELETE 104 #define VNSI_RECORDINGS_GETEDL 105 /* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */ #define VNSI_EPG_GETFORCHANNEL 120 /* OPCODE 140 - 159: VNSI network functions for channel scanning */ #define VNSI_SCAN_SUPPORTED 140 #define VNSI_SCAN_GETCOUNTRIES 141 #define VNSI_SCAN_GETSATELLITES 142 #define VNSI_SCAN_START 143 #define VNSI_SCAN_STOP 144 #define VNSI_SCAN_SUPPORTED_TYPES 145 /* OPCODE 160 - 179: VNSI network functions for channel scanning */ #define VNSI_OSD_CONNECT 160 #define VNSI_OSD_DISCONNECT 161 #define VNSI_OSD_HITKEY 162 /* OPCODE 180 - 189: VNSI network functions for deleted recording access */ #define VNSI_RECORDINGS_DELETED_ACCESS_SUPPORTED 180 #define VNSI_RECORDINGS_DELETED_GETCOUNT 181 #define VNSI_RECORDINGS_DELETED_GETLIST 182 #define VNSI_RECORDINGS_DELETED_DELETE 183 #define VNSI_RECORDINGS_DELETED_UNDELETE 184 #define VNSI_RECORDINGS_DELETED_DELETE_ALL 185 /** Stream packet types (server -> client) */ #define VNSI_STREAM_CHANGE 1 #define VNSI_STREAM_STATUS 2 #define VNSI_STREAM_QUEUESTATUS 3 #define VNSI_STREAM_MUXPKT 4 #define VNSI_STREAM_SIGNALINFO 5 #define VNSI_STREAM_CONTENTINFO 6 #define VNSI_STREAM_BUFFERSTATS 7 #define VNSI_STREAM_REFTIME 8 /** Scan packet types (server -> client) */ #define VNSI_SCANNER_PERCENTAGE 1 #define VNSI_SCANNER_SIGNAL 2 #define VNSI_SCANNER_DEVICE 3 #define VNSI_SCANNER_TRANSPONDER 4 #define VNSI_SCANNER_NEWCHANNEL 5 #define VNSI_SCANNER_FINISHED 6 #define VNSI_SCANNER_STATUS 7 /** OSD packet types (server -> client) */ #define VNSI_OSD_MOVEWINDOW 1 #define VNSI_OSD_CLEAR 2 #define VNSI_OSD_OPEN 3 #define VNSI_OSD_CLOSE 4 #define VNSI_OSD_SETPALETTE 5 #define VNSI_OSD_SETBLOCK 6 /** Status packet types (server -> client) */ #define VNSI_STATUS_TIMERCHANGE 1 #define VNSI_STATUS_RECORDING 2 #define VNSI_STATUS_MESSAGE 3 #define VNSI_STATUS_CHANNELCHANGE 4 #define VNSI_STATUS_RECORDINGSCHANGE 5 #define VNSI_STATUS_EPGCHANGE 6 /** Channel search signal supported flags */ #define VNSI_SCAN_SUPPORT_DVB_T 0x01 #define VNSI_SCAN_SUPPORT_DVB_C 0x02 #define VNSI_SCAN_SUPPORT_DVB_S 0x04 #define VNSI_SCAN_SUPPORT_ANALOG_TV 0x08 #define VNSI_SCAN_SUPPORT_ANALOG_RADIO 0x10 #define VNSI_SCAN_SUPPORT_ATSC 0x20 /** Packet return codes */ #define VNSI_RET_OK 0 #define VNSI_RET_RECRUNNING 1 #define VNSI_RET_NOTSUPPORTED 995 #define VNSI_RET_DATAUNKNOWN 996 #define VNSI_RET_DATALOCKED 997 #define VNSI_RET_DATAINVALID 998 #define VNSI_RET_ERROR 999 #endif // VNSI_COMMAND_H vdr-plugin-vnsiserver-1.3.1/vnsiosd.c000066400000000000000000000226331257700704000176500ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include "config.h" #include "vnsiosd.h" #include "vnsicommand.h" #include "responsepacket.h" #include "vnsi.h" #include #include #include #include #include #include "cxsocket.h" #define ACTION_NONE 0 #define ACTION_MOVE_LEFT 1 #define ACTION_MOVE_RIGHT 2 #define ACTION_MOVE_UP 3 #define ACTION_MOVE_DOWN 4 #define ACTION_SELECT_ITEM 7 #define ACTION_PREVIOUS_MENU 10 #define REMOTE_0 58 // remote keys 0-9. are used by multiple windows #define REMOTE_1 59 // for example in videoFullScreen.xml window id=2005 you can #define REMOTE_2 60 // enter time (mmss) to jump to particular point in the movie #define REMOTE_3 61 #define REMOTE_4 62 // with spincontrols you can enter 3digit number to quickly set #define REMOTE_5 63 // spincontrol to desired value #define REMOTE_6 64 #define REMOTE_7 65 #define REMOTE_8 66 #define REMOTE_9 67 #define ACTION_NAV_BACK 92 #define ACTION_TELETEXT_RED 215 // Teletext Color buttons to control TopText #define ACTION_TELETEXT_GREEN 216 // " " " " " " #define ACTION_TELETEXT_YELLOW 217 // " " " " " " #define ACTION_TELETEXT_BLUE 218 // " " " " " " // --- cVnsiOsd ----------------------------------------------------------- #define MAXNUMWINDOWS 16 // OSD windows are counted 0...15 #define MAXOSDMEMORY 1024000 // number of bytes available to the OSD class cVnsiOsd : public cOsd { private: int osdMem; bool shown; void Cmd(int cmd, int wnd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL, int size = 0); protected: virtual void SetActive(bool On); public: cVnsiOsd(int Left, int Top, uint Level); virtual ~cVnsiOsd(); virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); virtual void Flush(void); }; cVnsiOsd::cVnsiOsd(int Left, int Top, uint Level) :cOsd(Left, Top, Level) { shown = false; osdMem = MAXOSDMEMORY; DEBUGLOG("osd created. left: %d, top: %d", Left, Top); } cVnsiOsd::~cVnsiOsd() { SetActive(false); } void cVnsiOsd::SetActive(bool On) { if (On != Active()) { cOsd::SetActive(On); if (On) { // must clear all windows here to avoid flashing effects - doesn't work if done // in Flush() only for the windows that are actually used... for (int i = 0; i < MAXNUMWINDOWS; i++) { Cmd(VNSI_OSD_CLEAR, i); } if (GetBitmap(0)) // only flush here if there are already bitmaps Flush(); } else if (shown) { for (int i = 0; GetBitmap(i); i++) { Cmd(VNSI_OSD_CLOSE, i); } shown = false; } } } eOsdError cVnsiOsd::CanHandleAreas(const tArea *Areas, int NumAreas) { eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); if (Result == oeOk) { if (NumAreas > MAXNUMWINDOWS) return oeTooManyAreas; int TotalMemory = 0; for (int i = 0; i < NumAreas; i++) { if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) return oeBppNotSupported; if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0) return oeWrongAlignment; if (Areas[i].Width() < 1 || Areas[i].Height() < 1 || Areas[i].Width() > 720 || Areas[i].Height() > 576) return oeWrongAreaSize; TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp); } if (TotalMemory > osdMem) return oeOutOfMemory; } return Result; } eOsdError cVnsiOsd::SetAreas(const tArea *Areas, int NumAreas) { if (shown) { for (int i = 0; GetBitmap(i); i++) { Cmd(VNSI_OSD_CLOSE, i); } shown = false; } return cOsd::SetAreas(Areas, NumAreas); } void cVnsiOsd::Cmd(int cmd, int wnd, int color, int x0, int y0, int x1, int y1, const void *data, int size) { DEBUGLOG("OSD: cmd: %d, wnd: %d, color: %d, x0: %d, y0: %d, x1: %d, y1: %d", cmd, wnd, color, x0, y0, x1, y1); cVnsiOsdProvider::SendOsdPacket(cmd, wnd, color, x0, y0, x1, y1, data, size); } void cVnsiOsd::Flush(void) { if (!Active()) return; cBitmap *Bitmap; bool full = cVnsiOsdProvider::IsRequestFull(); for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { uint8_t reset = !shown || full; Cmd(VNSI_OSD_OPEN, i, Bitmap->Bpp(), Left() + Bitmap->X0(), Top() + Bitmap->Y0(), Left() + Bitmap->X0() + Bitmap->Width() - 1, Top() + Bitmap->Y0() + Bitmap->Height() - 1, (void *)&reset, 1); int x1 = 0, y1 = 0, x2 = 0, y2 = 0; if (!shown || Bitmap->Dirty(x1, y1, x2, y2) || full) { if (!shown || full) { x1 = y1 = 0; x2 = Bitmap->Width() - 1; y2 = Bitmap->Height() - 1; } // commit colors: int NumColors; const tColor *Colors = Bitmap->Colors(NumColors); if (Colors) { Cmd(VNSI_OSD_SETPALETTE, i, 0, NumColors, 0, 0, 0, Colors, NumColors*sizeof(tColor)); } // commit modified data: int size = (y2-y1) * Bitmap->Width() + (x2-x1+1); Cmd(VNSI_OSD_SETBLOCK, i, Bitmap->Width(), x1, y1, x2, y2, Bitmap->Data(x1, y1), size); } Bitmap->Clean(); } if (!shown) { // Showing the windows in a separate loop to avoid seeing them come up one after another for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { Cmd(VNSI_OSD_MOVEWINDOW, i, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0()); } shown = true; } } // --- cVnsiOsdProvider ------------------------------------------------------- cResponsePacket cVnsiOsdProvider::m_OsdPacket; cxSocket *cVnsiOsdProvider::m_Socket; cMutex cVnsiOsdProvider::m_Mutex; bool cVnsiOsdProvider::m_RequestFull; cVnsiOsdProvider::cVnsiOsdProvider(cxSocket *socket) { cMutexLock lock(&m_Mutex); INFOLOG("new osd provider"); m_Socket = socket; m_RequestFull = true; if (VNSIServerConfig.pDevice) ((cDvbVnsiDevice*)VNSIServerConfig.pDevice)->ActivateDecoder(true); } cVnsiOsdProvider::~cVnsiOsdProvider() { cMutexLock lock(&m_Mutex); m_Socket = NULL; if (VNSIServerConfig.pDevice) ((cDvbVnsiDevice*)VNSIServerConfig.pDevice)->ActivateDecoder(false); } cOsd *cVnsiOsdProvider::CreateOsd(int Left, int Top, uint Level) { cMutexLock lock(&m_Mutex); return new cVnsiOsd(Left, Top, Level); } void cVnsiOsdProvider::SendOsdPacket(int cmd, int wnd, int color, int x0, int y0, int x1, int y1, const void *data, int size) { cMutexLock lock(&m_Mutex); if (!m_Socket) return; if (!m_OsdPacket.initOsd(cmd, wnd, color, x0, y0, x1, y1)) { ERRORLOG("OSD response packet init fail"); return; } m_OsdPacket.setLen(m_OsdPacket.getOSDHeaderLength() + size); m_OsdPacket.finaliseOSD(); m_Socket->LockWrite(); m_Socket->write(m_OsdPacket.getPtr(), m_OsdPacket.getOSDHeaderLength(), -1, (size > 0) ? true: false); if (size) m_Socket->write(data, size); m_Socket->UnlockWrite(); } bool cVnsiOsdProvider::IsRequestFull() { cMutexLock lock(&m_Mutex); bool ret = m_RequestFull; m_RequestFull = false; return ret; } void cVnsiOsdProvider::SendKey(unsigned int key) { if (!cOsd::IsOpen()) { cRemote::Put(kMenu); return; } switch (key) { case ACTION_MOVE_UP: cRemote::Put(kUp); break; case ACTION_MOVE_DOWN: cRemote::Put(kDown); break; case ACTION_MOVE_LEFT: cRemote::Put(kLeft); break; case ACTION_MOVE_RIGHT: cRemote::Put(kRight); break; case ACTION_PREVIOUS_MENU: cRemote::Put(kMenu); break; case ACTION_NAV_BACK: cRemote::Put(kBack); break; case ACTION_SELECT_ITEM: cRemote::Put(kOk); break; case ACTION_TELETEXT_RED: cRemote::Put(kRed); break; case ACTION_TELETEXT_BLUE: cRemote::Put(kBlue); break; case ACTION_TELETEXT_YELLOW: cRemote::Put(kYellow); break; case ACTION_TELETEXT_GREEN: cRemote::Put(kGreen); break; case REMOTE_0: cRemote::Put(k0); break; case REMOTE_1: cRemote::Put(k1); break; case REMOTE_2: cRemote::Put(k2); break; case REMOTE_3: cRemote::Put(k3); break; case REMOTE_4: cRemote::Put(k4); break; case REMOTE_5: cRemote::Put(k5); break; case REMOTE_6: cRemote::Put(k6); break; case REMOTE_7: cRemote::Put(k7); break; case REMOTE_8: cRemote::Put(k8); break; case REMOTE_9: cRemote::Put(k9); break; default: break; } } vdr-plugin-vnsiserver-1.3.1/vnsiosd.h000066400000000000000000000026351257700704000176550ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2005-2012 Team XBMC * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include "responsepacket.h" class cxSocket; class cVnsiOsdProvider : public cOsdProvider { public: cVnsiOsdProvider(cxSocket *socket); virtual ~cVnsiOsdProvider(); virtual cOsd *CreateOsd(int Left, int Top, uint Level); static void SendOsdPacket(int cmd, int wnd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL, int size = 0); static bool IsRequestFull(); static void SendKey(unsigned int key); private: static cResponsePacket m_OsdPacket; static cxSocket *m_Socket; static cMutex m_Mutex; static bool m_RequestFull; }; vdr-plugin-vnsiserver-1.3.1/vnsiserver.c000066400000000000000000000121471257700704000203700ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vnsi.h" #include "vnsiserver.h" #include "vnsiclient.h" #include "channelfilter.h" unsigned int cVNSIServer::m_IdCnt = 0; class cAllowedHosts : public cSVDRPhosts { public: cAllowedHosts(const cString& AllowedHostsFile) { if (!Load(AllowedHostsFile, true, true)) { ERRORLOG("Invalid or missing '%s'. falling back to 'svdrphosts.conf'.", *AllowedHostsFile); cString Base = cString::sprintf("%s/../svdrphosts.conf", *VNSIServerConfig.ConfigDirectory); if (!Load(Base, true, true)) { ERRORLOG("Invalid or missing %s. Adding 127.0.0.1 to list of allowed hosts.", *Base); cSVDRPhost *localhost = new cSVDRPhost; if (localhost->Parse("127.0.0.1")) Add(localhost); else delete localhost; } } } }; cVNSIServer::cVNSIServer(int listenPort) : cThread("VDR VNSI Server") { m_ServerPort = listenPort; Start(); INFOLOG("VNSI Server started"); INFOLOG("Channel streaming timeout: %i seconds", VNSIServerConfig.stream_timeout); return; } cVNSIServer::~cVNSIServer() { m_Status.Shutdown(); Cancel(); INFOLOG("VNSI Server stopped"); } void cVNSIServer::NewClientConnected(int fd) { char buf[64]; struct sockaddr_in sin; socklen_t len = sizeof(sin); if (getpeername(fd, (struct sockaddr *)&sin, &len)) { ERRORLOG("getpeername() failed, dropping new incoming connection %d", m_IdCnt); close(fd); return; } cAllowedHosts AllowedHosts(m_AllowedHostsFile); if (!AllowedHosts.Acceptable(sin.sin_addr.s_addr)) { ERRORLOG("Address not allowed to connect (%s)", *m_AllowedHostsFile); close(fd); return; } if (fcntl(fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) == -1) { ERRORLOG("Error setting control socket to nonblocking mode"); close(fd); return; } int val = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); #ifdef SOL_TCP val = 30; setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val)); val = 15; setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val)); val = 5; setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val)); val = 1; setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val)); #endif INFOLOG("Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf)); cVNSIClient *connection = new cVNSIClient(fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf)); m_Status.AddClient(connection); m_IdCnt++; } void cVNSIServer::Action(void) { fd_set fds; struct timeval tv; if(*VNSIServerConfig.ConfigDirectory) { m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory); } else { ERRORLOG("cVNSIServer: missing ConfigDirectory!"); m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE); } VNSIChannelFilter.Load(); VNSIChannelFilter.SortChannels(); m_Status.Start(); m_ServerFD = socket(AF_INET, SOCK_STREAM, 0); if(m_ServerFD == -1) return; fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC); int one = 1; setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); struct sockaddr_in s; memset(&s, 0, sizeof(s)); s.sin_family = AF_INET; s.sin_port = htons(m_ServerPort); int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s)); if (x < 0) { close(m_ServerFD); INFOLOG("Unable to start VNSI Server, port already in use ?"); m_ServerFD = -1; return; } listen(m_ServerFD, 10); while (Running()) { FD_ZERO(&fds); FD_SET(m_ServerFD, &fds); tv.tv_sec = 0; tv.tv_usec = 250*1000; int r = select(m_ServerFD + 1, &fds, NULL, NULL, &tv); if (r == -1) { ERRORLOG("failed during select"); continue; } if (r == 0) { continue; } int fd = accept(m_ServerFD, 0, 0); if (fd >= 0) { NewClientConnected(fd); } else { ERRORLOG("accept failed"); } } return; } vdr-plugin-vnsiserver-1.3.1/vnsiserver.h000066400000000000000000000025471257700704000204000ustar00rootroot00000000000000/* * vdr-plugin-vnsi - KODI server plugin for VDR * * Copyright (C) 2010 Alwin Esch (Team XBMC) * Copyright (C) 2010, 2011 Alexander Pipelka * Copyright (C) 2015 Team KODI * * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KODI; see the file COPYING. If not, see * . * */ #ifndef VNSI_SERVER_H #define VNSI_SERVER_H #include #include "config.h" #include "status.h" class cVNSIClient; class cVNSIServer : public cThread { protected: virtual void Action(void); void NewClientConnected(int fd); int m_ServerPort; int m_ServerFD; cString m_AllowedHostsFile; cVNSIStatus m_Status; static unsigned int m_IdCnt; public: cVNSIServer(int listenPort); virtual ~cVNSIServer(); }; #endif // VNSI_SERVER_H vdr-plugin-vnsiserver-1.3.1/vnsiserver/000077500000000000000000000000001257700704000202175ustar00rootroot00000000000000vdr-plugin-vnsiserver-1.3.1/vnsiserver/allowed_hosts.conf000066400000000000000000000007251257700704000237410ustar00rootroot00000000000000# # allowed_hosts.conf This file describes a number of host addresses that # are allowed to connect to the streamdev server running # with the Video Disk Recorder (VDR) on this system. # Syntax: # # IP-Address[/Netmask] # 127.0.0.1 # always accept localhost 192.168.0.0/24 # any host on the local net #204.152.189.113 # a specific host #0.0.0.0/0 # any host on any net (USE THIS WITH CARE!) vdr-plugin-vnsiserver-1.3.1/wirbelscan_services.h000066400000000000000000000313461257700704000222250ustar00rootroot00000000000000/* * wirbelscan_services.h * * Copyright (C) 2010 Winfried Koehler * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * The author can be reached at: handygewinnspiel AT gmx DOT de * * The project's page is http://wirbel.htpc-forum.de/wirbelscan/index2.html */ #ifndef __WIRBELSCAN_SERVICES_H__ #define __WIRBELSCAN_SERVICES_H__ /******************************************************************** * * wirbelscans plugin service interface * * see http://wirbel.htpc-forum.de/wirbelscan/vdr-servdemo-0.0.1.tgz * for example on usage. * *******************************************************************/ namespace WIRBELSCAN_SERVICE { /* begin of namespace. to use this header file: * #include "../wirbelscan/wirbelscan_services.h" * using namespace WIRBELSCAN_SERVICE; */ /* --- service(s) version ---------------------------------------------------- */ #define SPlugin "wirbelscan_" // prefix #define SInfo "GetVersion" // plugin version and service api #define SCommand "DoCmd#0001" // command api 0001 #define SStatus "Status#0002" // query status #define SSetup "Setup#0001" // get/set setup, GetSetup#XXXX/SetSetup#XXXX #define SCountry "Country#0001" // get list of country IDs and Names #define SSat "Sat#0001" // get list of satellite IDs and Names #define SUser "User#0002" // get/set single user transponder, GetUser#XXXX/SetUser#XXXX /* --- wirbelscan_GetVersion ------------------------------------------------- * Query wirbelscans versions, will fail only if plugin version doesnt support service at all. */ typedef struct { const char * PluginVersion; // plugin version const char * CommandVersion; // commands service version const char * StatusVersion; // status service version const char * SetupVersion; // get/put setup service version const char * CountryVersion; // country ID list version const char * SatVersion; // satellite ID list version const char * UserVersion; // user transponder api version, 0.0.5-pre12b or higher. const char * reserved2; // reserved, dont use. const char * reserved3; // reserved, dont use. } cWirbelscanInfo; /* --- wirbelscan_DoCmd ------------------------------------------------------ * Execute commands. */ typedef enum { CmdStartScan = 0, // start scanning CmdStopScan = 1, // stop scanning CmdStore = 2, // store current setup } s_cmd; typedef struct { s_cmd cmd; // see above. bool replycode; // false, if unsuccessful. } cWirbelscanCmd; /* --- wirbelscan_Status ----------------------------------------------------- * Query Status. Use this to build up your osd information displayed to user. */ typedef enum { StatusUnknown = 0, // no status information available, try again later. StatusScanning = 1, // scan in progress. StatusStopped = 2, // no scan in progress (not started, finished or stopped). StatusBusy = 3, // plugin is busy, try again later. } cStatus; typedef struct { cStatus status; // see above. char curr_device[256]; // name of current device. meaningless, if (status != StatusScanning) uint16_t progress; // progress by means of "percent of predefined transponders". NOTE: will differ in terms of time. meaningless, if (status != StatusScanning) uint16_t strength; // current signal strength as reported from device. NOTE: updated only after switching to new transponder. meaningless, if (status != StatusScanning) char transponder[256]; // current transponder. meaningless, if (status != StatusScanning) uint16_t numChannels; // current number of (all) channels, including those which are new. meaningless, if (status != StatusScanning) uint16_t newChannels; // number of channels found during this scan. meaningless, if (status != StatusScanning) uint16_t nextTransponders; // number of transponders still to be scanned from NIT on this transponder. meaningless, if (status != StatusScanning) uint16_t reserved2; // reserved, dont use. uint16_t reserved3; // reserved, dont use. } cWirbelscanStatus; /* --- wirbelscan_GetSetup, wirbelscan_SetSetup ------------------------------ * Get/Set Setup. Use this to build up your setup osd displayed to user. */ typedef struct { uint16_t verbosity; // 0 (errors only) .. 5 (extended debug); default = 3 (messages) uint16_t logFile; // 0 = off, 1 = stdout, 2 = syslog uint16_t DVB_Type; // DVB-T = 0, DVB-C = 1, DVB-S/S2 = 2, PVRINPUT = 3, PVRINPUT(FM Radio) = 4, ATSC = 5, TRANSPONDER = 999 uint16_t DVBT_Inversion; // AUTO/OFF = 0, AUTO/ON = 1 uint16_t DVBC_Inversion; // AUTO/OFF = 0, AUTO/ON = 1 uint16_t DVBC_Symbolrate; // careful here - may change. AUTO = 0, 6900 = 1, 6875 = 2 (...) 14 = 5483, 15 = ALL uint16_t DVBC_QAM; // AUTO = 0,QAM64 = 1, QAM128 = 2, QAM256 = 3, ALL = 4 uint16_t CountryId; // the id according to country, found in country list, see wirbelscan_GetCountry uint16_t SatId; // the id according to satellite, found in list, see wirbelscan_GetSat uint32_t scanflags; // bitwise flag of wanted channels: TV = (1 << 0), RADIO = (1 << 1), FTA = (1 << 2), SCRAMBLED = (1 << 4), HDTV = (1 << 5) uint16_t ATSC_type; // VSB = 0, QAM = 1, both VSB+QAM = 2 uint16_t stuffing[6]; // dont use. } cWirbelscanScanSetup; /* --- wirbelscan_GetCountry, wirbelscan_GetSat ------------------------------ * Use this to build up your setup OSD - user needs to choose satellite and * country by name and assign correct IDs to setup, see * * cWirbelscanScanSetup::CountryId * * cWirbelscanScanSetup::SatId. * * 1) Query needed buffer size with cPreAllocBuffer::size == 0 * 2) allocate memory in your plugin, to be at least: cPreAllocBuffer::size * sizeof(SListItem) * 3) second call fills buffer with count * sizeof(SListItem) bytes. * 4) access to items as SListItem* */ typedef struct { int id; char short_name[8]; char full_name[64]; } SListItem; typedef struct { uint32_t size; uint32_t count; SListItem * buffer; } cPreAllocBuffer; /* --- wirbelscan_GetUser, wirbelscan_SetUser -------------------------------- * Scan a user defined Transponder. Service() expects a pointer to uint32_t Data[3]; * Data should be initialized and read using class cUserTransponder. * * --------------------------------------------- * id : 9 country-id/sat-id * frequency : 21 DVB-T: 177500..858000; ATSC/DVB-C: 73000..858000, DVB-S/S2: 2000..15000 * polarisation : 2 0=h, 1=v, 2=l, 3=r * type : 3 DVB_Type * symbolrate : 17 i.e. 27500, 6900 * fec_hi : 4 DVB-S/S2: 1=1/2, 2=2/3, 3=3/4, 4=4/5, 5=5/6, 6=6/7, 7=7/8, 8=8/9, 9=forbidden, 10=3/5, 11=9/10 * DVB-T 1=1/2, 2=2/3, 3=3/4, 4=forbidden, 5=5/6, 6=forbidden, 7=7/8, 8..15=forbidden * fec_lo : 4 DVB-T 1=1/2, 2=2/3, 3=3/4, 4=forbidden, 5=5/6, 6=forbidden, 7=7/8, 8..15=forbidden * modulation : 4 DVB-C: 0=forbidden, 1=QAM16, 2=QAM32, 3=QAM64, 4=QAM128, 5=QAM256, 6..15: forbidden * DVB-T: 0=QPSK, 1=QAM16, 2=forbidden, 3=QAM64, 4..15: forbidden * DVB-S: 0=QPSK, 1=QAM16, 2..8: forbidden, 9=PSK8, 10..15:forbidden * ATSC: 0..2: forbidden, 3=QAM64, 4=forbidden, 5=QAM256, 6=QAM_AUTO, 7=VSB8, 8=VSB16, 9..15=forbidden * orbit : 12 i.e. 192 for S19E2 * we_flag : 1 0=west, 1=east * rolloff : 2 0=0,35, 1=0,25, 2=0,20 * 2nd_gen_sys : 1 0=DVB-S/T, 1=DVB-S2/T2 * bw : 2 0=8MHz, 1=7MHz, 2=6MHz, 3=5MHz * priority : 1 0=LP, 1=HP * hierarchy : 4 0=OFF, 1=alpha1, 2=alpha2, 3=alpha4 * guardinterval : 2 0=1/32, 1=1/16, 2=1/8, 3=1/4 * transmission : 2 0=2k, 1=8k, 2=4k * inversion : 1 0=OFF, 1=ON * use_nit : 1 0=OFF, 1=ON * reserved : 3 always 0 * --------------------------------------------- */ #if ! defined(uint32_t) || ! defined(uint8_t) #include #endif #define P(v,b,p) ((v & ((1 << b) -1)) << p) #define G(v,b,p) ((v >> p) & ((1 << b) -1)) class cUserTransponder { private: uint32_t data[3]; public: ~cUserTransponder() { }; cUserTransponder(uint32_t * Data) { data[0] = *(Data + 0); data[1] = *(Data + 1); data[2] = *(Data + 2); }; // DVB-T cUserTransponder(uint8_t id, uint32_t frequency, uint8_t modulation, uint8_t fec_hp, uint8_t fec_lp, uint8_t bw, uint8_t priority, uint8_t hierarchy, uint8_t guard, uint8_t tm, uint8_t inversion, uint8_t use_nit) { data[0] = P(id,9,23) | P(frequency,21,2); data[1] = P(fec_hp,4,8) | P(fec_lp,4,4) | P(modulation,4,0); data[2] = P(bw,2,14) | P(priority,1,13) | P(hierarchy,4,9) | P(guard,2,7) | P(tm,2,5) | P(inversion,1,4) | P(use_nit,1,3); }; // DVB-C cUserTransponder(uint8_t id, uint32_t frequency, uint32_t symbolrate, uint8_t modulation, uint8_t inversion, uint8_t use_nit) { data[0] = P(id,9,23) | P(frequency,21,2); data[1] = P(1,3,29) | P(symbolrate,17,12) | P(modulation,4,0); data[2] = P(inversion,1,4) | P(use_nit,1,3); }; // DVB-S cUserTransponder(uint8_t id, uint8_t system, uint32_t frequency, uint8_t polarisation, uint32_t symbolrate, uint8_t modulation, uint8_t fec, uint16_t orbit, uint8_t we_flag, uint8_t rolloff, uint8_t use_nit) { data[0] = P(id,9,23) | P(frequency,21,2) | P(polarisation,2,0); data[1] = P(2,3,29) | P(symbolrate,17,12) | P(fec,4,8) | P(modulation,4,0); data[2] = P(orbit,12,20) | P(we_flag,1,19) | P(rolloff,2,17) | P(system,1,16) | P(use_nit,1,3); }; // ATSC cUserTransponder(uint8_t id, uint32_t frequency, uint8_t modulation, uint8_t use_nit) { data[0] = P(id,9,23) | P(frequency,21,2); data[1] = P(5,3,29) | P(modulation,4,0); data[2] = P(use_nit,1,3); }; const uint32_t * Data(void) { return data; }; int Id(void) { return G(data[0],9,23); }; int Frequency(void) { return G(data[0],21,2); }; int Polarisation(void){ return G(data[0],2,0); }; int Type(void) { return G(data[1],3,29); }; int Symbolrate(void) { return G(data[1],17,12); }; int FecHP(void) { return G(data[1],4,8); }; int FecLP(void) { return G(data[1],4,4); }; int Modulation(void) { return G(data[1],4,0); }; int Orbit(void) { return G(data[2],12,20); }; int EastFlag(void) { return G(data[2],1,19); }; int Rolloff(void) { return G(data[2],2,17); }; int Satsystem(void) { return G(data[2],1,16) + 5; }; /*deprecated. use System() instead.*/ int System(void) { return G(data[2],1,16) + 5; }; int Bandwidth(void) { return (8 - G(data[2],2,14)) * (int) 1E6; }; int Priority(void) { return G(data[2],1,13); }; int Hierarchy(void) { return G(data[2],4,9); }; int Guard(void) { return G(data[2],2,7); }; int Transmission(void){ return G(data[2],2,5); }; int Inversion(void) { return G(data[2],1,4); }; int UseNit(void) { return G(data[2],1,3); }; bool IsTerr(void) { return IsType(0); }; bool IsCable(void) { return IsType(1); }; bool IsSat(void) { return IsType(2); }; bool IsAtsc(void) { return IsType(5); }; bool IsType(int type) { return type == Type(); }; }; } /* end of namespace, dont touch */ #endif