├── LICENSE ├── Makefile ├── README.md ├── crc32.c ├── crc32.h ├── dvb2dvb.c ├── dvb2dvb.h ├── dvbmod.h ├── example.json ├── json.c ├── json.h ├── parse_config.c ├── parse_config.h ├── psi_create.c ├── psi_create.h ├── psi_read.c ├── psi_read.h ├── ringbuffer.c └── ringbuffer.h /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -g -Wall -W -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE 2 | LIBS = -lpthread -lcurl -lm 3 | OBJS = dvb2dvb.o psi_read.o psi_create.o crc32.o json.o parse_config.o ringbuffer.o 4 | 5 | all: dvb2dvb 6 | 7 | dvb2dvb: $(OBJS) 8 | $(CC) $(CFLAGS) $(LIBS) -o dvb2dvb $(OBJS) 9 | 10 | dvb2dvb.o: dvb2dvb.c dvb2dvb.h psi_read.h psi_create.h crc32.h ringbuffer.h 11 | $(CC) $(CFLAGS) -c -o dvb2dvb.o dvb2dvb.c 12 | 13 | psi_create.o: psi_create.c dvb2dvb.h psi_create.h crc32.h 14 | $(CC) $(CFLAGS) -c -o psi_create.o psi_create.c 15 | 16 | psi_read.o: psi_read.c dvb2dvb.h psi_read.h crc32.h 17 | $(CC) $(CFLAGS) -c -o psi_read.o psi_read.c 18 | 19 | crc32.o: crc32.c crc32.h 20 | $(CC) $(CFLAGS) -c -o crc32.o crc32.c 21 | 22 | json.o: json.c json.h 23 | $(CC) $(CFLAGS) -c -o json.o json.c 24 | 25 | parse_config.o: parse_config.c parse_config.h 26 | $(CC) $(CFLAGS) -c -o parse_config.o parse_config.c 27 | 28 | ringbuffer.o: ringbuffer.c ringbuffer.h 29 | $(CC) $(CFLAGS) -c -o ringbuffer.o ringbuffer.c 30 | 31 | 32 | clean: 33 | rm -f dvb2dvb $(OBJS) *~ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dvb2dvb 2 | ======= 3 | 4 | A tool to convert multiple VBR single-program DVB transport streams to 5 | a CBR multi-program transport stream suitable for feeding to a DVB 6 | modulator. 7 | 8 | 9 | Introduction 10 | ============ 11 | 12 | dvb2dvb was designed to integrate tvheadend (http://tvheadend.org) 13 | with a IT9507-based USB DVB-T modulator. It allows you to read 14 | multiple TV and radio channels from one or more tvheadend servers (via 15 | http streaming) and to output those channels as a single DVB-T 16 | multiplex. 17 | 18 | Full metadata (channel names, channel numbers (LCNs) and EIT 19 | (now/next)) are included in the output stream. 20 | 21 | It has been tested with the Linux driver and "sendts" example 22 | application available at 23 | 24 | https://github.com/linuxstb/it9507/ 25 | 26 | The DVB-T modulator is available to purchase here: 27 | 28 | http://www.idealez.com/hides/product-detail/en_US/69859 29 | 30 | The input streams must contain as a minumum the PAT, PMT and SDT 31 | tables. These are rewritten by dvb2dvb, which also adds a new NIT. EIT 32 | (present/following) is rewritten and passed through to the output 33 | stream if it is available in the input streams. 34 | 35 | The input streams are multiplexed to a single output stream based on 36 | the PCRs, which must be present in the input streams. These are 37 | sometimes contained within the video PID, but are often transmitted in 38 | their own PIDs. 39 | 40 | A recent (November 2014 or later) git master version of tvheadend 41 | using the "passthrough" output format will generate suitable streams 42 | to be used with dvb2dvb. 43 | 44 | Encrypted streams are fully supported by dvb2dvb, assuming they can be 45 | descrambled by tvheadend. dvb2dvb will remove all CA-related 46 | descriptors and flags from the streams, so they appear as FTA services 47 | to the DVB-T receiver. 48 | 49 | 50 | Building 51 | ======== 52 | 53 | Just type "make" in the source code directory. Two libraries are 54 | required - pthreads and libcurl. 55 | 56 | 57 | Current status 58 | ============== 59 | 60 | dvb2dvb is still under development. The basic functionality is 61 | working but not all the stream output parameters are user-configurable 62 | and the code is not stable or resilient to errors in the input 63 | streams. 64 | 65 | 66 | Usage 67 | ===== 68 | 69 | ./dvb2dvb config.json 70 | 71 | See example.json for an example configuration file. Note that dvb2dvb 72 | currently only supports one output mux, although the config file 73 | format allows for more. (This feature will be added in the near future). 74 | 75 | 76 | Copyright/Licence 77 | ================= 78 | 79 | dvb2dvb is (C) 2014 Dave Chapman. 80 | 81 | This program is free software; you can redistribute it and/or modify 82 | it under the terms of the GNU General Public License as published by 83 | the Free Software Foundation; either version 2 of the License, or 84 | (at your option) any later version. 85 | 86 | This program is distributed in the hope that it will be useful, 87 | but WITHOUT ANY WARRANTY; without even the implied warranty of 88 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 89 | GNU General Public License for more details. 90 | 91 | You should have received a copy of the GNU General Public License along 92 | with this program; if not, write to the Free Software Foundation, Inc., 93 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 94 | 95 | 96 | dvb2dvb includes json-parser from https://github.com/udp/json-parser - 97 | see the headers in json.[ch] for licensing information. 98 | -------------------------------------------------------------------------------- /crc32.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | dvb2dvb - combine multiple SPTS to a MPTS 4 | 5 | Copyright (C) 2014 Dave Chapman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | */ 22 | 23 | #include 24 | #include "crc32.h" 25 | 26 | /* CRC code taken from tvheadend */ 27 | 28 | 29 | /** 30 | * CRC32 31 | */ 32 | static uint32_t crc_tab[256] = { 33 | 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 34 | 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 35 | 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 36 | 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 37 | 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 38 | 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 39 | 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 40 | 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 41 | 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 42 | 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 43 | 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 44 | 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 45 | 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 46 | 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 47 | 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 48 | 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 49 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 50 | 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 51 | 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 52 | 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 53 | 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 54 | 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 55 | 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 56 | 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 57 | 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 58 | 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 59 | 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 60 | 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 61 | 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 62 | 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 63 | 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 64 | 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 65 | 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 66 | 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 67 | 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 68 | 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 69 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 70 | 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 71 | 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 72 | 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 73 | 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 74 | 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 75 | 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 76 | }; 77 | 78 | uint32_t psi_crc32(uint8_t *data, size_t datalen, uint32_t crc) 79 | { 80 | while(datalen--) 81 | crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff]; 82 | 83 | return crc; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /crc32.h: -------------------------------------------------------------------------------- 1 | #ifndef _CRC32_H 2 | #define _CRC32_H 3 | 4 | #include 5 | #include 6 | 7 | uint32_t psi_crc32(uint8_t *data, size_t datalen, uint32_t crc); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /dvb2dvb.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | dvb2dvb - combine multiple SPTS to a MPTS 4 | 5 | Copyright (C) 2014 Dave Chapman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "dvb2dvb.h" 35 | #include "psi_read.h" 36 | #include "psi_create.h" 37 | #include "crc32.h" 38 | #include "parse_config.h" 39 | 40 | static uint8_t null_packet[188] = { 41 | 0x47, 0x1f, 0xff, 0x10, 0xff, 0xff, 0xff, 0xff, 42 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 43 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 44 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 45 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 46 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 47 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 48 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 49 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 50 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 51 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 52 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 53 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 54 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 55 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 56 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 57 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 58 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 59 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 60 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 61 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 63 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 64 | 0xff, 0xff, 0xff, 0xff 65 | }; 66 | 67 | void dump_service(struct service_t* services, int i) 68 | { 69 | fprintf(stderr,"Service %d:\n",i); 70 | fprintf(stderr," URL: %s\n",services[i].url); 71 | fprintf(stderr," service_id: OLD=%d NEW=%d\n",services[i].service_id,services[i].new_service_id); 72 | fprintf(stderr," name: %s\n",services[i].name); 73 | fprintf(stderr," pmt_pid: OLD=%d NEW=%d\n",services[i].pmt_pid,services[i].new_pmt_pid); 74 | fprintf(stderr," pcr_pid: OLD=%d NEW=%d\n",services[i].pcr_pid,services[i].pid_map[services[i].pcr_pid]); 75 | fprintf(stderr," lcn: %d\n",services[i].lcn); 76 | } 77 | 78 | void check_cc(char *msg, int service, uint8_t *my_cc, uint8_t *buf) 79 | { 80 | if (buf[0] != 0x47) { 81 | fprintf(stderr,"%s: Service %d, NO SYNC BYTE: 0x%02x 0x%02x 0x%02x 0x%02x\n",msg,service,buf[0],buf[1],buf[2],buf[3]); 82 | return; 83 | } 84 | 85 | int pid = (((buf[1] & 0x1f) << 8) | buf[2]); 86 | int discontinuity_indicator=(buf[5]&0x80)>>7; 87 | int adaption_field_control=(buf[3]&0x30)>>4; 88 | if (my_cc[pid]==0xff) { 89 | my_cc[pid]=buf[3]&0x0f; 90 | } else { 91 | if ((adaption_field_control!=0) && (adaption_field_control!=2)) { 92 | my_cc[pid]++; my_cc[pid]%=16; 93 | } 94 | } 95 | 96 | if ((discontinuity_indicator==0) && (my_cc[pid]!=(buf[3]&0x0f))) { 97 | fprintf(stderr,"%s: Service %d, PID %d - packet incontinuity - expected %02x, found %02x\n",msg,service,pid,my_cc[pid],buf[3]&0x0f); 98 | my_cc[pid]=buf[3]&0x0f; 99 | } 100 | } 101 | 102 | 103 | static size_t 104 | curl_callback(void *contents, size_t size, size_t nmemb, void *userp) 105 | { 106 | struct service_t* sv = userp; 107 | int count = size*nmemb; 108 | 109 | // Check input stream for CC errors 110 | int needed = 188 - sv->curl_bytes; 111 | int bytes_left = count; 112 | // fprintf(stderr,"Processing %d bytes, needed=%d\n",count,needed); 113 | uint8_t *p = contents; 114 | while (bytes_left >= needed) { 115 | memcpy(&sv->curl_buf[sv->curl_bytes],p,needed); 116 | bytes_left -= needed; 117 | sv->curl_bytes = 0; 118 | p += needed; 119 | //check_cc("curl",sv->id, &sv->curl_cc[0], &sv->curl_buf[0]); 120 | needed = 188; 121 | } 122 | if (bytes_left) { 123 | sv->curl_bytes = bytes_left; 124 | memcpy(&sv->curl_buf[0],p,sv->curl_bytes); 125 | } 126 | // fprintf(stderr,"End of processing, %d bytes, byte_left=%d\n",count,bytes_left); 127 | 128 | int n = rb_write(&sv->inbuf, contents, count); 129 | 130 | if (n < count) { 131 | fprintf(stderr,"\nERROR: Stream %d, Input buffer full, dropping %d bytes\n",sv->id,(count)-n); 132 | } 133 | 134 | /* Confirm there are bytes in the buffer */ 135 | sv->status=1; 136 | 137 | //fprintf(stderr,"curl_callback, stream=%s, size=%d, written=%d\n",sv->name,(int)count,n); 138 | return count; /* Pretend we've consumed all */ 139 | 140 | } 141 | 142 | static void *curl_thread(void* userp) 143 | { 144 | struct service_t *sv = userp; 145 | CURL *curl; 146 | 147 | rb_init(&sv->inbuf); 148 | 149 | curl = curl_easy_init(); 150 | curl_easy_setopt(curl, CURLOPT_URL, sv->url); 151 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback); 152 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)sv); 153 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "dvb2dvb/git-master"); 154 | curl_easy_perform(curl); /* ignores error */ 155 | curl_easy_cleanup(curl); 156 | 157 | return NULL; 158 | } 159 | 160 | /* Read PAT/PMT/SDT from stream and stop at first packet with PCR */ 161 | int init_service(struct service_t* sv) 162 | { 163 | uint8_t buf[188]; 164 | int n; 165 | int pid; 166 | int i = 0; 167 | 168 | // First find the PAT, to identify the service_id and pmt_pid 169 | while(1) { 170 | n = rb_read(&sv->inbuf,buf,188); 171 | check_cc("rb_read0",sv->id, &sv->my_cc[0], buf); 172 | (void)n; 173 | i++; 174 | pid = (((buf[1] & 0x1f) << 8) | buf[2]); 175 | 176 | //fprintf(stderr,"Searching for PAT, pid=%d %02x %02x %02x %02x\n",pid,buf[0],buf[1],buf[2],buf[3]); 177 | if (pid==0) { 178 | process_pat(sv,buf); 179 | break; 180 | } 181 | } 182 | 183 | // Now process the other tables, in any order 184 | // PMT: sv->pmt_pid 185 | // SDT: 186 | while((!sv->pmt.length) || (!sv->sdt.length)) { 187 | n = rb_read(&sv->inbuf,buf,188); 188 | check_cc("rb_read1",sv->id, &sv->my_cc[0], buf); 189 | i++; 190 | pid = (((buf[1] & 0x1f) << 8) | buf[2]); 191 | if (pid==sv->pmt_pid) { 192 | process_section(&sv->next_pmt,&sv->pmt,buf,0x02); 193 | } else if (pid==17) { 194 | process_section(&sv->next_sdt,&sv->sdt,buf,0x42); 195 | if (sv->sdt.length) { 196 | process_sdt(sv); 197 | } 198 | } 199 | } 200 | 201 | process_pmt(sv); 202 | 203 | //fprintf(stderr,"Read SDT (%d bytes) and PMT (%d bytes)\n",sv->sdt.length,sv->pmt.length); 204 | 205 | // Read until we find a packet with a PCR 206 | // TODO: Merge this into the loop above, so we are always using the latest PMT/PAT when we have found a PCR. 207 | 208 | //fprintf(stderr,"Searching for PCR...\n"); 209 | 210 | // Create our new PMT section, removing unused streams and references to CA descriptors 211 | create_pmt(sv); 212 | 213 | if (sv->ait_pid) { 214 | create_ait(sv); 215 | } 216 | return 0; 217 | } 218 | 219 | void read_to_next_pcr(struct mux_t* mux, struct service_t* sv) 220 | { 221 | int found = 0; 222 | uint8_t* buf = (uint8_t*)(&sv->buf) + 188 * sv->packets_in_buf; 223 | 224 | while (!found) { 225 | int n = rb_read(&sv->inbuf,buf,188); 226 | check_cc("rb_read2",sv->id, &sv->my_cc[0], buf); 227 | (void)n; 228 | int pid = (((buf[1] & 0x1f) << 8) | buf[2]); 229 | if (pid==sv->pcr_pid) { 230 | if (((buf[3] & 0x20) == 0x20) && (buf[4] > 5) && (buf[5] & 0x10)) { 231 | sv->first_pcr = sv->second_pcr; 232 | sv->second_pcr = (uint64_t)buf[6] << 25; 233 | sv->second_pcr |= (uint64_t)buf[7] << 17; 234 | sv->second_pcr |= (uint64_t)buf[8] << 9; 235 | sv->second_pcr |= (uint64_t)buf[9] << 1; 236 | sv->second_pcr |= ((uint64_t)buf[10] >> 7) & 0x01; 237 | sv->second_pcr *= 300; 238 | sv->second_pcr += ((buf[10] & 0x01) << 8) | buf[11]; 239 | 240 | if (sv->second_pcr < sv->first_pcr) { 241 | fprintf(stderr,"WARNING: PCR wraparound - first_pcr=%s",pts2hmsu(sv->first_pcr,'.')); 242 | fprintf(stderr,", second_pcr=%s",pts2hmsu(sv->second_pcr,'.')); 243 | } 244 | found = 1; 245 | } 246 | } 247 | 248 | if (pid==0x12) { 249 | process_section(&sv->next_eit,&sv->eit,buf,0x4e); // EITpf, actual TS 250 | if (sv->eit.length) { 251 | struct section_t new_eit; 252 | if (rewrite_eit(&new_eit, &sv->eit, sv->service_id, sv->new_service_id, sv->onid, mux) == 0) { // This is for this service 253 | int npackets = copy_section(buf, &new_eit, 0x12); 254 | sv->packets_in_buf += npackets; 255 | buf += npackets * 188; 256 | } 257 | sv->eit.length = 0; // Clear section, we are done with it. 258 | } 259 | } 260 | 261 | if (sv->pid_map[pid]) { 262 | // Change PID 263 | buf[1] = (buf[1] & ~0x1f) | ((sv->pid_map[pid] & 0x1f00) >> 8); 264 | buf[2] = sv->pid_map[pid] & 0x00ff; 265 | 266 | sv->packets_in_buf++; 267 | buf += 188; 268 | } 269 | } 270 | } 271 | 272 | void sync_to_pcr(struct service_t* sv) 273 | { 274 | int n; 275 | int pid; 276 | uint8_t buf[188]; 277 | 278 | while (1) { 279 | n = rb_read(&sv->inbuf,buf,188); 280 | check_cc("rb_read3",sv->id, &sv->my_cc[0], buf); 281 | (void)n; 282 | pid = (((buf[1] & 0x1f) << 8) | buf[2]); 283 | if (pid==sv->pmt_pid) { 284 | process_section(&sv->next_pmt,&sv->pmt,buf,0x02); 285 | } else if (pid==17) { 286 | process_section(&sv->next_sdt,&sv->sdt,buf,0x42); 287 | } else if (pid==sv->pcr_pid) { 288 | // e.g. 4709 0320 b7 10 ff5b d09c 00ab 289 | if (((buf[3] & 0x20) == 0x20) && (buf[4] > 5) && (buf[5] & 0x10)) { 290 | sv->start_pcr = (uint64_t)buf[6] << 25; 291 | sv->start_pcr |= (uint64_t)buf[7] << 17; 292 | sv->start_pcr |= (uint64_t)buf[8] << 9; 293 | sv->start_pcr |= (uint64_t)buf[9] << 1; 294 | sv->start_pcr |= ((uint64_t)buf[10] >> 7) & 0x01; 295 | sv->start_pcr *= 300; 296 | sv->start_pcr += ((buf[10] & 0x01) << 8) | buf[11]; 297 | sv->second_pcr = sv->start_pcr; 298 | fprintf(stderr,"Service %d, pid=%d, start_pcr=%lld (%s)\n",sv->id,pid,sv->start_pcr,pts2hmsu(sv->start_pcr,'.')); 299 | memcpy(&sv->buf,buf,188); 300 | sv->packets_in_buf = 1; 301 | return; 302 | } 303 | } 304 | } 305 | } 306 | 307 | /* Function based on code in the tsrfsend.c application by Avalpa 308 | Digital Engineering srl */ 309 | static int calc_channel_capacity(struct dvb_modulator_parameters *params) 310 | { 311 | uint64_t channel_capacity; 312 | int temp; 313 | 314 | switch (params->constellation) { 315 | case QPSK: 316 | temp = 0; 317 | break; 318 | case QAM_16: 319 | temp = 1; 320 | break; 321 | case QAM_64: 322 | temp = 2; 323 | break; 324 | default: 325 | fprintf(stderr,"Invalid constellation, aborting\n"); 326 | exit(1); 327 | } 328 | channel_capacity = params->bandwidth_hz * 1000; 329 | channel_capacity = channel_capacity * ( temp * 2 + 2); 330 | 331 | switch (params->guard_interval) { 332 | case GUARD_INTERVAL_1_32: 333 | channel_capacity = channel_capacity*32/33; 334 | break; 335 | case GUARD_INTERVAL_1_16: 336 | channel_capacity = channel_capacity*16/17; 337 | break; 338 | case GUARD_INTERVAL_1_8: 339 | channel_capacity = channel_capacity*8/9; 340 | break; 341 | case GUARD_INTERVAL_1_4: 342 | channel_capacity = channel_capacity*4/5; 343 | break; 344 | default: 345 | fprintf(stderr,"Invalid guard interval, aborting\n"); 346 | exit(1); 347 | } 348 | switch (params->code_rate_HP) { 349 | case FEC_1_2: 350 | channel_capacity = channel_capacity*1/2; 351 | break; 352 | case FEC_2_3: 353 | channel_capacity = channel_capacity*2/3; 354 | break; 355 | case FEC_3_4: 356 | channel_capacity = channel_capacity*3/4; 357 | break; 358 | case FEC_5_6: 359 | channel_capacity = channel_capacity*5/6; 360 | break; 361 | case FEC_7_8: 362 | channel_capacity = channel_capacity*7/8; 363 | break; 364 | default: 365 | fprintf(stderr,"Invalid coderate, aborting\n"); 366 | exit(1); 367 | } 368 | 369 | return channel_capacity/544*423; 370 | } 371 | 372 | static void *output_thread(void* userp) 373 | { 374 | struct mux_t *m = userp; 375 | int mod_fd; 376 | int result; 377 | int channel_capacity; 378 | int gain; 379 | 380 | /* Open Device */ 381 | if ((mod_fd = open(m->device, O_RDWR)) < 0) { 382 | fprintf(stderr,"Failed to open device.\n"); 383 | return; 384 | } 385 | 386 | m->dvbmod_params.cell_id = 0; 387 | result = ioctl(mod_fd, DVBMOD_SET_PARAMETERS, &m->dvbmod_params); 388 | 389 | struct dvb_modulator_gain_range gain_range; 390 | gain_range.frequency_khz = m->dvbmod_params.frequency_khz; 391 | result = ioctl(mod_fd, DVBMOD_GET_RF_GAIN_RANGE, &gain_range); 392 | fprintf(stderr,"Gain range: %d to %d\n",gain_range.min_gain,gain_range.max_gain); 393 | 394 | result = ioctl(mod_fd, DVBMOD_SET_RF_GAIN, &m->gain); 395 | fprintf(stderr,"Gain set to %d\n",m->gain); 396 | 397 | /* Wait for 4MB in the ringbuffer */ 398 | while (rb_get_bytes_used(&m->outbuf) < 10*1024*1024) { 399 | usleep(50000); 400 | } 401 | 402 | /* The main transfer loop */ 403 | unsigned char buf[188*200]; 404 | int n; 405 | unsigned long long bytes_sent = 0; 406 | while(1) { 407 | n = rb_read(&m->outbuf,buf,sizeof(buf)); 408 | if (n == 0) { break; } 409 | 410 | int to_write = n; 411 | int bytes_done = 0; 412 | while (bytes_done < to_write) { 413 | n = write(mod_fd,buf+bytes_done,to_write-bytes_done); 414 | if (n == 0) { 415 | /* This shouldn't happen */ 416 | fprintf(stderr,"Zero write\n"); 417 | usleep(500); 418 | } else if (n <= 0) { 419 | fprintf(stderr,"Write error %d: ",n); 420 | perror("Write error: "); 421 | } else { 422 | //if (n < sizeof(buf)) { fprintf(stderr,"Short write - %d bytes\n",n); } 423 | //fprintf(stderr,"Wrote %d\n",n); 424 | bytes_sent += n; 425 | bytes_done += n; 426 | //fprintf(stderr,"Bytes sent: %llu\r",bytes_sent); 427 | } 428 | } 429 | } 430 | 431 | close(mod_fd); 432 | return; 433 | } 434 | 435 | static int ms_to_bits(int channel_capacity, int ms) 436 | { 437 | return ((int64_t)(((int64_t)channel_capacity * (int64_t)(ms)) / 1000)); 438 | } 439 | 440 | /* The main thread for each mux */ 441 | static void *mux_thread(void* userp) 442 | { 443 | struct mux_t *m = userp; 444 | int i,j; 445 | 446 | /* Calculate target bitrate */ 447 | m->channel_capacity = calc_channel_capacity(&m->dvbmod_params); 448 | fprintf(stderr,"Channel capacity = %dbps\n",m->channel_capacity); 449 | 450 | // SI table frequencies, in bits based on above bitrate 451 | m->pat_freq_in_bits = ms_to_bits(m->channel_capacity,200); 452 | m->pmt_freq_in_bits = ms_to_bits(m->channel_capacity,200); 453 | m->sdt_freq_in_bits = ms_to_bits(m->channel_capacity,1000); 454 | m->nit_freq_in_bits = ms_to_bits(m->channel_capacity,1000); 455 | m->ait_freq_in_bits = ms_to_bits(m->channel_capacity,500); 456 | 457 | /* Initialise output ringbuffer */ 458 | rb_init(&m->outbuf); 459 | 460 | /* Start output thread */ 461 | fprintf(stderr,"Creating output thread\n"); 462 | int error = pthread_create(&m->output_threadid, 463 | NULL, /* default attributes please */ 464 | output_thread, 465 | (void *)m); 466 | if (error) { 467 | fprintf(stderr, "Couldn't create output thread - errno %d\n", error); 468 | return; 469 | } 470 | 471 | for (i=0;inservices;i++) { 472 | m->services[i].id = i; 473 | m->services[i].new_pmt_pid = (i+1)*100; 474 | for (j=0;j<8192;j++) { m->services[i].my_cc[j] = 0xff; } 475 | for (j=0;j<8192;j++) { m->services[i].curl_cc[j] = 0xff; } 476 | 477 | fprintf(stderr,"Creating thread %d\n",i); 478 | int error = pthread_create(&m->services[i].curl_threadid, 479 | NULL, /* default attributes please */ 480 | curl_thread, 481 | (void *)&m->services[i]); 482 | 483 | if (error) 484 | fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); 485 | else 486 | fprintf(stderr, "Thread %d, gets %s\n", i, m->services[i].url); 487 | } 488 | 489 | for (i=0;inservices;i++) { 490 | int res = init_service(&m->services[i]); 491 | if (res < 0) { 492 | fprintf(stderr,"Error opening service %d (%s), aborting\n",i,m->services[i].url); 493 | return; 494 | } 495 | 496 | dump_service(m->services,i); 497 | } 498 | 499 | int active_services = 0; 500 | while (active_services < m->nservices) { 501 | for (i=0;inservices;i++) { 502 | if (m->services[i].status) { active_services++; } 503 | } 504 | fprintf(stderr,"Waiting for services - %d started\r",active_services); 505 | } 506 | 507 | #if 0 508 | /* Flush the input buffers */ 509 | for (i=0;inservices;i++) { 510 | int to_skip = (rb_get_bytes_used(&m->services[i].inbuf)/188) * 188; 511 | rb_skip(&m->services[i].inbuf, to_skip); 512 | fprintf(stderr,"Skipped %d bytes from service %d\n",to_skip,i); 513 | // Reset CC counters 514 | for (j=0;j<8192;j++) { m->services[i].my_cc[j] = 0xff; } 515 | } 516 | #endif 517 | 518 | for (i=0;inservices;i++) { 519 | sync_to_pcr(&m->services[i]); 520 | } 521 | 522 | //fprintf(stderr,"Creating PAT - nservices=%d\n",nservices); 523 | 524 | create_pat(&m->pat, m); 525 | create_sdt(&m->sdt, m); 526 | create_nit(&m->nit, m); 527 | 528 | int64_t output_bitpos = 0; 529 | int64_t next_pat_bitpos = 0; 530 | int64_t next_pmt_bitpos = 0; 531 | int64_t next_sdt_bitpos = 0; 532 | int64_t next_nit_bitpos = 0; 533 | int64_t next_ait_bitpos = 0; 534 | 535 | // The main output loop. We output one TS packet (either real or padding) in each iteration. 536 | int x = 1; 537 | int64_t padding_bits = 0; 538 | int eit_cc = 0; 539 | while (1) { 540 | // Ensure we have enough data for every service. 541 | for (i=0;inservices;i++) { 542 | if (m->services[i].packets_in_buf-m->services[i].packets_written == 1) { // Will contain a PCR. 543 | struct service_t* sv = &m->services[i]; 544 | 545 | // Move last packet to start of buffer 546 | sv->first_pcr = sv->second_pcr; 547 | memcpy(&sv->buf, (uint8_t*)(&sv->buf) + (188 * (sv->packets_in_buf-1)), 188); 548 | sv->packets_written = 0; 549 | sv->packets_in_buf = 1; 550 | 551 | read_to_next_pcr(m,sv); 552 | 553 | int64_t pcr_diff = sv->second_pcr - sv->first_pcr; 554 | int npackets = sv->packets_in_buf; 555 | //double packet_duration = pcr_diff / (double)npackets; 556 | //fprintf(stderr,"Stream %d: pcr_diff = %lld, npackets=%lld, packet duration=%.8g ticks\n",i,pcr_diff,npackets,packet_duration); 557 | 558 | // Now calculate the output position for each packet, in terms of total bits written so far. 559 | for (j=0;jpackets_in_buf;j++) { 560 | int64_t packet_pcr = sv->first_pcr + ((j * pcr_diff)/(npackets-1)) - sv->start_pcr; 561 | sv->bitpos[j] = (packet_pcr * m->channel_capacity) / 27000000; 562 | //fprintf(stderr, "Stream %d, packet %d, packet_pcr = %lld, bitpos %lld\n",i,j,packet_pcr,sv->bitpos[j]); 563 | } 564 | } 565 | } 566 | 567 | // Find the service with the most urgent packet (i.e. earliest bitpos) 568 | struct service_t* sv = &m->services[0]; 569 | for (i=1;inservices;i++) { 570 | if (m->services[i].bitpos[m->services[i].packets_written] < sv->bitpos[sv->packets_written]) { 571 | sv = &m->services[i]; 572 | } 573 | } 574 | 575 | //fprintf(stderr,"output_bitpos=%d, sv->bitpos[sv->packets_written]=%d\n",output_bitpos,sv->bitpos[sv->packets_written]); 576 | 577 | #if 0 578 | fprintf(stderr,"output_bitpos next_pat next_pmt next_sdt next_nit"); 579 | for (i=0;inservices;i++) { fprintf(stderr," service_%d",i); } 580 | fprintf(stderr,"\n"); 581 | fprintf(stderr,"%lld %lld %lld %lld %lld",output_bitpos,next_pat_bitpos,next_pmt_bitpos,next_sdt_bitpos,next_sdt_bitpos); 582 | for (i=0;inservices;i++) { fprintf(stderr," %lld",m->services[i].bitpos[m->services[i].packets_written]); } 583 | fprintf(stderr,"\n"); 584 | // return 0; 585 | #endif 586 | 587 | /* Now check for PSI packets */ 588 | int next_psi = 0; 589 | int64_t next_bitpos = sv->bitpos[sv->packets_written]; 590 | if (next_pat_bitpos <= next_bitpos) { next_psi = 1; next_bitpos = next_pat_bitpos; } 591 | if (next_pmt_bitpos <= next_bitpos) { next_psi = 2; next_bitpos = next_pmt_bitpos; } 592 | if (next_sdt_bitpos <= next_bitpos) { next_psi = 3; next_bitpos = next_sdt_bitpos; } 593 | if (next_nit_bitpos <= next_bitpos) { next_psi = 4; next_bitpos = next_nit_bitpos; } 594 | if ((m->services[0].ait_pid) && (next_ait_bitpos <= next_bitpos)) { next_psi = 5; next_bitpos = next_ait_bitpos; } 595 | 596 | /* Output NULL packets until we reach next_bitpos */ 597 | while (next_bitpos > output_bitpos) { 598 | //fprintf(stderr,"next_bitpos=%lld, output_bitpos=%lld \n",next_bitpos,output_bitpos); 599 | rb_write(&m->outbuf, null_packet, 188); 600 | padding_bits += 188*8; 601 | output_bitpos += 188*8; 602 | } 603 | 604 | /* Now output whichever packet is next */ 605 | int n,res; 606 | uint8_t* buf; 607 | int pid; 608 | switch (next_psi) { 609 | case 0: 610 | buf = &sv->buf[188*sv->packets_written]; 611 | pid = (((buf[1] & 0x1f) << 8) | buf[2]); 612 | if (pid==0x12) { // EIT - fix CC 613 | buf[3] = 0x10 | eit_cc; 614 | eit_cc = (eit_cc + 1) % 16; 615 | } 616 | res = rb_write(&m->outbuf, &sv->buf[188*sv->packets_written], 188); 617 | if (res != 188) { fprintf(stderr,"Write error - res=%d\n",res); } 618 | n = 1; 619 | sv->packets_written++; 620 | break; 621 | 622 | case 1: // PAT 623 | n = write_section(&m->outbuf, &m->pat, 0); 624 | next_pat_bitpos += m->pat_freq_in_bits; 625 | break; 626 | 627 | case 2: // PMT 628 | n = 0; 629 | for (i=0;inservices;i++) { 630 | n += write_section(&m->outbuf,&m->services[i].new_pmt, m->services[i].new_pmt_pid); 631 | } 632 | next_pmt_bitpos += m->pmt_freq_in_bits; 633 | break; 634 | 635 | case 3: // SDT 636 | n = write_section(&m->outbuf, &m->sdt, 0x11); 637 | next_sdt_bitpos += m->sdt_freq_in_bits; 638 | break; 639 | 640 | case 4: // NIT 641 | n = write_section(&m->outbuf, &m->nit, 0x10); 642 | next_nit_bitpos += m->nit_freq_in_bits; 643 | break; 644 | 645 | case 5: // AIT 646 | n = write_section(&m->outbuf, &m->services[0].ait, m->services[0].ait_pid); 647 | next_ait_bitpos += m->ait_freq_in_bits; 648 | break; 649 | } 650 | output_bitpos += n * 188*8; 651 | 652 | if (x==1000) { 653 | x = 0; 654 | for (i=0;inservices;i++) { 655 | fprintf(stderr,"%10d ",rb_get_bytes_used(&m->services[i].inbuf)); 656 | } 657 | fprintf(stderr,"Average capacity used: %.3g%% Outbuf = %10d \r",100.0*(double)(output_bitpos-padding_bits)/(double)output_bitpos,rb_get_bytes_used(&m->outbuf)); 658 | } 659 | x++; 660 | } 661 | } 662 | 663 | int main(int argc, char* argv[]) 664 | { 665 | int nmuxes; 666 | struct mux_t *muxes; 667 | 668 | if (argc != 2) { 669 | fprintf(stderr,"Usage: dvb2dvb config.json\n"); 670 | return 1; 671 | } 672 | 673 | nmuxes = parse_config(argv[1],&muxes); 674 | 675 | if (nmuxes < 0) { 676 | fprintf(stderr,"[JSON] Error reading config file\n"); 677 | return 1; 678 | } 679 | 680 | if (nmuxes != 1) { 681 | fprintf(stderr,"[JSON] Error - only 1 mux supported for now\n"); 682 | return 1; 683 | } 684 | 685 | /* Must initialize libcurl before any threads are started */ 686 | curl_global_init(CURL_GLOBAL_ALL); 687 | 688 | /* TODO: Do this for each mux */ 689 | 690 | fprintf(stderr,"Creating mux processing thread 0\n"); 691 | int error = pthread_create(&muxes[0].threadid, 692 | NULL, /* default attributes please */ 693 | mux_thread, 694 | (void *)muxes); 695 | 696 | fprintf(stderr,"Created mux thread - error=%d\n",error); 697 | fprintf(stderr,"Waiting for mux thread to terminate...\n"); 698 | 699 | pthread_join(muxes[0].threadid, NULL); 700 | 701 | fprintf(stderr,"Mux thread terminated.\n"); 702 | return 0; 703 | } 704 | -------------------------------------------------------------------------------- /dvb2dvb.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | dvb2dvb - combine multiple SPTS to a MPTS 4 | 5 | Copyright (C) 2014 Dave Chapman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | */ 22 | 23 | #ifndef _DVB2DVB_H 24 | #define _DVB2DVB_H 25 | 26 | #include 27 | #include 28 | #include "ringbuffer.h" 29 | #include "dvbmod.h" 30 | 31 | #ifndef MAX 32 | #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) 33 | #endif 34 | #ifndef MIN 35 | #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) 36 | #endif 37 | 38 | // Buffer size for input packets - must be large enough to hold all packets between two PCR timestamps 39 | // DVB-T max is about 31Mbits/s, pcr max interval is 40ms, so buffer needs to be at least 1240000 bits (about 824 TS packets) 40 | // So we make the buffer hold 2048 TS packets (about 371KB) 41 | #define INPUT_BUFFER_SIZE_IN_PACKETS 2048 42 | 43 | struct section_t 44 | { 45 | int length; 46 | int bytes_read; 47 | uint8_t buf[4096]; 48 | int cc; 49 | }; 50 | 51 | struct hbbtv_t 52 | { 53 | char* url; 54 | char* initial_path; 55 | int application_type; 56 | int AIT_version_number; 57 | }; 58 | 59 | struct service_t 60 | { 61 | int id; 62 | int status; /* 0 = not started, 1 = streaming */ 63 | char *url; 64 | char* name; 65 | 66 | int service_id; 67 | int service_type; 68 | int onid; 69 | int tsid; 70 | int new_service_id; 71 | int lcn; 72 | int pmt_pid; 73 | int pcr_pid; 74 | int new_pmt_pid; /* First PID used (for PMT) in output stream */ 75 | int ait_pid; 76 | uint16_t pid_map[8192]; 77 | int64_t start_pcr; 78 | int64_t first_pcr; 79 | int64_t second_pcr; 80 | int packets_in_buf; 81 | int packets_written; 82 | uint8_t my_cc[8192]; 83 | uint8_t buf[INPUT_BUFFER_SIZE_IN_PACKETS*188]; 84 | int64_t bitpos[INPUT_BUFFER_SIZE_IN_PACKETS]; 85 | 86 | uint8_t curl_cc[8192]; 87 | uint8_t curl_buf[188]; 88 | int curl_bytes; 89 | 90 | struct section_t pmt; 91 | struct section_t sdt; 92 | struct section_t eit; 93 | struct section_t next_pmt; 94 | struct section_t next_sdt; 95 | struct section_t next_eit; 96 | 97 | struct hbbtv_t hbbtv; 98 | struct section_t ait; 99 | 100 | struct section_t new_pmt; 101 | 102 | /* Curl-related fields */ 103 | pthread_t curl_threadid; 104 | 105 | struct ringbuffer_t inbuf; /* Input ringbuffer for curl requests */ 106 | }; 107 | 108 | struct mux_t 109 | { 110 | char* device; 111 | struct dvb_modulator_parameters dvbmod_params; 112 | int gain; 113 | int tsid; 114 | int onid; 115 | int nid; 116 | 117 | int channel_capacity; 118 | int pat_freq_in_bits; 119 | int pmt_freq_in_bits; 120 | int sdt_freq_in_bits; 121 | int nit_freq_in_bits; 122 | int ait_freq_in_bits; 123 | 124 | struct section_t pat; 125 | struct section_t sdt; 126 | struct section_t nit; 127 | 128 | int nservices; 129 | struct service_t* services; 130 | 131 | pthread_t threadid; /* Mux processing thread id */ 132 | pthread_t output_threadid; /* Output thread id */ 133 | 134 | struct ringbuffer_t outbuf; /* Output ringbuffer to write to modulator */ 135 | }; 136 | 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /dvbmod.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * dvbmod.h - DVB modulator kernel API 4 | * 5 | * Copyright (C) 2013 Dave Chapman 6 | * 7 | */ 8 | 9 | #ifndef _DVBMOD_H_ 10 | #define _DVBMOD_H_ 11 | 12 | #include 13 | #include 14 | 15 | /** 16 | * Modulator API structs 17 | */ 18 | 19 | struct dvb_modulator_parameters { 20 | __u32 frequency_khz; /* frequency in KHz */ 21 | fe_transmit_mode_t transmission_mode; 22 | fe_modulation_t constellation; 23 | fe_guard_interval_t guard_interval; 24 | fe_code_rate_t code_rate_HP; 25 | __u16 bandwidth_hz; /* Bandwidth in Hz */ 26 | __u16 cell_id; 27 | }; 28 | 29 | struct dvb_modulator_gain_range { 30 | __u32 frequency_khz; /* frequency in KHz */ 31 | int min_gain; 32 | int max_gain; 33 | }; 34 | 35 | /** 36 | * Modulator API commands 37 | */ 38 | 39 | #define DVBMOD_SET_PARAMETERS _IOW('k', 0x40, struct dvb_modulator_parameters) 40 | #define DVBMOD_SET_RF_GAIN _IOWR('k', 0x41, int) 41 | #define DVBMOD_GET_RF_GAIN _IOR('k', 0x42, int) 42 | #define DVBMOD_GET_RF_GAIN_RANGE _IOWR('k', 0x43, struct dvb_modulator_gain_range) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "bandwidth_hz": 8000, 4 | "transmission_mode": "8K", 5 | "constellation": "QAM_64", 6 | "guard_interval": "1/32", 7 | "code_rate_HP": "7/8", 8 | "gain" : -6, 9 | "onid": 9018, 10 | "nid": 12339 11 | }, 12 | "muxes": [ 13 | { 14 | "device": "/dev/dvbmod0", 15 | "frequency_khz": 802000, 16 | "tsid": 57005, 17 | "services": [ 18 | { 19 | "comment": "This is ignored by dvb2dvb", 20 | "url": "http://localhost:9981/stream/channelnumber/301", 21 | "lcn": 90, 22 | "service_id": 60000, 23 | "hbbtv": { 24 | "url": "http://www.example.com/hbbtv/" 25 | } 26 | }, 27 | { 28 | "comment": "This is ignored by dvb2dvb", 29 | "url": "http://localhost:9981/stream/channelnumber/302", 30 | "lcn": 91, 31 | "service_id": 60001, 32 | "hbbtv": { 33 | "url": "http://www.example.com/hbbtv/" 34 | } 35 | } 36 | ] 37 | }, 38 | { 39 | "device": "/dev/dvbmod1", 40 | "frequency_khz": 810000, 41 | "tsid": 57006, 42 | "services": [ 43 | { 44 | "comment": "This is ignored by dvb2dvb", 45 | "url": "http://localhost:9981/stream/channelnumber/303", 46 | "lcn": 92, 47 | "service_id": 60002 48 | } 49 | ] 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /json.c: -------------------------------------------------------------------------------- 1 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 2 | * 3 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 4 | * https://github.com/udp/json-parser 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #include "json.h" 31 | 32 | #ifdef _MSC_VER 33 | #ifndef _CRT_SECURE_NO_WARNINGS 34 | #define _CRT_SECURE_NO_WARNINGS 35 | #endif 36 | #endif 37 | 38 | const struct _json_value json_value_none; 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | typedef unsigned int json_uchar; 46 | 47 | static unsigned char hex_value (json_char c) 48 | { 49 | if (isdigit(c)) 50 | return c - '0'; 51 | 52 | switch (c) { 53 | case 'a': case 'A': return 0x0A; 54 | case 'b': case 'B': return 0x0B; 55 | case 'c': case 'C': return 0x0C; 56 | case 'd': case 'D': return 0x0D; 57 | case 'e': case 'E': return 0x0E; 58 | case 'f': case 'F': return 0x0F; 59 | default: return 0xFF; 60 | } 61 | } 62 | 63 | typedef struct 64 | { 65 | unsigned long used_memory; 66 | 67 | unsigned int uint_max; 68 | unsigned long ulong_max; 69 | 70 | json_settings settings; 71 | int first_pass; 72 | 73 | const json_char * ptr; 74 | unsigned int cur_line, cur_col; 75 | 76 | } json_state; 77 | 78 | static void * default_alloc (size_t size, int zero, void * user_data) 79 | { 80 | return zero ? calloc (1, size) : malloc (size); 81 | } 82 | 83 | static void default_free (void * ptr, void * user_data) 84 | { 85 | free (ptr); 86 | } 87 | 88 | static void * json_alloc (json_state * state, unsigned long size, int zero) 89 | { 90 | if ((state->ulong_max - state->used_memory) < size) 91 | return 0; 92 | 93 | if (state->settings.max_memory 94 | && (state->used_memory += size) > state->settings.max_memory) 95 | { 96 | return 0; 97 | } 98 | 99 | return state->settings.mem_alloc (size, zero, state->settings.user_data); 100 | } 101 | 102 | static int new_value (json_state * state, 103 | json_value ** top, json_value ** root, json_value ** alloc, 104 | json_type type) 105 | { 106 | json_value * value; 107 | int values_size; 108 | 109 | if (!state->first_pass) 110 | { 111 | value = *top = *alloc; 112 | *alloc = (*alloc)->_reserved.next_alloc; 113 | 114 | if (!*root) 115 | *root = value; 116 | 117 | switch (value->type) 118 | { 119 | case json_array: 120 | 121 | if (value->u.array.length == 0) 122 | break; 123 | 124 | if (! (value->u.array.values = (json_value **) json_alloc 125 | (state, value->u.array.length * sizeof (json_value *), 0)) ) 126 | { 127 | return 0; 128 | } 129 | 130 | value->u.array.length = 0; 131 | break; 132 | 133 | case json_object: 134 | 135 | if (value->u.object.length == 0) 136 | break; 137 | 138 | values_size = sizeof (*value->u.object.values) * value->u.object.length; 139 | 140 | if (! (value->u.object.values = (json_object_entry *) json_alloc 141 | (state, values_size + ((unsigned long) value->u.object.values), 0)) ) 142 | { 143 | return 0; 144 | } 145 | 146 | value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; 147 | 148 | value->u.object.length = 0; 149 | break; 150 | 151 | case json_string: 152 | 153 | if (! (value->u.string.ptr = (json_char *) json_alloc 154 | (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) 155 | { 156 | return 0; 157 | } 158 | 159 | value->u.string.length = 0; 160 | break; 161 | 162 | default: 163 | break; 164 | }; 165 | 166 | return 1; 167 | } 168 | 169 | if (! (value = (json_value *) json_alloc 170 | (state, sizeof (json_value) + state->settings.value_extra, 1))) 171 | { 172 | return 0; 173 | } 174 | 175 | if (!*root) 176 | *root = value; 177 | 178 | value->type = type; 179 | value->parent = *top; 180 | 181 | #ifdef JSON_TRACK_SOURCE 182 | value->line = state->cur_line; 183 | value->col = state->cur_col; 184 | #endif 185 | 186 | if (*alloc) 187 | (*alloc)->_reserved.next_alloc = value; 188 | 189 | *alloc = *top = value; 190 | 191 | return 1; 192 | } 193 | 194 | #define whitespace \ 195 | case '\n': ++ state.cur_line; state.cur_col = 0; \ 196 | case ' ': case '\t': case '\r' 197 | 198 | #define string_add(b) \ 199 | do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); 200 | 201 | #define line_and_col \ 202 | state.cur_line, state.cur_col 203 | 204 | static const long 205 | flag_next = 1 << 0, 206 | flag_reproc = 1 << 1, 207 | flag_need_comma = 1 << 2, 208 | flag_seek_value = 1 << 3, 209 | flag_escaped = 1 << 4, 210 | flag_string = 1 << 5, 211 | flag_need_colon = 1 << 6, 212 | flag_done = 1 << 7, 213 | flag_num_negative = 1 << 8, 214 | flag_num_zero = 1 << 9, 215 | flag_num_e = 1 << 10, 216 | flag_num_e_got_sign = 1 << 11, 217 | flag_num_e_negative = 1 << 12, 218 | flag_line_comment = 1 << 13, 219 | flag_block_comment = 1 << 14; 220 | 221 | json_value * json_parse_ex (json_settings * settings, 222 | const json_char * json, 223 | size_t length, 224 | char * error_buf) 225 | { 226 | json_char error [json_error_max]; 227 | const json_char * end; 228 | json_value * top, * root, * alloc = 0; 229 | json_state state = { 0 }; 230 | long flags; 231 | long num_digits = 0, num_e = 0; 232 | json_int_t num_fraction = 0; 233 | 234 | /* Skip UTF-8 BOM 235 | */ 236 | if (length >= 3 && ((unsigned char) json [0]) == 0xEF 237 | && ((unsigned char) json [1]) == 0xBB 238 | && ((unsigned char) json [2]) == 0xBF) 239 | { 240 | json += 3; 241 | length -= 3; 242 | } 243 | 244 | error[0] = '\0'; 245 | end = (json + length); 246 | 247 | memcpy (&state.settings, settings, sizeof (json_settings)); 248 | 249 | if (!state.settings.mem_alloc) 250 | state.settings.mem_alloc = default_alloc; 251 | 252 | if (!state.settings.mem_free) 253 | state.settings.mem_free = default_free; 254 | 255 | memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); 256 | memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); 257 | 258 | state.uint_max -= 8; /* limit of how much can be added before next check */ 259 | state.ulong_max -= 8; 260 | 261 | for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) 262 | { 263 | json_uchar uchar; 264 | unsigned char uc_b1, uc_b2, uc_b3, uc_b4; 265 | json_char * string = 0; 266 | unsigned int string_length = 0; 267 | 268 | top = root = 0; 269 | flags = flag_seek_value; 270 | 271 | state.cur_line = 1; 272 | 273 | for (state.ptr = json ;; ++ state.ptr) 274 | { 275 | json_char b = (state.ptr == end ? 0 : *state.ptr); 276 | 277 | if (flags & flag_string) 278 | { 279 | if (!b) 280 | { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col); 281 | goto e_failed; 282 | } 283 | 284 | if (string_length > state.uint_max) 285 | goto e_overflow; 286 | 287 | if (flags & flag_escaped) 288 | { 289 | flags &= ~ flag_escaped; 290 | 291 | switch (b) 292 | { 293 | case 'b': string_add ('\b'); break; 294 | case 'f': string_add ('\f'); break; 295 | case 'n': string_add ('\n'); break; 296 | case 'r': string_add ('\r'); break; 297 | case 't': string_add ('\t'); break; 298 | case 'u': 299 | 300 | if (end - state.ptr < 4 || 301 | (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || 302 | (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || 303 | (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || 304 | (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) 305 | { 306 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); 307 | goto e_failed; 308 | } 309 | 310 | uc_b1 = (uc_b1 << 4) | uc_b2; 311 | uc_b2 = (uc_b3 << 4) | uc_b4; 312 | uchar = (uc_b1 << 8) | uc_b2; 313 | 314 | if ((uchar & 0xF800) == 0xD800) { 315 | json_uchar uchar2; 316 | 317 | if (end - state.ptr < 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' || 318 | (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || 319 | (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || 320 | (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || 321 | (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) 322 | { 323 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); 324 | goto e_failed; 325 | } 326 | 327 | uc_b1 = (uc_b1 << 4) | uc_b2; 328 | uc_b2 = (uc_b3 << 4) | uc_b4; 329 | uchar2 = (uc_b1 << 8) | uc_b2; 330 | 331 | uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); 332 | } 333 | 334 | if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F)) 335 | { 336 | string_add ((json_char) uchar); 337 | break; 338 | } 339 | 340 | if (uchar <= 0x7FF) 341 | { 342 | if (state.first_pass) 343 | string_length += 2; 344 | else 345 | { string [string_length ++] = 0xC0 | (uchar >> 6); 346 | string [string_length ++] = 0x80 | (uchar & 0x3F); 347 | } 348 | 349 | break; 350 | } 351 | 352 | if (uchar <= 0xFFFF) { 353 | if (state.first_pass) 354 | string_length += 3; 355 | else 356 | { string [string_length ++] = 0xE0 | (uchar >> 12); 357 | string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); 358 | string [string_length ++] = 0x80 | (uchar & 0x3F); 359 | } 360 | 361 | break; 362 | } 363 | 364 | if (state.first_pass) 365 | string_length += 4; 366 | else 367 | { string [string_length ++] = 0xF0 | (uchar >> 18); 368 | string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F); 369 | string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); 370 | string [string_length ++] = 0x80 | (uchar & 0x3F); 371 | } 372 | 373 | break; 374 | 375 | default: 376 | string_add (b); 377 | }; 378 | 379 | continue; 380 | } 381 | 382 | if (b == '\\') 383 | { 384 | flags |= flag_escaped; 385 | continue; 386 | } 387 | 388 | if (b == '"') 389 | { 390 | if (!state.first_pass) 391 | string [string_length] = 0; 392 | 393 | flags &= ~ flag_string; 394 | string = 0; 395 | 396 | switch (top->type) 397 | { 398 | case json_string: 399 | 400 | top->u.string.length = string_length; 401 | flags |= flag_next; 402 | 403 | break; 404 | 405 | case json_object: 406 | 407 | if (state.first_pass) 408 | (*(json_char **) &top->u.object.values) += string_length + 1; 409 | else 410 | { 411 | top->u.object.values [top->u.object.length].name 412 | = (json_char *) top->_reserved.object_mem; 413 | 414 | top->u.object.values [top->u.object.length].name_length 415 | = string_length; 416 | 417 | (*(json_char **) &top->_reserved.object_mem) += string_length + 1; 418 | } 419 | 420 | flags |= flag_seek_value | flag_need_colon; 421 | continue; 422 | 423 | default: 424 | break; 425 | }; 426 | } 427 | else 428 | { 429 | string_add (b); 430 | continue; 431 | } 432 | } 433 | 434 | if (state.settings.settings & json_enable_comments) 435 | { 436 | if (flags & (flag_line_comment | flag_block_comment)) 437 | { 438 | if (flags & flag_line_comment) 439 | { 440 | if (b == '\r' || b == '\n' || !b) 441 | { 442 | flags &= ~ flag_line_comment; 443 | -- state.ptr; /* so null can be reproc'd */ 444 | } 445 | 446 | continue; 447 | } 448 | 449 | if (flags & flag_block_comment) 450 | { 451 | if (!b) 452 | { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col); 453 | goto e_failed; 454 | } 455 | 456 | if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/') 457 | { 458 | flags &= ~ flag_block_comment; 459 | ++ state.ptr; /* skip closing sequence */ 460 | } 461 | 462 | continue; 463 | } 464 | } 465 | else if (b == '/') 466 | { 467 | if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) 468 | { sprintf (error, "%d:%d: Comment not allowed here", line_and_col); 469 | goto e_failed; 470 | } 471 | 472 | if (++ state.ptr == end) 473 | { sprintf (error, "%d:%d: EOF unexpected", line_and_col); 474 | goto e_failed; 475 | } 476 | 477 | switch (b = *state.ptr) 478 | { 479 | case '/': 480 | flags |= flag_line_comment; 481 | continue; 482 | 483 | case '*': 484 | flags |= flag_block_comment; 485 | continue; 486 | 487 | default: 488 | sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b); 489 | goto e_failed; 490 | }; 491 | } 492 | } 493 | 494 | if (flags & flag_done) 495 | { 496 | if (!b) 497 | break; 498 | 499 | switch (b) 500 | { 501 | whitespace: 502 | continue; 503 | 504 | default: 505 | 506 | sprintf (error, "%d:%d: Trailing garbage: `%c`", 507 | state.cur_line, state.cur_col, b); 508 | 509 | goto e_failed; 510 | }; 511 | } 512 | 513 | if (flags & flag_seek_value) 514 | { 515 | switch (b) 516 | { 517 | whitespace: 518 | continue; 519 | 520 | case ']': 521 | 522 | if (top && top->type == json_array) 523 | flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; 524 | else 525 | { sprintf (error, "%d:%d: Unexpected ]", line_and_col); 526 | goto e_failed; 527 | } 528 | 529 | break; 530 | 531 | default: 532 | 533 | if (flags & flag_need_comma) 534 | { 535 | if (b == ',') 536 | { flags &= ~ flag_need_comma; 537 | continue; 538 | } 539 | else 540 | { 541 | sprintf (error, "%d:%d: Expected , before %c", 542 | state.cur_line, state.cur_col, b); 543 | 544 | goto e_failed; 545 | } 546 | } 547 | 548 | if (flags & flag_need_colon) 549 | { 550 | if (b == ':') 551 | { flags &= ~ flag_need_colon; 552 | continue; 553 | } 554 | else 555 | { 556 | sprintf (error, "%d:%d: Expected : before %c", 557 | state.cur_line, state.cur_col, b); 558 | 559 | goto e_failed; 560 | } 561 | } 562 | 563 | flags &= ~ flag_seek_value; 564 | 565 | switch (b) 566 | { 567 | case '{': 568 | 569 | if (!new_value (&state, &top, &root, &alloc, json_object)) 570 | goto e_alloc_failure; 571 | 572 | continue; 573 | 574 | case '[': 575 | 576 | if (!new_value (&state, &top, &root, &alloc, json_array)) 577 | goto e_alloc_failure; 578 | 579 | flags |= flag_seek_value; 580 | continue; 581 | 582 | case '"': 583 | 584 | if (!new_value (&state, &top, &root, &alloc, json_string)) 585 | goto e_alloc_failure; 586 | 587 | flags |= flag_string; 588 | 589 | string = top->u.string.ptr; 590 | string_length = 0; 591 | 592 | continue; 593 | 594 | case 't': 595 | 596 | if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' || 597 | *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e') 598 | { 599 | goto e_unknown_value; 600 | } 601 | 602 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 603 | goto e_alloc_failure; 604 | 605 | top->u.boolean = 1; 606 | 607 | flags |= flag_next; 608 | break; 609 | 610 | case 'f': 611 | 612 | if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' || 613 | *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' || 614 | *(++ state.ptr) != 'e') 615 | { 616 | goto e_unknown_value; 617 | } 618 | 619 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 620 | goto e_alloc_failure; 621 | 622 | flags |= flag_next; 623 | break; 624 | 625 | case 'n': 626 | 627 | if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' || 628 | *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l') 629 | { 630 | goto e_unknown_value; 631 | } 632 | 633 | if (!new_value (&state, &top, &root, &alloc, json_null)) 634 | goto e_alloc_failure; 635 | 636 | flags |= flag_next; 637 | break; 638 | 639 | default: 640 | 641 | if (isdigit (b) || b == '-') 642 | { 643 | if (!new_value (&state, &top, &root, &alloc, json_integer)) 644 | goto e_alloc_failure; 645 | 646 | if (!state.first_pass) 647 | { 648 | while (isdigit (b) || b == '+' || b == '-' 649 | || b == 'e' || b == 'E' || b == '.') 650 | { 651 | if ( (++ state.ptr) == end) 652 | { 653 | b = 0; 654 | break; 655 | } 656 | 657 | b = *state.ptr; 658 | } 659 | 660 | flags |= flag_next | flag_reproc; 661 | break; 662 | } 663 | 664 | flags &= ~ (flag_num_negative | flag_num_e | 665 | flag_num_e_got_sign | flag_num_e_negative | 666 | flag_num_zero); 667 | 668 | num_digits = 0; 669 | num_fraction = 0; 670 | num_e = 0; 671 | 672 | if (b != '-') 673 | { 674 | flags |= flag_reproc; 675 | break; 676 | } 677 | 678 | flags |= flag_num_negative; 679 | continue; 680 | } 681 | else 682 | { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b); 683 | goto e_failed; 684 | } 685 | }; 686 | }; 687 | } 688 | else 689 | { 690 | switch (top->type) 691 | { 692 | case json_object: 693 | 694 | switch (b) 695 | { 696 | whitespace: 697 | continue; 698 | 699 | case '"': 700 | 701 | if (flags & flag_need_comma) 702 | { sprintf (error, "%d:%d: Expected , before \"", line_and_col); 703 | goto e_failed; 704 | } 705 | 706 | flags |= flag_string; 707 | 708 | string = (json_char *) top->_reserved.object_mem; 709 | string_length = 0; 710 | 711 | break; 712 | 713 | case '}': 714 | 715 | flags = (flags & ~ flag_need_comma) | flag_next; 716 | break; 717 | 718 | case ',': 719 | 720 | if (flags & flag_need_comma) 721 | { 722 | flags &= ~ flag_need_comma; 723 | break; 724 | } 725 | 726 | default: 727 | sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b); 728 | goto e_failed; 729 | }; 730 | 731 | break; 732 | 733 | case json_integer: 734 | case json_double: 735 | 736 | if (isdigit (b)) 737 | { 738 | ++ num_digits; 739 | 740 | if (top->type == json_integer || flags & flag_num_e) 741 | { 742 | if (! (flags & flag_num_e)) 743 | { 744 | if (flags & flag_num_zero) 745 | { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b); 746 | goto e_failed; 747 | } 748 | 749 | if (num_digits == 1 && b == '0') 750 | flags |= flag_num_zero; 751 | } 752 | else 753 | { 754 | flags |= flag_num_e_got_sign; 755 | num_e = (num_e * 10) + (b - '0'); 756 | continue; 757 | } 758 | 759 | top->u.integer = (top->u.integer * 10) + (b - '0'); 760 | continue; 761 | } 762 | 763 | num_fraction = (num_fraction * 10) + (b - '0'); 764 | continue; 765 | } 766 | 767 | if (b == '+' || b == '-') 768 | { 769 | if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) 770 | { 771 | flags |= flag_num_e_got_sign; 772 | 773 | if (b == '-') 774 | flags |= flag_num_e_negative; 775 | 776 | continue; 777 | } 778 | } 779 | else if (b == '.' && top->type == json_integer) 780 | { 781 | if (!num_digits) 782 | { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col); 783 | goto e_failed; 784 | } 785 | 786 | top->type = json_double; 787 | top->u.dbl = (double) top->u.integer; 788 | 789 | num_digits = 0; 790 | continue; 791 | } 792 | 793 | if (! (flags & flag_num_e)) 794 | { 795 | if (top->type == json_double) 796 | { 797 | if (!num_digits) 798 | { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col); 799 | goto e_failed; 800 | } 801 | 802 | top->u.dbl += ((double) num_fraction) / (pow (10.0, (double) num_digits)); 803 | } 804 | 805 | if (b == 'e' || b == 'E') 806 | { 807 | flags |= flag_num_e; 808 | 809 | if (top->type == json_integer) 810 | { 811 | top->type = json_double; 812 | top->u.dbl = (double) top->u.integer; 813 | } 814 | 815 | num_digits = 0; 816 | flags &= ~ flag_num_zero; 817 | 818 | continue; 819 | } 820 | } 821 | else 822 | { 823 | if (!num_digits) 824 | { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col); 825 | goto e_failed; 826 | } 827 | 828 | top->u.dbl *= pow (10.0, (double) 829 | (flags & flag_num_e_negative ? - num_e : num_e)); 830 | } 831 | 832 | if (flags & flag_num_negative) 833 | { 834 | if (top->type == json_integer) 835 | top->u.integer = - top->u.integer; 836 | else 837 | top->u.dbl = - top->u.dbl; 838 | } 839 | 840 | flags |= flag_next | flag_reproc; 841 | break; 842 | 843 | default: 844 | break; 845 | }; 846 | } 847 | 848 | if (flags & flag_reproc) 849 | { 850 | flags &= ~ flag_reproc; 851 | -- state.ptr; 852 | } 853 | 854 | if (flags & flag_next) 855 | { 856 | flags = (flags & ~ flag_next) | flag_need_comma; 857 | 858 | if (!top->parent) 859 | { 860 | /* root value done */ 861 | 862 | flags |= flag_done; 863 | continue; 864 | } 865 | 866 | if (top->parent->type == json_array) 867 | flags |= flag_seek_value; 868 | 869 | if (!state.first_pass) 870 | { 871 | json_value * parent = top->parent; 872 | 873 | switch (parent->type) 874 | { 875 | case json_object: 876 | 877 | parent->u.object.values 878 | [parent->u.object.length].value = top; 879 | 880 | break; 881 | 882 | case json_array: 883 | 884 | parent->u.array.values 885 | [parent->u.array.length] = top; 886 | 887 | break; 888 | 889 | default: 890 | break; 891 | }; 892 | } 893 | 894 | if ( (++ top->parent->u.array.length) > state.uint_max) 895 | goto e_overflow; 896 | 897 | top = top->parent; 898 | 899 | continue; 900 | } 901 | } 902 | 903 | alloc = root; 904 | } 905 | 906 | return root; 907 | 908 | e_unknown_value: 909 | 910 | sprintf (error, "%d:%d: Unknown value", line_and_col); 911 | goto e_failed; 912 | 913 | e_alloc_failure: 914 | 915 | strcpy (error, "Memory allocation failure"); 916 | goto e_failed; 917 | 918 | e_overflow: 919 | 920 | sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col); 921 | goto e_failed; 922 | 923 | e_failed: 924 | 925 | if (error_buf) 926 | { 927 | if (*error) 928 | strcpy (error_buf, error); 929 | else 930 | strcpy (error_buf, "Unknown error"); 931 | } 932 | 933 | if (state.first_pass) 934 | alloc = root; 935 | 936 | while (alloc) 937 | { 938 | top = alloc->_reserved.next_alloc; 939 | state.settings.mem_free (alloc, state.settings.user_data); 940 | alloc = top; 941 | } 942 | 943 | if (!state.first_pass) 944 | json_value_free_ex (&state.settings, root); 945 | 946 | return 0; 947 | } 948 | 949 | json_value * json_parse (const json_char * json, size_t length) 950 | { 951 | json_settings settings = { 0 }; 952 | return json_parse_ex (&settings, json, length, 0); 953 | } 954 | 955 | void json_value_free_ex (json_settings * settings, json_value * value) 956 | { 957 | json_value * cur_value; 958 | 959 | if (!value) 960 | return; 961 | 962 | value->parent = 0; 963 | 964 | while (value) 965 | { 966 | switch (value->type) 967 | { 968 | case json_array: 969 | 970 | if (!value->u.array.length) 971 | { 972 | settings->mem_free (value->u.array.values, settings->user_data); 973 | break; 974 | } 975 | 976 | value = value->u.array.values [-- value->u.array.length]; 977 | continue; 978 | 979 | case json_object: 980 | 981 | if (!value->u.object.length) 982 | { 983 | settings->mem_free (value->u.object.values, settings->user_data); 984 | break; 985 | } 986 | 987 | value = value->u.object.values [-- value->u.object.length].value; 988 | continue; 989 | 990 | case json_string: 991 | 992 | settings->mem_free (value->u.string.ptr, settings->user_data); 993 | break; 994 | 995 | default: 996 | break; 997 | }; 998 | 999 | cur_value = value; 1000 | value = value->parent; 1001 | settings->mem_free (cur_value, settings->user_data); 1002 | } 1003 | } 1004 | 1005 | void json_value_free (json_value * value) 1006 | { 1007 | json_settings settings = { 0 }; 1008 | settings.mem_free = default_free; 1009 | json_value_free_ex (&settings, value); 1010 | } 1011 | 1012 | -------------------------------------------------------------------------------- /json.h: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 3 | * 4 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSON_H 32 | #define _JSON_H 33 | 34 | #ifndef json_char 35 | #define json_char char 36 | #endif 37 | 38 | #ifndef json_int_t 39 | #ifndef _MSC_VER 40 | #include 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include 48 | 49 | #ifdef __cplusplus 50 | 51 | #include 52 | 53 | extern "C" 54 | { 55 | 56 | #endif 57 | 58 | typedef struct 59 | { 60 | unsigned long max_memory; 61 | int settings; 62 | 63 | /* Custom allocator support (leave null to use malloc/free) 64 | */ 65 | 66 | void * (* mem_alloc) (size_t, int zero, void * user_data); 67 | void (* mem_free) (void *, void * user_data); 68 | 69 | void * user_data; /* will be passed to mem_alloc and mem_free */ 70 | 71 | size_t value_extra; /* how much extra space to allocate for values? */ 72 | 73 | } json_settings; 74 | 75 | #define json_enable_comments 0x01 76 | 77 | typedef enum 78 | { 79 | json_none, 80 | json_object, 81 | json_array, 82 | json_integer, 83 | json_double, 84 | json_string, 85 | json_boolean, 86 | json_null 87 | 88 | } json_type; 89 | 90 | extern const struct _json_value json_value_none; 91 | 92 | typedef struct _json_object_entry 93 | { 94 | json_char * name; 95 | unsigned int name_length; 96 | 97 | struct _json_value * value; 98 | 99 | } json_object_entry; 100 | 101 | typedef struct _json_value 102 | { 103 | struct _json_value * parent; 104 | 105 | json_type type; 106 | 107 | union 108 | { 109 | int boolean; 110 | json_int_t integer; 111 | double dbl; 112 | 113 | struct 114 | { 115 | unsigned int length; 116 | json_char * ptr; /* null terminated */ 117 | 118 | } string; 119 | 120 | struct 121 | { 122 | unsigned int length; 123 | 124 | json_object_entry * values; 125 | 126 | #if defined(__cplusplus) && __cplusplus >= 201103L 127 | decltype(values) begin () const 128 | { return values; 129 | } 130 | decltype(values) end () const 131 | { return values + length; 132 | } 133 | #endif 134 | 135 | } object; 136 | 137 | struct 138 | { 139 | unsigned int length; 140 | struct _json_value ** values; 141 | 142 | #if defined(__cplusplus) && __cplusplus >= 201103L 143 | decltype(values) begin () const 144 | { return values; 145 | } 146 | decltype(values) end () const 147 | { return values + length; 148 | } 149 | #endif 150 | 151 | } array; 152 | 153 | } u; 154 | 155 | union 156 | { 157 | struct _json_value * next_alloc; 158 | void * object_mem; 159 | 160 | } _reserved; 161 | 162 | #ifdef JSON_TRACK_SOURCE 163 | 164 | /* Location of the value in the source JSON 165 | */ 166 | unsigned int line, col; 167 | 168 | #endif 169 | 170 | 171 | /* Some C++ operator sugar */ 172 | 173 | #ifdef __cplusplus 174 | 175 | public: 176 | 177 | inline _json_value () 178 | { memset (this, 0, sizeof (_json_value)); 179 | } 180 | 181 | inline const struct _json_value &operator [] (int index) const 182 | { 183 | if (type != json_array || index < 0 184 | || ((unsigned int) index) >= u.array.length) 185 | { 186 | return json_value_none; 187 | } 188 | 189 | return *u.array.values [index]; 190 | } 191 | 192 | inline const struct _json_value &operator [] (const char * index) const 193 | { 194 | if (type != json_object) 195 | return json_value_none; 196 | 197 | for (unsigned int i = 0; i < u.object.length; ++ i) 198 | if (!strcmp (u.object.values [i].name, index)) 199 | return *u.object.values [i].value; 200 | 201 | return json_value_none; 202 | } 203 | 204 | inline operator const char * () const 205 | { 206 | switch (type) 207 | { 208 | case json_string: 209 | return u.string.ptr; 210 | 211 | default: 212 | return ""; 213 | }; 214 | } 215 | 216 | inline operator json_int_t () const 217 | { 218 | switch (type) 219 | { 220 | case json_integer: 221 | return u.integer; 222 | 223 | case json_double: 224 | return (json_int_t) u.dbl; 225 | 226 | default: 227 | return 0; 228 | }; 229 | } 230 | 231 | inline operator bool () const 232 | { 233 | if (type != json_boolean) 234 | return false; 235 | 236 | return u.boolean != 0; 237 | } 238 | 239 | inline operator double () const 240 | { 241 | switch (type) 242 | { 243 | case json_integer: 244 | return (double) u.integer; 245 | 246 | case json_double: 247 | return u.dbl; 248 | 249 | default: 250 | return 0; 251 | }; 252 | } 253 | 254 | #endif 255 | 256 | } json_value; 257 | 258 | json_value * json_parse (const json_char * json, 259 | size_t length); 260 | 261 | #define json_error_max 128 262 | json_value * json_parse_ex (json_settings * settings, 263 | const json_char * json, 264 | size_t length, 265 | char * error); 266 | 267 | void json_value_free (json_value *); 268 | 269 | 270 | /* Not usually necessary, unless you used a custom mem_alloc and now want to 271 | * use a custom mem_free. 272 | */ 273 | void json_value_free_ex (json_settings * settings, 274 | json_value *); 275 | 276 | 277 | #ifdef __cplusplus 278 | } /* extern "C" */ 279 | #endif 280 | 281 | #endif 282 | 283 | 284 | -------------------------------------------------------------------------------- /parse_config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "dvb2dvb.h" 9 | #include "json.h" 10 | 11 | static int parse_mux_params(struct mux_t *mux, json_value *json) 12 | { 13 | int i; 14 | char* s; 15 | 16 | if (json->type != json_object) { 17 | fprintf(stderr,"[JSON] Invalid mux - not an object\n"); 18 | return -1; 19 | } 20 | 21 | for (i=0;i<(int)json->u.object.length;i++) { 22 | if (!strcmp(json->u.object.values[i].name,"device")) 23 | mux->device = strdup(json->u.object.values[i].value->u.string.ptr); 24 | else if (!strcmp(json->u.object.values[i].name,"frequency_khz")) 25 | mux->dvbmod_params.frequency_khz = json->u.object.values[i].value->u.integer; 26 | else if (!strcmp(json->u.object.values[i].name,"bandwidth_hz")) 27 | mux->dvbmod_params.bandwidth_hz = json->u.object.values[i].value->u.integer; 28 | else if (!strcmp(json->u.object.values[i].name,"transmission_mode")){ 29 | s = json->u.object.values[i].value->u.string.ptr; 30 | if (!strcmp(s,"2K")) mux->dvbmod_params.transmission_mode = TRANSMISSION_MODE_2K; 31 | else if (!strcmp(s,"4K")) mux->dvbmod_params.transmission_mode = TRANSMISSION_MODE_4K; 32 | else if (!strcmp(s,"8K")) mux->dvbmod_params.transmission_mode = TRANSMISSION_MODE_8K; 33 | else { 34 | fprintf(stderr,"Unknown transmission_mode %s\n",s); 35 | return -1; 36 | } 37 | } else if (!strcmp(json->u.object.values[i].name,"constellation")) { 38 | s = json->u.object.values[i].value->u.string.ptr; 39 | if (!strcmp(s,"QPSK")) mux->dvbmod_params.constellation = QPSK; 40 | else if (!strcmp(s,"QAM_16")) mux->dvbmod_params.constellation = QAM_16; 41 | else if (!strcmp(s,"QAM_64")) mux->dvbmod_params.constellation = QAM_64; 42 | else { 43 | fprintf(stderr,"Unknown constellation %s\n",s); 44 | return -1; 45 | } 46 | } else if (!strcmp(json->u.object.values[i].name,"guard_interval")) { 47 | s = json->u.object.values[i].value->u.string.ptr; 48 | if (!strcmp(s,"1/4")) mux->dvbmod_params.guard_interval = GUARD_INTERVAL_1_4; 49 | else if (!strcmp(s,"1/8")) mux->dvbmod_params.guard_interval = GUARD_INTERVAL_1_8; 50 | else if (!strcmp(s,"1/16")) mux->dvbmod_params.guard_interval = GUARD_INTERVAL_1_16; 51 | else if (!strcmp(s,"1/32")) mux->dvbmod_params.guard_interval = GUARD_INTERVAL_1_32; 52 | else { 53 | fprintf(stderr,"Unknown guard_interval %s\n",s); 54 | return -1; 55 | } 56 | } else if (!strcmp(json->u.object.values[i].name,"code_rate_HP")) { 57 | s = json->u.object.values[i].value->u.string.ptr; 58 | if (!strcmp(s,"1/2")) mux->dvbmod_params.code_rate_HP = FEC_1_2; 59 | else if (!strcmp(s,"2/3")) mux->dvbmod_params.code_rate_HP = FEC_2_3; 60 | else if (!strcmp(s,"3/4")) mux->dvbmod_params.code_rate_HP = FEC_3_4; 61 | else if (!strcmp(s,"4/5")) mux->dvbmod_params.code_rate_HP = FEC_4_5; 62 | else if (!strcmp(s,"5/6")) mux->dvbmod_params.code_rate_HP = FEC_5_6; 63 | else if (!strcmp(s,"6/7")) mux->dvbmod_params.code_rate_HP = FEC_6_7; 64 | else if (!strcmp(s,"7/8")) mux->dvbmod_params.code_rate_HP = FEC_7_8; 65 | else if (!strcmp(s,"8/9")) mux->dvbmod_params.code_rate_HP = FEC_8_9; 66 | else { 67 | fprintf(stderr,"Unknown code_rate_HP %s\n",s); 68 | return -1; 69 | } 70 | } else if (!strcmp(json->u.object.values[i].name,"gain")) 71 | mux->gain = json->u.object.values[i].value->u.integer; 72 | else if (!strcmp(json->u.object.values[i].name,"tsid")) 73 | mux->tsid = json->u.object.values[i].value->u.integer; 74 | else if (!strcmp(json->u.object.values[i].name,"onid")) 75 | mux->onid = json->u.object.values[i].value->u.integer; 76 | else if (!strcmp(json->u.object.values[i].name,"nid")) 77 | mux->nid = json->u.object.values[i].value->u.integer; 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | int parse_config(char* filename, struct mux_t **muxes_res) 84 | { 85 | int i,j; 86 | struct mux_t *mux_defaults = NULL; 87 | struct mux_t *mux; 88 | int nmuxes; 89 | int service_id = 60000; // TODO: Make configurable 90 | int lcn = 91; // TODO: Make configurable 91 | 92 | char jsonbuf[65536]; // TODO: Allocate dynamically 93 | 94 | int fd = open(filename,O_RDONLY); 95 | if (fd < 0) { 96 | fprintf(stderr,"Cannot read configuration file\n"); 97 | return -2; 98 | } 99 | int n = read(fd,jsonbuf,sizeof(jsonbuf)); 100 | close(fd); 101 | 102 | json_value *json = json_parse(jsonbuf,n); 103 | 104 | if (json==NULL) { 105 | fprintf(stderr,"Error parsing JSON\n"); 106 | return -3; 107 | } 108 | 109 | if (json->type != json_object) { 110 | fprintf(stderr,"[JSON] Error - expecting top-level object\n"); 111 | return -4; 112 | } 113 | 114 | json_value *common = NULL; 115 | json_value *muxes = NULL; 116 | for (i=0;i<(int)json->u.object.length;i++) { 117 | if (!strcmp(json->u.object.values[i].name,"common")) 118 | common = json->u.object.values[i].value; 119 | else if (!strcmp(json->u.object.values[i].name,"muxes")) 120 | muxes = json->u.object.values[i].value; 121 | } 122 | if (!muxes) { 123 | fprintf(stderr,"[JSON]: ERROR - No muxes defined in config file\n"); 124 | return -5; 125 | } 126 | 127 | if (muxes->type != json_array) { 128 | fprintf(stderr,"[JSON] ERROR: muxes must be an array\n"); 129 | return -6; 130 | } 131 | 132 | nmuxes = muxes->u.array.length; 133 | 134 | if (nmuxes == 0) { 135 | fprintf(stderr,"ERROR: No muxes defined\n"); 136 | return -7; 137 | } 138 | 139 | *muxes_res = calloc(nmuxes, sizeof(struct mux_t)); 140 | mux = *muxes_res; 141 | 142 | /* Process the common parameters */ 143 | if (common) { 144 | mux_defaults = calloc(sizeof(struct mux_t), 1); 145 | 146 | if (parse_mux_params(mux_defaults, common) < 0) { 147 | fprintf(stderr,"Error parsing common section\n"); 148 | return -8; 149 | } 150 | } 151 | 152 | for (j = 0; j < nmuxes; j++) { 153 | /* Set mux to default values, if any */ 154 | if (mux_defaults) 155 | *mux = *mux_defaults; 156 | 157 | json_value* m = muxes->u.array.values[j]; 158 | if (parse_mux_params(mux, m) < 0) { 159 | fprintf(stderr,"Error parsing muxes\n"); 160 | return -9; 161 | } 162 | 163 | json_value *services = NULL; 164 | for (i=0;i<(int)m->u.object.length;i++) { 165 | if (!strcmp(m->u.object.values[i].name,"services")) 166 | services = m->u.object.values[i].value; 167 | } 168 | 169 | if (services == NULL) { 170 | fprintf(stderr,"[JSON] Error - no services in mux\n"); 171 | return -10; 172 | } 173 | 174 | mux->nservices = services->u.array.length; 175 | fprintf(stderr,"Mux %d, found %d services\n",j,mux->nservices); 176 | 177 | mux->services = calloc(mux->nservices,sizeof(struct service_t)); 178 | 179 | for (i=0;inservices;i++) { 180 | json_value *s = services->u.array.values[i]; 181 | if (s->type != json_object) { 182 | fprintf(stderr,"ERROR: Service %d is not a JSON object\n",i); 183 | return -11; 184 | } 185 | int j; 186 | for (j=0;j<(int)s->u.object.length;j++) { 187 | if ((!strcmp(s->u.object.values[j].name,"url")) && (s->u.object.values[j].value->type == json_string)) 188 | mux->services[i].url = strdup(s->u.object.values[j].value->u.string.ptr); 189 | else if ((!strcmp(s->u.object.values[j].name,"service_id")) && (s->u.object.values[j].value->type == json_integer)) 190 | mux->services[i].new_service_id = s->u.object.values[j].value->u.integer; 191 | else if ((!strcmp(s->u.object.values[j].name,"lcn")) && (s->u.object.values[j].value->type == json_integer)) 192 | mux->services[i].lcn = s->u.object.values[j].value->u.integer; 193 | } 194 | 195 | /* Add hbbtv to first service */ 196 | if (i < 0) { 197 | mux->services[i].hbbtv.application_type = 0x0010; 198 | mux->services[i].hbbtv.AIT_version_number = 1; 199 | mux->services[i].hbbtv.url = "http://www.avalpa.com/assets/freesoft/HbbTv/"; 200 | mux->services[i].hbbtv.initial_path = "index.php"; 201 | } 202 | } 203 | 204 | mux++; 205 | } 206 | 207 | if (mux_defaults) 208 | free(mux_defaults); 209 | 210 | return nmuxes; 211 | } 212 | -------------------------------------------------------------------------------- /parse_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARSE_CONFIG_H 2 | #define _PARSE_CONFIG_H 3 | 4 | #include "dvb2dvb.h" 5 | 6 | int parse_config(char* filename, struct mux_t **muxes_res); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /psi_create.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | dvb2dvb - combine multiple SPTS to a MPTS 4 | 5 | Copyright (C) 2014 Dave Chapman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "dvb2dvb.h" 27 | #include "psi_create.h" 28 | #include "ringbuffer.h" 29 | #include "crc32.h" 30 | 31 | static void put_u16be(uint8_t *buf, uint16_t x) 32 | { 33 | buf[0] = (x & 0xff00) >> 8; 34 | buf[1] = x & 0xff; 35 | } 36 | 37 | static void put_u32be(uint8_t *buf, uint32_t x) 38 | { 39 | buf[0] = (x & 0xff000000) >> 24; 40 | buf[1] = (x & 0xff0000) >> 16; 41 | buf[2] = (x & 0xff00) >> 8; 42 | buf[3] = (x & 0xff); 43 | } 44 | 45 | void recode_text(uint8_t* dst, uint8_t *src, int max_size, struct chunk_t* overflow) 46 | { 47 | // TODO: Check existing content, and do not add codepage 48 | // info if it is all ASCII. Also do not add if there is already 49 | // a code page indicator (first character <= 0x1f). 50 | //fprintf(stderr,"Recoding text - first char is 0x%02x\n",src[1]); 51 | int len = *src; 52 | if (len==0) { 53 | *dst = 0; // Nothing to copy. 54 | } else { 55 | int len = *src; 56 | uint8_t *p = dst+1; 57 | *p++ = 0x10; 58 | *p++ = 0x00; 59 | *p++ = 0x01; 60 | if (overflow && (overflow->len)) { 61 | // fprintf(stderr,"Copying overflow to start of output descriptor - len=%d\n",overflow->len); 62 | memcpy(p, &overflow->buf[0], overflow->len); 63 | p += overflow->len; 64 | overflow->len = 0; 65 | } 66 | if (overflow) { 67 | int space_left = max_size - (int)(p - dst - 1); 68 | //fprintf(stderr,"max_size=%d, space_left=%d\n",max_size,space_left); 69 | int to_copy = MIN(space_left, len); 70 | memcpy(p,src+1,to_copy); 71 | p += to_copy; 72 | if (to_copy < len) { 73 | //fprintf(stderr,"Overflow - saving %d bytes\n",len-to_copy); 74 | memcpy(&overflow->buf[0], src + to_copy + 1, len-to_copy); 75 | overflow->len = len-to_copy; 76 | } 77 | } else { 78 | memcpy(p,src+1,len); 79 | p += len; 80 | } 81 | *dst = p-dst-1; 82 | } 83 | } 84 | 85 | int copy_eit_descriptors(uint8_t *dst, uint8_t *src, int len, int onid) 86 | { 87 | int i = 0; 88 | uint8_t *p = dst; 89 | struct chunk_t overflow; 90 | overflow.len = 0; 91 | 92 | while (i < len) { 93 | int descriptor_tag=src[i]; 94 | int descriptor_length=src[i+1]; 95 | 96 | int processed = 0; 97 | if (descriptor_tag == 0x4d) { // short_event_descriptor 98 | if ((onid == 0x0001)|| (onid==0x0085)) { // Astra 19E providers seem to incorrectly use iso-8859-1 by default 99 | *p++ = 0x4d; 100 | uint8_t *p1 = p++; // Save position of descriptor length 101 | memcpy(p,src+i+2,3); // iso_639 language code 102 | p += 3; 103 | uint8_t *q = src+i+5; 104 | // event_name 105 | recode_text(p, q, 255, NULL); 106 | p += *p + 1; q += *q + 1; 107 | // text 108 | recode_text(p, q, 255, NULL); 109 | p += *p + 1; 110 | 111 | *p1 = p-p1-1; 112 | processed = 1; 113 | } 114 | } else if (descriptor_tag == 0x4e) { // extended_event_descriptor 115 | if ((onid == 0x0001)|| (onid==0x0085)) { // Astra 19E providers seem to incorrectly use iso-8859-1 by default 116 | uint8_t *q = src + i; 117 | uint8_t *p1 = p + 1; // Save position of descriptor length 118 | memcpy(p, q, 6); 119 | p += 6; q += 6; 120 | memcpy(p, q, *q + 1); // items 121 | p += *p + 1; q += *q + 1; 122 | // text 123 | 124 | int max_size = 253 - (int)(p-p1-1) - 1; // C+ uses a max descriptor length of 253 - why not 255? 125 | //fprintf(stderr,"Calling recode_text - descriptor_length=%d, *q=%d, p-p1=%d, max_size=%d \n",descriptor_length,*q,(int)(p-p1),max_size); 126 | recode_text(p, q, max_size, &overflow); 127 | //fprintf(stderr,"Recorded - overflow.len=%d\n",overflow.len); 128 | p += *p + 1; 129 | 130 | //fprintf(stderr,"Setting descriptor length = %d \n",(int)(p-p1-1)); 131 | *p1 = p-p1-1; 132 | processed = 1; 133 | } 134 | } 135 | if (!processed) { 136 | memcpy(p, src+i, descriptor_length + 2); 137 | p += descriptor_length + 2; 138 | } 139 | i += descriptor_length + 2; 140 | } 141 | 142 | return p - dst; 143 | } 144 | 145 | int rewrite_eit(struct section_t* new_eit, struct section_t* eit, int old_service_id, int new_service_id, int onid, struct mux_t* mux) 146 | { 147 | uint8_t *buf = &eit->buf[0]; 148 | uint8_t *newbuf = &new_eit->buf[0]; 149 | 150 | int service_id = (buf[3] << 8) | buf[4]; 151 | if (service_id == old_service_id) { 152 | memcpy(newbuf, buf, 14); 153 | put_u16be(newbuf+3, new_service_id); 154 | put_u16be(newbuf+8, mux->tsid); 155 | put_u16be(newbuf+10, mux->onid); 156 | 157 | uint8_t *p = newbuf + 14; 158 | uint8_t *q = buf+14; 159 | //Assume 1 event for now. 160 | // while (i < eit->length) { 161 | memcpy(p, q, 10); 162 | p += 10; q += 10; 163 | int descriptors_loop_length = ((q[0]&0x0f)<<8) | q[1]; 164 | int new_descriptors_loop_length = copy_eit_descriptors(p+2, q+2, descriptors_loop_length, onid); 165 | int running_status = (q[0] & 0xe0) >> 5; 166 | int free_CA_mode = 0; 167 | put_u16be(p, (running_status << 13) | (free_CA_mode << 12) | new_descriptors_loop_length); 168 | p += new_descriptors_loop_length + 2; 169 | // } 170 | 171 | new_eit->length = p - newbuf + 4; 172 | // Back-fill section_lenth 173 | put_u16be(newbuf+1, 0xf000 | (new_eit->length - 3)); 174 | 175 | uint32_t crc = psi_crc32(newbuf, new_eit->length - 4, 0xffffffff); 176 | put_u32be(p, crc); 177 | 178 | return 0; 179 | } else { 180 | return -1; 181 | } 182 | } 183 | 184 | int copy_pmt_descriptors(uint8_t *dst, uint8_t *src, int len) 185 | { 186 | int i = 0; 187 | uint8_t *p = dst; 188 | 189 | while (i < len) { 190 | int descriptor_tag=src[i]; 191 | int descriptor_length=src[i+1]; 192 | 193 | //fprintf(stderr,"descriptor_tag=0x%02x, len=%d\n",descriptor_tag,descriptor_length); 194 | if ((descriptor_tag != 0x09) && // ca descriptor 195 | (descriptor_tag != 0xc0) // private descriptor used by C+ Spain 196 | ) { 197 | memcpy(p, src+i, descriptor_length + 2); 198 | p += descriptor_length + 2; 199 | } 200 | i += descriptor_length + 2; 201 | } 202 | 203 | return p - dst; 204 | } 205 | 206 | int copy_sdt_descriptors(uint8_t *dst, uint8_t *src, int len, int onid) 207 | { 208 | int i = 0; 209 | uint8_t *p = dst; 210 | 211 | while (i < len) { 212 | int descriptor_tag=src[i]; 213 | int descriptor_length=src[i+1]; 214 | 215 | //fprintf(stderr,"descriptor_tag=0x%02x, len=%d\n",descriptor_tag,descriptor_length); 216 | // Only copy the service_descriptor 217 | if (descriptor_tag == 0x48) { 218 | if ((onid == 0x0001)|| (onid==0x0085)) { // Astra 19E providers seem to incorrectly use iso-8859-1 by default 219 | // insert 0x10 0x00 0x01 before any text strings to indicate iso-8859-1 220 | *p++ = 0x48; 221 | uint8_t *p1 = p++; // Save position of descriptor length 222 | *p++ = src[i+2]; // stream_type 223 | uint8_t *q = src+i+3; 224 | // provider_name 225 | recode_text(p, q, 255, NULL); 226 | p += *p + 1; q += *q + 1; 227 | // service_name 228 | recode_text(p, q, 255, NULL); 229 | p += *p + 1; 230 | 231 | *p1 = p-p1-1; 232 | } else { 233 | memcpy(p, src+i, descriptor_length + 2); 234 | p += descriptor_length + 2; 235 | } 236 | } 237 | i += descriptor_length + 2; 238 | } 239 | 240 | return p - dst; 241 | } 242 | 243 | /* Create NIT 244 | 245 | A UK Freeview NIT contains no network descriptors, and the following transport stream descriptors: 246 | 247 | 0x41 - service_list_descriptor (list of service ids and service types 248 | 0x51 - terrstrial_delivery_descriptor 249 | 0x7f - extension_descriptor 250 | DVB-DescriptorTag: 127 (0x7f) [= extension_descriptor] 251 | descriptor_length: 5 (0x05) 252 | descriptor_tag_extension: 9 (0x09) 253 | selector_bytes: 254 | 0000: 47 42 52 f8 GBR. 255 | 0x5f - private_data_specifier_descriptor 256 | DVB-DescriptorTag: 95 (0x5f) [= private_data_specifier_descriptor] 257 | descriptor_length: 4 (0x04) 258 | PrivateDataSpecifier: 9018 (0x0000233a) [= Independent Television Commission] 259 | 0x83 - logical_channel_numbers_descriptor 260 | 0x25 - metadata_pointer_descriptor 261 | MPEG-DescriptorTag: 37 (0x25) [= metadata_pointer_descriptor] 262 | descriptor_length: 33 (0x21) 263 | 0000: 01 01 3f ff 1f 57 40 0d 0c 64 6d 6f 6c 2e 63 6f ..?..W@..dmol.co 264 | 0010: 2e 75 6b 2f 31 0a 64 6d 6f 6c 2e 63 6f 2e 75 6b .uk/1.dmol.co.uk 265 | 0020: 03 266 | */ 267 | void create_nit(struct section_t* nitsec, struct mux_t* mux) 268 | { 269 | struct service_t* services = mux->services; 270 | uint8_t *nit = &nitsec->buf[0]; 271 | uint8_t *p; 272 | int i,j; 273 | 274 | int version_number = 1; 275 | int current_next_indicator = 1; 276 | 277 | memset(nit,0,sizeof(nitsec->buf)); 278 | 279 | nit[0] = 0x40; // table_id 280 | // skip section_length - 2 bytes 281 | put_u16be(nit+3,mux->nid); 282 | nit[5] = 0xc0 | (version_number << 1) | current_next_indicator; 283 | nit[6] = 0x00; // section_number 284 | nit[7] = 0x00; // last_section_number 285 | put_u16be(nit+8,0xf000); // network_descriptors_length=0 286 | // nit+10,11 is transport_stream_loop_length 287 | i = 12; 288 | 289 | // Transport Stream loop - just one entry. 290 | put_u16be(nit+i,mux->tsid); 291 | put_u16be(nit+i+2,mux->onid); 292 | // nit+i+4,5 = transport_descriptors_length 293 | 294 | // Transport descriptors 295 | p = nit+i+6; 296 | 297 | // service_list_descriptor 298 | p[0] = 0x41; 299 | p[1] = mux->nservices * 3; 300 | for (j=0;jnservices;j++) { 301 | put_u16be(p+2+3*j, services[j].new_service_id); 302 | p[2+3*j+2] = services[j].service_type; 303 | } 304 | p += 2+mux->nservices*3; 305 | 306 | // terrestrial_delivery_descriptor 307 | int centre_frequency = 802000 * 100; 308 | int bandwidth = 0; // 8MHz 309 | int priority = 1; 310 | int Time_Slicing_indicator = 1; 311 | int MPE_FEC_indicator = 1; 312 | int reserved1 = 3; 313 | int constellation = 2; // QAM-64 314 | int hierarchy_information = 0; 315 | int code_rate_HP_stream = 1; // 2/3 316 | int code_rate_LP_stream = 0; 317 | int guard_interval = 3; // 1/4 318 | int transmission_mode = 1; // 8k 319 | int other_frequency_flag = 1; 320 | 321 | p[0] = 0x5a; 322 | p[1] = 11; 323 | put_u32be(p+2,centre_frequency); 324 | p[6] = (bandwidth << 5) | (priority << 4) | (Time_Slicing_indicator << 3) | (MPE_FEC_indicator << 2) | reserved1; 325 | p[7] = (constellation << 6) | (hierarchy_information << 3) | code_rate_HP_stream; 326 | p[8] = (code_rate_LP_stream << 5) | (guard_interval << 3) | (transmission_mode << 1) | other_frequency_flag; 327 | p[9] = 0xff; 328 | p[10] = 0xff; 329 | p[11] = 0xff; 330 | p[12] = 0xff; 331 | p += 13; 332 | 333 | // private_data_specifier_description 334 | // I don't know the meaning of this, but it is sent on real Freeview muxes, and my 335 | // TV doesn't use the LCNs without it. 336 | p[0] = 0x5f; 337 | p[1] = 4; 338 | put_u32be(p+2,mux->onid); 339 | p += 6; 340 | 341 | // logical_channel_numbers descriptor 342 | p[0] = 0x83; 343 | p[1] = mux->nservices * 4; 344 | for (j=0;jnservices;j++) { 345 | put_u16be(p+2+4*j, services[j].new_service_id); 346 | put_u16be(p+2+4*j+2, 0xfc00 | services[j].lcn); 347 | } 348 | p += 2+mux->nservices*4; 349 | 350 | // Back-fill transport_stream_loop_length 351 | put_u16be(nit+10, 0xf000 | (p - (nit+i+6) - 4)); 352 | 353 | // Back-fill transport_descriptors_length 354 | put_u16be(nit+i+4, 0xf000 | (p - (nit+i+6))); 355 | 356 | // Back-fill section_lenth 357 | int section_length = (p-nit) - 3 + 4; 358 | put_u16be(nit+1, 0xf000 | section_length); 359 | 360 | uint32_t crc = psi_crc32(nit, (p-nit), 0xffffffff); 361 | 362 | put_u32be(p, crc); 363 | 364 | nitsec->length = p - nit + 4; 365 | } 366 | 367 | 368 | void create_sdt(struct section_t* sdtsec, struct mux_t* mux) 369 | { 370 | struct service_t *services = mux->services; 371 | uint8_t *sdt = &sdtsec->buf[0]; 372 | int i,j,k; 373 | 374 | int version_number = 1; 375 | int current_next_indicator = 1; 376 | 377 | memset(sdt,0,sizeof(sdtsec->buf)); 378 | 379 | sdt[0] = 0x42; // table_id 380 | // skip section_length - 2 bytes 381 | put_u16be(sdt+3,mux->tsid); 382 | sdt[5] = 0xc0 | (version_number << 1) | current_next_indicator; 383 | sdt[6] = 0x00; // section_number 384 | sdt[7] = 0x00; // last_section_number 385 | put_u16be(sdt+8,mux->onid); 386 | sdt[10] = 0xff; // reserved 387 | 388 | i = 11; 389 | for (k=0;knservices;k++) { 390 | uint8_t *buf = &services[k].sdt.buf[0]; 391 | j = 11; 392 | while (j < services[k].sdt.length - 4) { 393 | int service_id = (buf[j] << 8) | buf[j+1]; 394 | //fprintf(stderr,"Processing SDT: i=%d, service=%d\n",i,service_id); 395 | int EIT_schedule_flag = 0; 396 | int EIT_present_following_flag = 1; 397 | int running_status = (buf[j+3]&0xe0) >> 5; 398 | int free_CA_mode = 0; 399 | int descriptors_loop_length=((buf[j+3]&0x0f)<<8) | buf[j+4]; 400 | 401 | //fprintf(stderr,"create_sdt: service_id=%d, services[%d].service_id=%d\n",service_id,k,services[k].service_id); 402 | if (service_id == services[k].service_id) { 403 | put_u16be(sdt+i,services[k].new_service_id); 404 | sdt[i+2] = 0xfc | (EIT_schedule_flag << 1) | EIT_present_following_flag; 405 | 406 | int new_descriptors_loop_length = copy_sdt_descriptors(sdt+i+5,&services[k].sdt.buf[j+5], descriptors_loop_length, services[k].onid); 407 | 408 | put_u16be(sdt+i+3, (running_status << 13) | (free_CA_mode << 12) | new_descriptors_loop_length); 409 | i += 5 + new_descriptors_loop_length; 410 | } 411 | j += 5 + descriptors_loop_length; 412 | } 413 | } 414 | 415 | int section_length = i - 3 + 4; 416 | put_u16be(sdt+1, 0xe000 | section_length); 417 | 418 | uint32_t crc = psi_crc32(sdt, i, 0xffffffff); 419 | 420 | put_u32be(sdt+i, crc); 421 | 422 | sdtsec->length = i + 4; 423 | 424 | // check_sdt(&sv->new_sdt); 425 | } 426 | 427 | void create_pmt(struct service_t* sv) 428 | { 429 | uint8_t *pmt = &sv->new_pmt.buf[0]; 430 | 431 | int version_number = 1; 432 | int current_next_indicator = 1; 433 | 434 | memset(pmt,0,sizeof(sv->new_pmt.buf)); 435 | 436 | pmt[0] = 0x02; // table_id 437 | // skip section_length - 2 bytes 438 | put_u16be(pmt+3,sv->new_service_id); 439 | pmt[5] = 0xc0 | (version_number << 1) | current_next_indicator; 440 | pmt[6] = 0x00; // section_number 441 | pmt[7] = 0x00; // last_section_number 442 | put_u16be(pmt+8,0xe000 | sv->pid_map[sv->pcr_pid]); 443 | 444 | int program_info_length = ((sv->pmt.buf[10] & 0x0f) << 8) | sv->pmt.buf[11]; 445 | put_u16be(pmt+10,0xf000 | program_info_length); // program_info_length 446 | 447 | int i = 12; 448 | if (program_info_length) { 449 | memcpy(pmt + 12, &sv->pmt.buf[12], program_info_length); 450 | i += program_info_length; 451 | } 452 | int j = i; 453 | 454 | while ( j < sv->pmt.length - 4) { 455 | int stream_type = sv->pmt.buf[j]; 456 | int pid = ((sv->pmt.buf[j+1]&0x1f) << 8) | sv->pmt.buf[j+2]; 457 | int ES_info_length = ((sv->pmt.buf[j+3] & 0x0f) << 8) | sv->pmt.buf[j+4]; 458 | 459 | if (sv->pid_map[pid]) { 460 | pmt[i] = stream_type; 461 | put_u16be(pmt+i+1, 0xe000 | sv->pid_map[pid]); 462 | 463 | // TODO: Only copy interesting descriptors 464 | int new_ES_info_length = copy_pmt_descriptors(pmt+i+5,&sv->pmt.buf[j+5], ES_info_length); 465 | put_u16be(pmt+i+3, 0xf000 | new_ES_info_length); 466 | 467 | i += 5 + new_ES_info_length; 468 | } 469 | j += 5 + ES_info_length; 470 | } 471 | 472 | /* hbbtv - insert reference to AIT stream */ 473 | if (sv->ait_pid) { 474 | pmt[i++] = 0x05; 475 | put_u16be(pmt+i, 0xe000 | sv->ait_pid); i += 2; 476 | put_u16be(pmt+i, 0xf000 | 5); i += 2; 477 | pmt[i++] = 0x6f; // application_signalling_descriptor 478 | pmt[i++] = 3; // length 479 | put_u16be(pmt+i, 0x8000 | sv->hbbtv.application_type); i += 2; 480 | pmt[i++] = sv->hbbtv.AIT_version_number; 481 | } 482 | 483 | int section_length = i - 3 + 4; 484 | put_u16be(pmt+1, 0xb000 | section_length); 485 | 486 | uint32_t crc = psi_crc32(pmt, i, 0xffffffff); 487 | 488 | put_u32be(pmt+i, crc); 489 | 490 | sv->new_pmt.length = i + 4; 491 | } 492 | 493 | void create_pat(struct section_t *patsec, struct mux_t* mux) 494 | { 495 | struct service_t *services = mux->services; 496 | uint8_t *pat = &patsec->buf[0]; 497 | int i,j; 498 | 499 | int section_length = 5 + (mux->nservices * 4) + 4; 500 | int version_number = 1; 501 | int current_next_indicator = 1; 502 | 503 | memset(pat,0,sizeof(struct section_t)); 504 | 505 | pat[0] = 0x00; // table_id 506 | put_u16be(pat+1, 0x8000 | section_length); 507 | put_u16be(pat+3, mux->tsid); 508 | pat[5] = 0xc0 | (version_number << 1) | current_next_indicator; 509 | pat[6] = 0x00; // section_number 510 | pat[7] = 0x00; // last_section_number 511 | 512 | i = 8; 513 | for (j=0;jnservices;j++) { 514 | put_u16be(pat+i,services[j].new_service_id); i += 2; 515 | put_u16be(pat+i,0xe000 | services[j].new_pmt_pid); i += 2; 516 | } 517 | 518 | uint32_t crc = psi_crc32(pat, i, 0xffffffff); 519 | 520 | put_u32be(pat+i, crc); 521 | 522 | patsec->length = i + 4; 523 | } 524 | 525 | void create_ait(struct service_t* sv) 526 | { 527 | uint8_t *ait = &sv->ait.buf[0]; 528 | int i,j,k; 529 | int current_next_indicator = 1; 530 | 531 | memset(ait,0,sizeof(sv->ait.buf)); 532 | 533 | ait[0] = 0x74; // table_id 534 | // skip section_length - 2 bytes 535 | put_u16be(ait+3,sv->hbbtv.application_type); 536 | ait[5] = 0xc0 | (sv->hbbtv.AIT_version_number << 1) | current_next_indicator; 537 | ait[6] = 0x00; // section_number 538 | ait[7] = 0x00; // last_section_number 539 | 540 | // Common descriptors (empty) 541 | ait[8] = 0xf0; 542 | ait[9] = 0; 543 | 544 | // Application loop 545 | i = 12; 546 | j = i; 547 | 548 | put_u32be(ait+i, 0x13); i += 4; // organisation_id 549 | put_u16be(ait+i, 0x0001) ; i += 2; // application_id = 1 (unsigned application) 550 | ait[i++] = 0x01; // application_control_code = autostart application 551 | i += 2; // descriptors loop length 552 | k = i; 553 | // application descriptors 554 | 555 | // 1) transport protocol descriptor 556 | int url_len = strlen(sv->hbbtv.url); 557 | ait[i++] = 2; // transport_protocol_descriptor 558 | ait[i++] = url_len + 5; // descriptor_length 559 | put_u16be(ait+i, 0x0003); i += 2; // protocol_id = Transport via HTTP over the interaction channel 560 | ait[i++] = 0; // tranport_protocol_label 561 | ait[i++] = url_len; 562 | memcpy(ait+i, sv->hbbtv.url, url_len); i += url_len; 563 | ait[i++] = 0; 564 | 565 | // 2) application_descriptor 566 | ait[i++] = 0; // application_descriptor 567 | ait[i++] = 9; 568 | ait[i++] = 5; // application_profiles_length 569 | put_u16be(ait+i, 0x0001); i += 2; 570 | ait[i++] = 0x00; 571 | ait[i++] = 0x05; 572 | ait[i++] = 0x00; 573 | ait[i++] = (0 << 7) | (0x3 << 5) | 0x1f; 574 | ait[i++] = 2; // application_priority 575 | ait[i++] = 0; // transport_protocol_label 576 | 577 | // 3) application_name_descriptor 578 | char* application_name = "hbbtv application"; 579 | int name_len = strlen(application_name); 580 | ait[i++] = 0x01; // application_name_descriptor 581 | ait[i++] = name_len + 4; 582 | memcpy(ait+i,"eng",3); i += 3; 583 | ait[i++] = name_len; 584 | memcpy(ait+i,application_name,name_len); i += name_len; 585 | 586 | // 4) simple_application_location_descriptor 587 | int path_len = strlen(sv->hbbtv.initial_path); 588 | ait[i++] = 0x15; // simple_application_location_descriptor 589 | ait[i++] = path_len; 590 | memcpy(ait+i,sv->hbbtv.initial_path,path_len); i += path_len; 591 | 592 | put_u16be(ait+k-2,0xf000 | (i-k)); // descriptors loop length 593 | put_u16be(ait+j-2,0xf000 | (i-j)); // length of application 594 | 595 | int section_length = i - 3 + 4; 596 | put_u16be(ait+1, 0xf000 | section_length); 597 | 598 | uint32_t crc = psi_crc32(ait, i, 0xffffffff); 599 | 600 | put_u32be(ait+i, crc); 601 | 602 | sv->ait.length = i + 4; 603 | } 604 | 605 | int copy_section(uint8_t* tsbuf, struct section_t* section, int pid) 606 | { 607 | int i; 608 | uint8_t *buf = §ion->buf[0]; 609 | int n = section->length; 610 | int bytes_written = 0; 611 | int num_packets = 0; 612 | 613 | while (n > 0) { 614 | tsbuf[0] = 0x47; 615 | if (bytes_written == 0) { 616 | put_u16be(tsbuf+1,0x4000 | pid); 617 | tsbuf[4] = 0; 618 | i = 5; 619 | } else { 620 | put_u16be(tsbuf+1,pid); 621 | i = 4; 622 | } 623 | tsbuf[3] = 0x10 | section->cc; 624 | section->cc = (section->cc + 1) % 16; 625 | 626 | int to_write = MIN(188-i,n); 627 | memcpy(tsbuf+i, buf+bytes_written, to_write); 628 | if (i+to_write < 188) { 629 | memset(tsbuf+i+to_write,0xff,188-(i+to_write)); 630 | } 631 | 632 | tsbuf += 188; 633 | bytes_written += to_write; 634 | n -= to_write; 635 | num_packets++; 636 | } 637 | return num_packets; 638 | } 639 | 640 | int write_section(struct ringbuffer_t* rb, struct section_t* section, int pid) 641 | { 642 | int i; 643 | uint8_t *buf = §ion->buf[0]; 644 | uint8_t tsbuf[188]; 645 | int n = section->length; 646 | int bytes_written = 0; 647 | int num_packets = 0; 648 | 649 | tsbuf[0] = 0x47; 650 | 651 | while (n > 0) { 652 | if (bytes_written == 0) { 653 | put_u16be(tsbuf+1,0x4000 | pid); 654 | tsbuf[4] = 0; 655 | i = 5; 656 | } else { 657 | put_u16be(tsbuf+1,pid); 658 | i = 4; 659 | } 660 | tsbuf[3] = 0x10 | section->cc; 661 | section->cc = (section->cc + 1) % 16; 662 | 663 | int to_write = MIN(188-i,n); 664 | memcpy(tsbuf+i, buf+bytes_written, to_write); 665 | if (i+to_write < 188) { 666 | memset(tsbuf+i+to_write,0xff,188-(i+to_write)); 667 | } 668 | 669 | rb_write(rb, tsbuf, 188); 670 | bytes_written += to_write; 671 | n -= to_write; 672 | num_packets++; 673 | } 674 | return num_packets; 675 | } 676 | 677 | -------------------------------------------------------------------------------- /psi_create.h: -------------------------------------------------------------------------------- 1 | #ifndef _PSI_CREATE_H 2 | #define _PSI_CREATE_H 3 | 4 | #include 5 | #include "dvb2dvb.h" 6 | #include "ringbuffer.h" 7 | 8 | struct chunk_t { 9 | int len; 10 | char buf[128]; 11 | }; 12 | 13 | void recode_text(uint8_t* dst, uint8_t *src, int max_size, struct chunk_t* overflow); 14 | int copy_eit_descriptors(uint8_t *dst, uint8_t *src, int len, int onid); 15 | int rewrite_eit(struct section_t* new_eit, struct section_t* eit, int old_service_id, int new_service_id, int onid, struct mux_t* mux); 16 | int copy_pmt_descriptors(uint8_t *dst, uint8_t *src, int len); 17 | int copy_sdt_descriptors(uint8_t *dst, uint8_t *src, int len, int onid); 18 | void create_nit(struct section_t* nitsec, struct mux_t* mux); 19 | void create_sdt(struct section_t* sdtsec, struct mux_t* mux); 20 | void create_pmt(struct service_t* sv); 21 | void create_ait(struct service_t* sv); 22 | void create_pat(struct section_t *patsec, struct mux_t* mux); 23 | int copy_section(uint8_t* tsbuf, struct section_t* section, int pid); 24 | int write_section(struct ringbuffer_t* rb, struct section_t* section, int pid); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /psi_read.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | dvb2dvb - combine multiple SPTS to a MPTS 4 | 5 | Copyright (C) 2014 Dave Chapman 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "dvb2dvb.h" 27 | #include "psi_create.h" 28 | #include "crc32.h" 29 | 30 | static char* RST[] = { 31 | "Undefined", 32 | "Not running", 33 | "Starts in a few seconds", 34 | "Pausing", 35 | "Running", 36 | "Reserved", 37 | "Reserved", 38 | "Reserved" 39 | }; 40 | 41 | static char pts_text[30]; 42 | char* pts2hmsu(uint64_t pts,char sep) { 43 | int h,m,s,u; 44 | 45 | pts/=90*300; // Convert to milliseconds 46 | h=(pts/(1000*60*60)); 47 | m=(pts/(1000*60))-(h*60); 48 | s=(pts/1000)-(h*3600)-(m*60); 49 | u=pts-(h*1000*60*60)-(m*1000*60)-(s*1000); 50 | 51 | sprintf(pts_text,"%d:%02d:%02d%c%03d",h,m,s,sep,u); 52 | return(pts_text); 53 | } 54 | 55 | int process_pat(struct service_t* sv, uint8_t* buf) 56 | { 57 | int i = 5; 58 | 59 | if (buf[4] != 0) { 60 | fprintf(stderr,"Unsupported PAT format - pointer_to_data != 0 (%d)\n",buf[4]); 61 | return -1;; 62 | } 63 | 64 | int table_id = buf[i++]; 65 | int section_length = ((buf[i]&0x0f) << 8) | buf[i+1]; i += 2; 66 | if (section_length > 180) { 67 | fprintf(stderr,"Multi-packet PAT not supported\n"); 68 | return -1; 69 | } 70 | 71 | int transport_stream_id = (buf[i] << 8) | buf[i+1]; i += 2; 72 | int version_number = (buf[i]&0x3e) >> 1; 73 | int current_next_indicator = buf[i++] & 0x01; 74 | int section_number = buf[i++]; 75 | int last_section_number = buf[i++]; 76 | if (last_section_number != 0) { 77 | fprintf(stderr,"Multi-packet PAT not supported (last_section_number = %d)\n",last_section_number); 78 | return -1; 79 | } 80 | 81 | //fprintf(stderr,"table_id=%d\nsection_length=%d\ntransport_stream_id=0x%04x\nversion_number=%d\ncurrent_next_indicator=%d\nsection_number=%d\nlast_section_number=%d\n",table_id,section_length,transport_stream_id,version_number,current_next_indicator,section_number,last_section_number); 82 | int nprograms = (section_length - 9) / 4; 83 | if ((nprograms * 4 + 9) != section_length) { 84 | fprintf(stderr,"Unexpected section_length in PAT - %d is not equal to 9 + nprograms * 4\n",section_length); 85 | return -1; 86 | } 87 | 88 | int j; 89 | for (j = 0 ; j < nprograms; j++) { 90 | sv->service_id = (buf[i+j*4] << 8) | buf[i+j*4+1]; 91 | sv->pmt_pid = ((buf[i+j*4+2]&0x1f) << 8) | buf[i+j*4+3]; 92 | //printf("Program %d PMT PID %d\n",sv->service_id,sv->pmt_pid); 93 | } 94 | 95 | //fprintf(stderr,"Processed PAT\n"); 96 | return 0; 97 | } 98 | 99 | int process_sdt(struct service_t* sv) 100 | { 101 | uint8_t *buf = &sv->sdt.buf[0]; 102 | int length = sv->sdt.length; 103 | int i = 3; 104 | 105 | sv->tsid = (buf[i] << 8) | buf[i+1]; i += 2; 106 | int version_number = (buf[i]&0x3e) >> 1; 107 | int current_next_indicator = buf[i++] & 0x01; 108 | int section_number = buf[i++]; 109 | int last_section_number = buf[i++]; 110 | sv->onid = (buf[i] << 8) | buf[i+1]; i += 2; 111 | i++; // Reserved 112 | 113 | fprintf(stderr,"Processing SDT section %d, last_section=%d- tsid=0x%04x, onid=0x%04x:\n",section_number,last_section_number,sv->tsid,sv->onid); 114 | while (i < length - 4) { 115 | int service_id = (buf[i] << 8) | buf[i+1]; i += 2; 116 | //fprintf(stderr,"Processing SDT: i=%d, service=%d\n",i,service_id); 117 | int EIT_schedule_flag = (buf[i] & 0x2) >> 1; 118 | int EIT_present_following_flag = buf[i] & 0x1; 119 | i++; 120 | int running_status = (buf[i]&0xe0) >> 5; 121 | int free_CA_mode = (buf[i]&0x10) >> 4; 122 | int descriptors_loop_length=((buf[i]&0x0f)<<8) | buf[i+1]; i+= 2; 123 | 124 | int j = i; 125 | //fprintf(stderr,"SDT: service_id %d, running_status: %s, EIT_schedule_flag=%d,EIT_present_following_flag=%d,free_CA_mode=%d\n",service_id,RST[running_status],EIT_schedule_flag,EIT_present_following_flag,free_CA_mode); 126 | if (i + descriptors_loop_length > length) { 127 | fprintf(stderr,"ERROR in SDT: i=%d, j=%d, section_length=%d, descriptors_loop_length=%d\n",i,j,length,descriptors_loop_length); 128 | exit(1); 129 | } 130 | i+= descriptors_loop_length; 131 | 132 | //int s = services[service_id]; 133 | while (j < i) { 134 | int descriptor_tag=buf[j++]; 135 | int descriptor_length=buf[j++]; 136 | int k = j; 137 | j += descriptor_length; 138 | 139 | if (descriptor_tag == 0x48) { 140 | int service_type=buf[k++]; 141 | int provider_name_length=buf[k++]; 142 | k += provider_name_length; 143 | int service_name_length = buf[k++]; 144 | char tmp[128]; 145 | memcpy(tmp,buf + k, service_name_length); 146 | tmp[service_name_length] = 0; 147 | if (service_id == sv->service_id) { 148 | sv->name = strdup(tmp); 149 | sv->service_type = service_type; 150 | } 151 | //fprintf(stderr,"Service ID %d, Service type %d, name=\"%s\", running_status=%s\n",service_id,service_type,tmp,RST[running_status]); 152 | } 153 | } 154 | } 155 | if (sv->name == NULL) { 156 | // Not found, keep searching for other sections. 157 | sv->sdt.length = 0; 158 | } 159 | return 0; 160 | } 161 | 162 | int bcd2dec(unsigned char buf) 163 | { 164 | return (((buf&0xf0) >> 4) * 10) + (buf & 0x0f); 165 | } 166 | 167 | int process_pmt(struct service_t* sv) 168 | { 169 | uint8_t *buf = &sv->pmt.buf[0]; 170 | int length = sv->pmt.length; 171 | int i = 3; 172 | 173 | int program_id = (buf[i] << 8) | buf[i+1]; i += 2; 174 | int version_number = (buf[i]&0x3e) >> 1; 175 | int current_next_indicator = buf[i] & 0x01; i++; 176 | int section_number = buf[i++]; 177 | int last_section_number = buf[i++]; 178 | sv->pcr_pid = ((buf[i]&0x1f)<<8) | buf[i+1]; i+=2; 179 | int program_info_length = ((buf[i]&0x0f)<<8) | buf[i+1]; i+=2; 180 | //fprintf(stderr,"PMT program_info_length=%d\n",program_info_length); 181 | i += program_info_length; 182 | 183 | //fprintf(stderr,"PMT: program_id=%d, version=%d, current_next_indicator=%d, section_number=%d, last_section_number=%d\n",program_id,version_number,current_next_indicator,section_number,last_section_number); 184 | //fprintf(stderr,"pcr_pid=%d\n",sv->pcr_pid); 185 | 186 | int new_pid = sv->new_pmt_pid+1; 187 | while ( i < length - 4) { 188 | int stream_type = buf[i++]; 189 | int pid = ((buf[i]&0x1f) << 8) | buf[i+1]; i += 2; 190 | int ES_info_length = ((buf[i] & 0x0f) << 8) | buf[i+1]; i += 2; 191 | i += ES_info_length; 192 | 193 | /* The following list is taken from tvheadend. These are the only stream types 194 | tvheadend includes in its output for a service */ 195 | switch (stream_type) { 196 | case 0x01: // SCT_MPEG2VIDEO; 197 | case 0x02: // SCT_MPEG2VIDEO; 198 | case 0x80: // 0x80 is DigiCipher II (North American cable) encrypted MPEG-2 199 | case 0x03: // SCT_MPEG2AUDIO; 200 | case 0x04: // SCT_MPEG2AUDIO; 201 | case 0x06: // SCT_AC3 or SCT_TELETEXT 202 | case 0x81: // SCT_AC3 or SCT_TELETEXT 203 | case 0x0f: // SCT_MP4A; 204 | case 0x11: // SCT_AAC; 205 | case 0x1b: // SCT_H264; 206 | case 0x24: // SCT_HEVC; 207 | sv->pid_map[pid] = new_pid++; 208 | break; 209 | default: 210 | break; 211 | } 212 | 213 | //fprintf(stderr,"[INFO] PID %d - stream_type 0x%02x mapped to PID %d\n",pid,stream_type,sv->pid_map[pid]); 214 | } 215 | 216 | // If the PCR PID hasn't already been mapped, then map it to the next new PID 217 | if (!sv->pid_map[sv->pcr_pid]) { 218 | sv->pid_map[sv->pcr_pid] = new_pid++; 219 | } 220 | 221 | if (sv->hbbtv.url) { 222 | sv->ait_pid = new_pid; 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | void process_section(struct section_t* next, struct section_t* curr, uint8_t* buf, int table_id) 229 | { 230 | int i,n; 231 | 232 | int payload_unit_start_indicator = (buf[1]&0x40)>>6; 233 | 234 | if (next->length == 0) { // No packets read 235 | if (payload_unit_start_indicator) { 236 | i = 5 + buf[4]; 237 | if (buf[i]==table_id) { 238 | next->length = 3 + (((buf[i+1]&0x0f) << 8) | buf[i+2]); 239 | int to_copy = MIN(188-i,next->length); 240 | memcpy(&next->buf[0],buf+i,to_copy); 241 | next->bytes_read = to_copy; 242 | } else { 243 | //fprintf(stderr,"Skipping table_id 0x%02x\n",buf[i]); 244 | return; 245 | } 246 | } else { 247 | return; 248 | } 249 | } else { 250 | i = 4; 251 | if (payload_unit_start_indicator) { 252 | fprintf(stderr,"PUSI set within section - not handled!\n"); 253 | i++; 254 | } 255 | int to_copy = MIN(188-i,next->length-next->bytes_read); 256 | memcpy(&next->buf[0]+next->bytes_read,buf+i,to_copy); 257 | next->bytes_read += to_copy; 258 | } 259 | 260 | if (next->bytes_read == next->length) { 261 | /* We have a complete section */ 262 | if (psi_crc32(&next->buf[0],next->length,0xffffffff)==0) { 263 | memcpy(curr, next, sizeof(struct section_t)); 264 | } else { 265 | fprintf(stderr,"CRC error, skipping section - table_id=0x%02x, length=%d\n",next->buf[0],next->length); 266 | } 267 | 268 | memset(next, 0, sizeof(struct section_t)); 269 | } 270 | 271 | return; 272 | } 273 | 274 | -------------------------------------------------------------------------------- /psi_read.h: -------------------------------------------------------------------------------- 1 | #ifndef _PSI_READ_H 2 | #define _PSI_READ_H 3 | 4 | #include 5 | #include "dvb2dvb.h" 6 | 7 | int process_pat(struct service_t* sv, uint8_t* buf); 8 | int process_sdt(struct service_t* sv); 9 | int bcd2dec(unsigned char buf); 10 | char* pts2hmsu(uint64_t pts,char sep); 11 | int process_pmt(struct service_t* sv); 12 | void process_section(struct section_t* next, struct section_t* curr, uint8_t* buf, int table_id); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /ringbuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "dvb2dvb.h" 6 | #include "ringbuffer.h" 7 | 8 | /* A simple mutex-free ringbuffer. 9 | 10 | The read thread only modifies the head pointer. 11 | The write thread only modifies the tail pointer. 12 | 13 | One byte is always left empty to distinguish between an empty and 14 | full buffer. 15 | 16 | rb->head - the next byte to read 17 | rb->tail - the next free location to write 18 | 19 | */ 20 | 21 | int rb_init(struct ringbuffer_t *rb) 22 | { 23 | rb->head = rb->buf; 24 | rb->tail = rb->buf; 25 | return 0; 26 | } 27 | 28 | int rb_get_bytes_used(struct ringbuffer_t* rb) 29 | { 30 | return((rb->tail - rb->head) % sizeof(rb->buf)); 31 | } 32 | 33 | int rb_read(struct ringbuffer_t *rb, uint8_t* buf, int count) 34 | { 35 | // fprintf(stderr,"rb_read - entering loop\n"); 36 | int bytes_used = rb_get_bytes_used(rb); 37 | 38 | //fprintf(stderr,"rb_read(): count=%d, bytes_used=%d\n",count,bytes_used); 39 | while (count >= bytes_used) { 40 | usleep(10); 41 | bytes_used = rb_get_bytes_used(rb); 42 | // fprintf(stderr,"rb_read(): count=%d, bytes_used=%d\n",count,bytes_used); 43 | } 44 | //fprintf(stderr,"rb_read - leaving loop\n"); 45 | 46 | // fprintf(stderr,"rb->head=%p, count=%d, rb->buf=%p, sizeof(rb->buf)=%d\n",rb->head,count,rb->buf,(int)sizeof(rb->buf)); 47 | if (rb->head + count >= rb->buf + sizeof(rb->buf)) { 48 | /* Two-part copy */ 49 | int n1 = sizeof(rb->buf) - (rb->head - rb->buf); 50 | memcpy(buf,rb->head,n1); 51 | memcpy(buf+n1,rb->buf,count-n1); 52 | rb->head = rb->buf + count - n1; 53 | } else { 54 | /* Single copy */ 55 | memcpy(buf,rb->head,count); 56 | rb->head += count; 57 | } 58 | 59 | return count; 60 | } 61 | 62 | /* skip count bytes in the buffer (i.e. read and discard) 63 | can only be called by the read thread. 64 | caller is responsible for ensuring there are enough bytes in the buffer to skip. 65 | */ 66 | int rb_skip(struct ringbuffer_t *rb, int count) 67 | { 68 | if (rb->head + count >= rb->buf + sizeof(rb->buf)) { 69 | /* Two-part copy */ 70 | int n1 = sizeof(rb->buf) - (rb->head - rb->buf); 71 | rb->head = rb->buf + count - n1; 72 | } else { 73 | /* Single copy */ 74 | rb->head += count; 75 | } 76 | 77 | return count; 78 | } 79 | 80 | int rb_write(struct ringbuffer_t *rb, uint8_t* buf, int count) 81 | { 82 | int to_copy; 83 | 84 | int bytes_used = (rb->tail - rb->head) % sizeof(rb->buf); 85 | //fprintf(stderr,"bytes_used = %d\n",bytes_used); 86 | 87 | if (bytes_used + count >= (int)sizeof(rb->buf)) { 88 | to_copy = sizeof(rb->buf) - bytes_used - 1; 89 | } else { 90 | to_copy = count; 91 | } 92 | //fprintf(stderr,"to_copy=%d, requested=%d\n",to_copy,count); 93 | if (to_copy) { 94 | //fprintf(stderr,"rb->tail=%p, to_copy=%d, rb->buf=%p, sizeof(rb->buf)=%d\n",rb->tail,to_copy,rb->buf,(int)sizeof(rb->buf)); 95 | if (rb->tail + to_copy >= rb->buf + sizeof(rb->buf)) { 96 | /* Two-part copy */ 97 | int n1 = sizeof(rb->buf) - (rb->tail - rb->buf); 98 | memcpy(rb->tail,buf,n1); 99 | memcpy(rb->buf,buf+n1,to_copy-n1); 100 | rb->tail = rb->buf + to_copy - n1; 101 | } else { 102 | /* Single copy */ 103 | memcpy(rb->tail,buf,to_copy); 104 | rb->tail += to_copy; 105 | } 106 | } 107 | 108 | return to_copy; 109 | } 110 | -------------------------------------------------------------------------------- /ringbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _RINGBUFFER_H 2 | #define _RINGBUFFER_H 3 | 4 | struct ringbuffer_t { 5 | uint8_t* head; 6 | uint8_t* tail; 7 | uint8_t buf[15*1024*1024]; 8 | }; 9 | 10 | int rb_init(struct ringbuffer_t *rb); 11 | int rb_write(struct ringbuffer_t *rb, uint8_t* buf, int count); 12 | int rb_read(struct ringbuffer_t *rb, uint8_t* buf, int count); 13 | int rb_skip(struct ringbuffer_t *rb, int count); 14 | int rb_get_bytes_used(struct ringbuffer_t* rb); 15 | 16 | #endif 17 | --------------------------------------------------------------------------------