├── 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 |
--------------------------------------------------------------------------------