├── LICENSE ├── README.md ├── bmd_h264_cat.sln ├── bmd_h264_cat.vcxproj └── src ├── BMDStreamingEncodingFrameRate.h ├── BMDStreamingH264EntropyCoding.h ├── BMDStreamingH264Level.h ├── BMDStreamingH264Profile.h ├── const_str.h └── main.cpp /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bmd_h264_cat 2 | Blackmagic Design H.264 Pro Recorder console utility 3 | 4 | ## drivers 5 | Latest working driver is **Desktop Video 10.11.4 (media express 3.5.7)**, see https://www.blackmagicdesign.com/support/download/ceb304a75100442e9e763d6b371690d0/Windows 6 | 7 | ## binaries 8 | * http://research.m1stereo.tv/bmd_h264_cat/2021-05-17/bmd_h264_cat.exe.win32.7z 9 | * http://research.m1stereo.tv/bmd_h264_cat/2021-05-17/bmd_h264_cat.exe.x64.7z 10 | 11 | 12 | ## feature 13 | * receiving MPEG-TS stream from Blackmagic Design H.264 Pro Recorder 14 | * save to file 15 | * send to stdout 16 | * UDP multicast/unicast 17 | * TCP stream saving 18 | 19 | ## usage 20 |
21 | Usage:
22 |     bmd_h264_cat.exe <args> [- | filename]
23 | Where args are:
24 |     -ab <INT>          audio bitrate in kbs
25 |     -vb <INT>          video bitrate in kbs
26 |     -ar <INT>          audio samplerate
27 |     -ac <INT>          audio channels
28 |     -framerate <STR>   framerate, see list below
29 |     -profile <STR>     h264 encoding profile to use, see list below
30 |     -entropy <STR>     h264 encoding entropy to use, see list below
31 |     -level <STR>       h264 encoding level to use, see list below
32 |     -preset <STR>      hardware encoder preset name to use, see list in logs
33 |     -src-x <INT>       source rectangle
34 |     -src-y <INT>
35 |     -src-width <INT>
36 |     -src-height <INT>
37 |     -dst-width <INT>   destination width
38 |     -dst-height <INT>  destination height
39 |     -savefile          save files timestamped
40 |     -udp-host <STR>    host where sent UDP packet
41 |     -udp-port <INT>    port where sent UDP packet
42 |     -udp-sndbuf <INT>  SO_SNDBUF of UDP socket
43 |     -tcp-host <STR>    host where sent TCP packet
44 |     -tcp-port <INT>    port where sent TCP packet
45 |     -tcp-sndbuf <INT>  SO_SNDBUF of TCP socket
46 | Framerates: [50i] [5994i] [60i] [2398p] [24p] [25p] [2997p] [30p] [50p] [5994p] [60p]
47 | Entropyies: [CAVLC] [CABAC]
48 | Levels: [12] [13] [2] [21] [22] [3] [31] [32] [4] [41] [42]
49 | Profiles: [High] [Main] [Baseline]
50 | 
51 | 52 | ## examples 53 | 54 | ### sending UDP unicast stream 55 |
bmd_h264_cat.exe -savefile -preset "iPad / iPhone 4" -udp-host 10.1.5.65 -udp-port 40001
56 | 57 | ### sending UDP multicast stream 58 |
bmd_h264_cat.exe -savefile -preset "iPad / iPhone 4" -udp-host 224.1.1.1 -udp-port 40001
59 | 60 | ### sending TCP stream 61 |
bmd_h264_cat.exe -savefile -preset "iPad / iPhone 4" -tcp-host 10.1.5.57 -tcp-port 20001
62 | 63 | it could be received by **socat** and transformed to multicast: 64 |
socat -b1316 TCP-LISTEN:20001,reuseaddr,fork UDP-DATAGRAM:224.1.1.1:30001,ttl=10
65 | 66 | further step could be implemented with **udpxy**: 67 |
/usr/local/src/2015-11-15/udpxy-1.0.23-9/udpxy -p 10000
68 | 69 | stream could be checked with VLC by networks links: 70 | * udp://@224.1.1.1:30001 71 | * http://10.1.5.57:10000/udp/224.1.1.1:30001/demo.ts 72 | 73 | ### rtmp publishing 74 | 75 |
76 | bmd_h264_cat.exe -savefile -preset "YouTube 720p" - | c:\ffmpeg\bin\ffmpeg_flv_aac_seq_header.exe -f mpegts -i - -acodec copy -vcodec copy -flvflags aac_seq_header_detect -bsf:a aac_adtstoasc -f flv rtmp://a.rtmp.youtube.com/live2/foo-bar-key
77 | 
78 | 79 | please note, that in this case custom version of ffmpeg used, because of http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2015-November/183483.html 80 | 81 | if you have no this version or patch not applied then you have to re-encode audio only. 82 | 83 | ## -savefile 84 | is argument that could save receivied stream into auto-timestamp-named file - that could helps to avoid loosing some files 85 | 86 | ## limitation 87 | not all arguments are really working. API support it, but firmware does not. bitrate, preset, profile parameters works 88 | -------------------------------------------------------------------------------- /bmd_h264_cat.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bmd_h264_cat", "bmd_h264_cat.vcxproj", "{2E94FF7C-DD31-48CD-A49D-34C3051C063D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Debug|x64.ActiveCfg = Debug|x64 17 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Debug|x64.Build.0 = Debug|x64 18 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Debug|x86.ActiveCfg = Debug|Win32 19 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Debug|x86.Build.0 = Debug|Win32 20 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Release|x64.ActiveCfg = Release|x64 21 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Release|x64.Build.0 = Release|x64 22 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Release|x86.ActiveCfg = Release|Win32 23 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /bmd_h264_cat.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {2E94FF7C-DD31-48CD-A49D-34C3051C063D} 23 | Win32Proj 24 | bmd_h264_cat 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | C:\sdk\decklink\Blackmagic DeckLink SDK 10.4.1\Win\include\;$(IncludePath) 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | C:\sdk\decklink\Blackmagic DeckLink SDK 10.4.1\Win\include\;$(IncludePath) 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | Disabled 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | 107 | 108 | Console 109 | true 110 | 111 | 112 | 113 | 114 | Level3 115 | 116 | 117 | MaxSpeed 118 | true 119 | true 120 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 121 | 122 | 123 | Console 124 | true 125 | true 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | 133 | 134 | MaxSpeed 135 | true 136 | true 137 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | 139 | 140 | Console 141 | true 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /src/BMDStreamingEncodingFrameRate.h: -------------------------------------------------------------------------------- 1 | #ifndef BMDStreamingEncodingFrameRate_h 2 | #define BMDStreamingEncodingFrameRate_h 3 | 4 | #include "const_str.h" 5 | #include "../DeckLinkAPI_h.h" 6 | 7 | #define _CRT_SECURE_NO_WARNINGS 8 | 9 | static const void* BMDStreamingEncodingFrameRate_pairs[] = 10 | { 11 | (void*)bmdStreamingEncodedFrameRate50i, "50i", 12 | (void*)bmdStreamingEncodedFrameRate5994i, "5994i", 13 | (void*)bmdStreamingEncodedFrameRate60i, "60i", 14 | (void*)bmdStreamingEncodedFrameRate2398p, "2398p", 15 | (void*)bmdStreamingEncodedFrameRate24p, "24p", 16 | (void*)bmdStreamingEncodedFrameRate25p, "25p", 17 | (void*)bmdStreamingEncodedFrameRate2997p, "2997p", 18 | (void*)bmdStreamingEncodedFrameRate30p, "30p", 19 | (void*)bmdStreamingEncodedFrameRate50p, "50p", 20 | (void*)bmdStreamingEncodedFrameRate5994p, "5994p", 21 | (void*)bmdStreamingEncodedFrameRate60p, "60p", 22 | NULL, NULL 23 | }; 24 | 25 | #pragma warning(push) 26 | #pragma warning(disable: 4311) 27 | #pragma warning(disable: 4302) 28 | CONST_FROM_CHAR(BMDStreamingEncodingFrameRate, BMDStreamingEncodingFrameRate_pairs); 29 | CONST_TO_CHAR(BMDStreamingEncodingFrameRate, BMDStreamingEncodingFrameRate_pairs); 30 | #pragma warning(pop) 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/BMDStreamingH264EntropyCoding.h: -------------------------------------------------------------------------------- 1 | #ifndef BMDStreamingH264EntropyCoding_h 2 | #define BMDStreamingH264EntropyCoding_h 3 | 4 | #include "const_str.h" 5 | #include "../DeckLinkAPI_h.h" 6 | 7 | static const void* BMDStreamingH264EntropyCoding_pairs[] = 8 | { 9 | (void*)bmdStreamingH264EntropyCodingCAVLC, "CAVLC", 10 | (void*)bmdStreamingH264EntropyCodingCABAC, "CABAC", 11 | NULL, NULL 12 | }; 13 | 14 | #pragma warning(push) 15 | #pragma warning(disable: 4311) 16 | #pragma warning(disable: 4302) 17 | CONST_FROM_CHAR(BMDStreamingH264EntropyCoding, BMDStreamingH264EntropyCoding_pairs); 18 | CONST_TO_CHAR(BMDStreamingH264EntropyCoding, BMDStreamingH264EntropyCoding_pairs); 19 | #pragma warning(pop) 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/BMDStreamingH264Level.h: -------------------------------------------------------------------------------- 1 | #ifndef BMDStreamingH264Level_h 2 | #define BMDStreamingH264Level_h 3 | 4 | #include "const_str.h" 5 | #include "../DeckLinkAPI_h.h" 6 | 7 | static const void* BMDStreamingH264Level_pairs[] = 8 | { 9 | (void*)bmdStreamingH264Level12, "12", 10 | (void*)bmdStreamingH264Level13, "13", 11 | (void*)bmdStreamingH264Level2, "2", 12 | (void*)bmdStreamingH264Level21, "21", 13 | (void*)bmdStreamingH264Level22, "22", 14 | (void*)bmdStreamingH264Level3, "3", 15 | (void*)bmdStreamingH264Level31, "31", 16 | (void*)bmdStreamingH264Level32, "32", 17 | (void*)bmdStreamingH264Level4, "4", 18 | (void*)bmdStreamingH264Level41, "41", 19 | (void*)bmdStreamingH264Level42, "42", 20 | NULL, NULL 21 | }; 22 | 23 | #pragma warning(push) 24 | #pragma warning(disable: 4311) 25 | #pragma warning(disable: 4302) 26 | CONST_FROM_CHAR(BMDStreamingH264Level, BMDStreamingH264Level_pairs); 27 | CONST_TO_CHAR(BMDStreamingH264Level, BMDStreamingH264Level_pairs); 28 | #pragma warning(pop) 29 | 30 | #endif /* BMDStreamingH264Level_h */ 31 | -------------------------------------------------------------------------------- /src/BMDStreamingH264Profile.h: -------------------------------------------------------------------------------- 1 | #ifndef BMDStreamingH264Profile_h 2 | #define BMDStreamingH264Profile_h 3 | 4 | #include "const_str.h" 5 | #include "../DeckLinkAPI_h.h" 6 | 7 | static const void* BMDStreamingH264Profile_pairs[] = 8 | { 9 | (void*)bmdStreamingH264ProfileHigh, "High", 10 | (void*)bmdStreamingH264ProfileMain, "Main", 11 | (void*)bmdStreamingH264ProfileBaseline, "Baseline", 12 | NULL, NULL 13 | }; 14 | 15 | #pragma warning(push) 16 | #pragma warning(disable: 4311) 17 | #pragma warning(disable: 4302) 18 | CONST_FROM_CHAR(BMDStreamingH264Profile, BMDStreamingH264Profile_pairs); 19 | CONST_TO_CHAR(BMDStreamingH264Profile, BMDStreamingH264Profile_pairs); 20 | #pragma warning(pop) 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/const_str.h: -------------------------------------------------------------------------------- 1 | #ifndef CONST_STR_H 2 | #define CONST_STR_H 3 | 4 | #define _CRT_SECURE_NO_WARNINGS 5 | 6 | #define CONST_FROM_CHAR(T, LIST) \ 7 | static const T T##_from_str(char* src) \ 8 | { \ 9 | int i; \ 10 | for (i = 0; LIST[i]; i += 2) \ 11 | if (!_stricmp(src, (char*)LIST[i + 1])) \ 12 | return (T)(int)LIST[i]; \ 13 | return (T)0; \ 14 | }; 15 | 16 | #define CONST_TO_CHAR(T, LIST) \ 17 | static const char* T##_to_str(T src, char* def = NULL) \ 18 | { \ 19 | int i; \ 20 | for (i = 0; LIST[i]; i += 2) \ 21 | if(src == (T)(int)LIST[i]) \ 22 | return (char*)LIST[i + 1]; \ 23 | return def; \ 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 3 | 4 | #define DIV_RATIO 1000 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #pragma comment(lib, "winmm.lib") 17 | 18 | #define WS_VER_MAJOR 2 19 | #define WS_VER_MINOR 2 20 | #include 21 | #pragma comment(lib, "ws2_32.lib") 22 | 23 | #include "../DeckLinkAPI_h.h" 24 | #include "../DeckLinkAPI_i.c" 25 | 26 | #pragma comment(lib, "comsuppw.lib") 27 | #pragma comment(lib, "winmm") 28 | 29 | #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } 30 | #ifndef PATH_MAX 31 | #define PATH_MAX 1024 32 | #endif 33 | 34 | #include "BMDStreamingEncodingFrameRate.h" 35 | #include "BMDStreamingH264EntropyCoding.h" 36 | #include "BMDStreamingH264Level.h" 37 | #include "BMDStreamingH264Profile.h" 38 | 39 | static int f_socket_init = 0; 40 | 41 | class CStreamingOutput : 42 | public IBMDStreamingDeviceNotificationCallback, 43 | public IBMDStreamingH264InputCallback 44 | { 45 | public: 46 | int p1; 47 | BOOL m_playing; 48 | IDeckLink* m_streamingDevice; 49 | IBMDStreamingDiscovery* m_streamingDiscovery; 50 | IBMDStreamingDeviceInput* m_streamingDeviceInput; 51 | BMDStreamingDeviceMode m_deviceMode; 52 | BMDVideoConnection m_inputConnector; 53 | BMDDisplayMode m_inputMode; 54 | 55 | int m_AudioBitrateKbs, m_VideoBitrateKbs, m_PresetAlter; 56 | int m_SrcX, m_SrcY, m_SrcWidth, m_SrcHeight, m_DstWidth, m_DstHeight, m_AudioSampleRate, m_AudioChannelCount; 57 | char m_Preset[PATH_MAX], m_Profile[PATH_MAX], m_Entropy[PATH_MAX], m_FrameRate[PATH_MAX], m_Level[PATH_MAX]; 58 | struct 59 | { 60 | char host[PATH_MAX]; 61 | int port; 62 | SOCKET socket; 63 | int sndbuf; 64 | } tcp; 65 | struct 66 | { 67 | char host[PATH_MAX]; 68 | int port; 69 | struct sockaddr_in addr; 70 | SOCKET socket; 71 | int idx; 72 | char buf[4096]; 73 | int sndbuf; 74 | } udp; 75 | struct 76 | { 77 | FILE* descriptor; 78 | FILE* stdout_descriptor; 79 | char filename[PATH_MAX]; 80 | } file; 81 | 82 | int start() 83 | { 84 | HRESULT result; 85 | 86 | // Note: at this point you may get device notification messages! 87 | result = m_streamingDiscovery->InstallDeviceNotifications(this); 88 | if (FAILED(result)) 89 | { 90 | fprintf(stderr, "ERROR: InstallDeviceNotifications failed\n"); 91 | return -1; 92 | }; 93 | 94 | fprintf(stderr, "%s: InstallDeviceNotifications done\n", __FUNCTION__); 95 | 96 | return 0; 97 | }; 98 | 99 | int stop() 100 | { 101 | if (m_playing) 102 | m_streamingDeviceInput->StopCapture(); 103 | m_streamingDiscovery->UninstallDeviceNotifications(); 104 | SAFE_RELEASE(m_streamingDeviceInput); 105 | SAFE_RELEASE(m_streamingDevice); 106 | return 0; 107 | }; 108 | 109 | int release() 110 | { 111 | fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); 112 | 113 | if (udp.socket > 0) 114 | { 115 | // shutdown(udp.socket, SD_BOTH); 116 | closesocket(udp.socket); 117 | } 118 | 119 | if (tcp.socket > 0) 120 | { 121 | // shutdown(udp.socket, SD_BOTH); 122 | closesocket(tcp.socket); 123 | } 124 | 125 | if (file.descriptor) 126 | fclose(file.descriptor); 127 | 128 | SAFE_RELEASE(m_streamingDiscovery); 129 | 130 | fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); 131 | 132 | return 0; 133 | }; 134 | 135 | int init() 136 | { 137 | int r; 138 | HRESULT result; 139 | WSADATA wsaData; 140 | 141 | fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); 142 | 143 | result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 144 | if (FAILED(result)) 145 | { 146 | fprintf(stderr, "%s:%d ERROR! CoInitializeEx failed\n", __FUNCTION__, __LINE__); 147 | return -1; 148 | }; 149 | 150 | result = CoCreateInstance(CLSID_CBMDStreamingDiscovery_v10_8, NULL, CLSCTX_ALL, IID_IBMDStreamingDiscovery, (void**)&m_streamingDiscovery); 151 | if (FAILED(result)) 152 | { 153 | fprintf(stderr, "%s:%d ERROR! Failed to create streaming discovery (v10_8)\n", __FUNCTION__, __LINE__); 154 | 155 | result = CoCreateInstance(CLSID_CBMDStreamingDiscovery, NULL, CLSCTX_ALL, IID_IBMDStreamingDiscovery, (void**)&m_streamingDiscovery); 156 | if (FAILED(result)) 157 | { 158 | fprintf(stderr, "%s:%d ERROR! Failed to create streaming discovery (current)\n", __FUNCTION__, __LINE__); 159 | return -1; 160 | } 161 | }; 162 | 163 | /* file to save */ 164 | if(!strcmp(file.filename, "-")) 165 | { 166 | file.stdout_descriptor = stdout; 167 | _setmode(_fileno(file.stdout_descriptor), O_BINARY); 168 | } 169 | else if (file.filename[0]) 170 | { 171 | file.descriptor = fopen(file.filename, "wb"); 172 | if (!file.descriptor) 173 | { 174 | fprintf(stderr, "%s:%d ERROR! Failed to open file [%s]\n", __FUNCTION__, __LINE__, file.filename); 175 | return -1; 176 | }; 177 | fprintf(stderr, "%s:%d data will be saved to file [%s]\n", __FUNCTION__, __LINE__, file.filename); 178 | } 179 | else 180 | { 181 | fprintf(stderr, "%s:%d ERROR! Filename to save not specified, either specify file name directlry or provide -savefile flag\n", 182 | __FUNCTION__, __LINE__); 183 | return -1; 184 | } 185 | 186 | /* init winsock */ 187 | if (!f_socket_init) 188 | { 189 | if (WSAStartup(((unsigned long)WS_VER_MAJOR) | (((unsigned long)WS_VER_MINOR) << 8), &wsaData) != 0) 190 | { 191 | fprintf(stderr, "%s:%d ERROR! WSAStartup failed\n", __FUNCTION__, __LINE__); 192 | return -WSAGetLastError(); 193 | }; 194 | }; 195 | 196 | /* open udp socket */ 197 | if (udp.port && udp.host[0]) 198 | { 199 | char* tmp; 200 | struct in_addr *tmp_in_addr; 201 | struct sockaddr_in saddr; 202 | struct hostent *host_ip; 203 | unsigned long 204 | multicast_a = ntohl(inet_addr("224.0.0.0")), 205 | multicast_b = ntohl(inet_addr("239.255.255.255")); 206 | 207 | /* resolv hostname */ 208 | host_ip = gethostbyname(udp.host); 209 | if (NULL == host_ip) 210 | { 211 | host_ip = gethostbyaddr(udp.host, strlen(udp.host), AF_INET); 212 | if (NULL == host_ip) 213 | { 214 | fprintf(stderr, "%s:%d ERROR! failed to resolv [%s]\n", __FUNCTION__, __LINE__, udp.host); 215 | return -1; 216 | } 217 | } 218 | 219 | /* create communication socket */ 220 | udp.socket = socket(AF_INET, SOCK_DGRAM, 0); 221 | if (INVALID_SOCKET == udp.socket) 222 | { 223 | r = -WSAGetLastError(); 224 | fprintf(stderr, "%s:%d ERROR! failed to create socket\n", __FUNCTION__, __LINE__); 225 | return r; 226 | } 227 | 228 | /* setup source address */ 229 | memset(&saddr, 0, sizeof(struct sockaddr_in)); 230 | saddr.sin_family = PF_INET; 231 | saddr.sin_port = htons(0); // Use the first free port 232 | saddr.sin_addr.s_addr = htonl(INADDR_ANY); // bind socket to any interface 233 | r = bind(udp.socket, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); 234 | if (INVALID_SOCKET == r) 235 | { 236 | r = -WSAGetLastError(); 237 | fprintf(stderr, "%s:%d ERROR! failed to bind socket\n", __FUNCTION__, __LINE__); 238 | return r; 239 | } 240 | 241 | /* prepare address */ 242 | udp.addr.sin_family = AF_INET; 243 | tmp_in_addr = (struct in_addr*)host_ip->h_addr_list[0]; 244 | tmp = inet_ntoa(*tmp_in_addr); 245 | udp.addr.sin_addr.s_addr = inet_addr(tmp); 246 | udp.addr.sin_port = htons((unsigned short)udp.port); 247 | 248 | /* check for multicast setup */ 249 | if (ntohl(udp.addr.sin_addr.s_addr) > multicast_a && udp.addr.sin_addr.s_addr < multicast_b) 250 | { 251 | struct in_addr iaddr; 252 | unsigned char ttl = 32; 253 | unsigned char one = 1; 254 | 255 | memset(&iaddr, 0, sizeof(struct in_addr)); 256 | 257 | iaddr.s_addr = INADDR_ANY; // use DEFAULT interface 258 | 259 | // Set the outgoing interface to DEFAULT 260 | r = setsockopt(udp.socket, IPPROTO_IP, IP_MULTICAST_IF, 261 | (const char *)&iaddr, sizeof(struct in_addr)); 262 | if (SOCKET_ERROR == r) 263 | fprintf(stderr, "%s:%d ERROR! setsockopt failed with %d\n", 264 | __FUNCTION__, __LINE__, WSAGetLastError()); 265 | 266 | // Set multicast packet TTL to 3; default TTL is 1 267 | r = setsockopt(udp.socket, IPPROTO_IP, IP_MULTICAST_TTL, 268 | (const char *)&ttl, sizeof(unsigned char)); 269 | if (SOCKET_ERROR == r) 270 | fprintf(stderr, "%s:%d ERROR! setsockopt failed with %d\n", 271 | __FUNCTION__, __LINE__, WSAGetLastError()); 272 | 273 | // send multicast traffic to myself too 274 | r = setsockopt(udp.socket, IPPROTO_IP, IP_MULTICAST_LOOP, 275 | (const char *)&one, sizeof(unsigned char)); 276 | if (SOCKET_ERROR == r) 277 | fprintf(stderr, "%s:%d ERROR! setsockopt failed with %d\n", 278 | __FUNCTION__, __LINE__, WSAGetLastError()); 279 | }; 280 | 281 | if (udp.sndbuf) 282 | { 283 | r = udp.sndbuf; 284 | r = setsockopt(udp.socket, SOL_SOCKET, SO_SNDBUF, (const char *)&r, sizeof(r)); 285 | }; 286 | }; 287 | 288 | /* open udp socket */ 289 | if (tcp.port && tcp.host[0]) 290 | { 291 | char* tmp; 292 | struct in_addr *tmp_in_addr; 293 | struct sockaddr_in saddr; 294 | struct hostent *host_ip; 295 | 296 | /* resolv hostname */ 297 | host_ip = gethostbyname(tcp.host); 298 | if (NULL == host_ip) 299 | { 300 | host_ip = gethostbyaddr(tcp.host, strlen(tcp.host), AF_INET); 301 | if (NULL == host_ip) 302 | { 303 | fprintf(stderr, "%s:%d ERROR! failed to resolv [%s]\n", __FUNCTION__, __LINE__, udp.host); 304 | return -1; 305 | } 306 | } 307 | 308 | /* create communication socket */ 309 | tcp.socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 310 | if (INVALID_SOCKET == tcp.socket) 311 | { 312 | r = -WSAGetLastError(); 313 | fprintf(stderr, "%s:%d ERROR! failed to create socket [%ld]\n", __FUNCTION__, __LINE__, -r); 314 | return r; 315 | } 316 | 317 | /* setup address */ 318 | tmp_in_addr = (struct in_addr*)host_ip->h_addr_list[0]; 319 | tmp = inet_ntoa(*tmp_in_addr); 320 | 321 | memset(&saddr, 0, sizeof(struct sockaddr_in)); 322 | saddr.sin_family = AF_INET; 323 | saddr.sin_port = htons(tcp.port); 324 | saddr.sin_addr.s_addr = inet_addr(tmp); 325 | 326 | r = connect(tcp.socket, (struct sockaddr *)&saddr, sizeof(struct sockaddr)); 327 | if (INVALID_SOCKET == r) 328 | { 329 | r = -WSAGetLastError(); 330 | fprintf(stderr, "%s:%d ERROR! failed to connect socket [%ld]\n", __FUNCTION__, __LINE__, -r); 331 | return r; 332 | } 333 | 334 | if (tcp.sndbuf) 335 | { 336 | r = tcp.sndbuf; 337 | r = setsockopt(tcp.socket, SOL_SOCKET, SO_SNDBUF, (const char *)&r, sizeof(r)); 338 | }; 339 | } 340 | 341 | 342 | return 0; 343 | }; 344 | 345 | static void print_usage() 346 | { 347 | int i; 348 | 349 | fprintf 350 | ( 351 | stderr, 352 | "Usage:\n" 353 | " bmd_h264_cat.exe [- | filename]\n" 354 | "Where args are:\n" 355 | " -ab audio bitrate in kbs\n" 356 | " -vb video bitrate in kbs\n" 357 | " -ar audio samplerate\n" 358 | " -ac audio channels\n" 359 | " -framerate framerate, see list below\n" 360 | " -profile h264 encoding profile to use, see list below\n" 361 | " -entropy h264 encoding entropy to use, see list below\n" 362 | " -level h264 encoding level to use, see list below\n" 363 | " -preset hardware encoder preset name to use, see list in logs\n" 364 | " -src-x source rectangle\n" 365 | " -src-y \n" 366 | " -src-width \n" 367 | " -src-height \n" 368 | " -dst-width destination width\n" 369 | " -dst-height destination height\n" 370 | " -savefile save files timestamped\n" 371 | " -udp-host host where sent UDP packet\n" 372 | " -udp-port port where sent UDP packet\n" 373 | " -udp-sndbuf SO_SNDBUF of UDP socket\n" 374 | " -tcp-host host where sent TCP packet\n" 375 | " -tcp-port port where sent TCP packet\n" 376 | " -tcp-sndbuf SO_SNDBUF of TCP socket\n" 377 | ); 378 | 379 | fprintf(stderr, "Framerates:"); 380 | for (i = 0; BMDStreamingEncodingFrameRate_pairs[i]; i += 2) 381 | fprintf(stderr, " [%s]", (char*)BMDStreamingEncodingFrameRate_pairs[i + 1]); 382 | fprintf(stderr, "\n"); 383 | 384 | fprintf(stderr, "Entropyies:"); 385 | for (i = 0; BMDStreamingH264EntropyCoding_pairs[i]; i += 2) 386 | fprintf(stderr, " [%s]", (char*)BMDStreamingH264EntropyCoding_pairs[i + 1]); 387 | fprintf(stderr, "\n"); 388 | 389 | fprintf(stderr, "Levels:"); 390 | for (i = 0; BMDStreamingH264Level_pairs[i]; i += 2) 391 | fprintf(stderr, " [%s]", (char*)BMDStreamingH264Level_pairs[i + 1]); 392 | fprintf(stderr, "\n"); 393 | 394 | fprintf(stderr, "Profiles:"); 395 | for (i = 0; BMDStreamingH264Profile_pairs[i]; i += 2) 396 | fprintf(stderr, " [%s]", (char*)BMDStreamingH264Profile_pairs[i + 1]); 397 | fprintf(stderr, "\n"); 398 | } 399 | 400 | int load_args(int argc, char** argv) 401 | { 402 | #define IF_PARAM0(V) if (!strcmp(V, argv[i])) 403 | #define IF_PARAM1(V) if (!strcmp(V, argv[i]) && (i + 1) < argc) 404 | #define PARAM1_INT(N, P) IF_PARAM1(N) { i++; P = atol(argv[i]); m_PresetAlter++;} 405 | #define PARAM1_INT_NA(N, P) IF_PARAM1(N) { i++; P = atol(argv[i]);} 406 | #define PARAM1_CHAR(N, P) IF_PARAM1(N) { i++; strncpy(P, argv[i], PATH_MAX); m_PresetAlter++;} 407 | #define PARAM1_CHAR_NA(N, P) IF_PARAM1(N) { i++; strncpy(P, argv[i], PATH_MAX);} 408 | /* setup arguments */ 409 | for (int i = 1; i < argc; i++) 410 | { 411 | PARAM1_INT("-ab", m_AudioBitrateKbs) 412 | else PARAM1_INT("-ar", m_AudioSampleRate) 413 | else PARAM1_INT("-ac", m_AudioChannelCount) 414 | else PARAM1_INT("-vb", m_VideoBitrateKbs) 415 | else PARAM1_CHAR_NA("-preset", m_Preset) 416 | else PARAM1_CHAR("-profile", m_Profile) 417 | else PARAM1_CHAR("-level", m_Level) 418 | else PARAM1_CHAR("-entropy", m_Level) 419 | else PARAM1_CHAR("-framerate", m_FrameRate) 420 | else PARAM1_INT("-src-x", m_SrcX) 421 | else PARAM1_INT("-src-y", m_SrcY) 422 | else PARAM1_INT("-src-width", m_SrcWidth) 423 | else PARAM1_INT("-src-height", m_SrcHeight) 424 | else PARAM1_INT("-dst-width", m_DstWidth) 425 | else PARAM1_INT("-dst-height", m_DstHeight) 426 | else PARAM1_CHAR_NA("-udp-host", udp.host) 427 | else PARAM1_INT_NA("-udp-port", udp.port) 428 | else PARAM1_INT_NA("-udp-sndbuf", udp.sndbuf) 429 | else PARAM1_CHAR_NA("-tcp-host", tcp.host) 430 | else PARAM1_INT_NA("-tcp-port", tcp.port) 431 | else PARAM1_INT_NA("-tcp-sndbuf", tcp.sndbuf) 432 | else if (!strcmp("-savefile", argv[i])) 433 | { 434 | time_t ltime; 435 | struct tm *rtime; 436 | 437 | /* check if log file should be rotated */ 438 | time(<ime); 439 | 440 | /* date to filename */ 441 | rtime = localtime(<ime); 442 | strftime(file.filename, MAX_PATH, "%Y%m%d_%H%M%S.ts", rtime); 443 | } 444 | else if (!strcmp("-h", argv[i])) 445 | { 446 | print_usage(); 447 | return -1; 448 | } 449 | else 450 | strncpy(file.filename, argv[i], sizeof(file.filename)); 451 | } 452 | 453 | return 0; 454 | }; 455 | 456 | CStreamingOutput(int argc, char** argv) 457 | { 458 | p1 = 0; 459 | m_playing = false; 460 | m_streamingDevice = NULL; 461 | m_streamingDiscovery = NULL; 462 | m_streamingDeviceInput = NULL; 463 | m_PresetAlter = m_AudioBitrateKbs = m_VideoBitrateKbs = 0; 464 | m_SrcX = m_SrcY = m_SrcWidth = m_SrcHeight = m_DstWidth = m_DstHeight = m_AudioSampleRate = m_AudioChannelCount = 0; 465 | m_Level[0] = m_FrameRate[0] = m_Preset[0] = m_Profile[0] = m_Entropy[0] = 0; 466 | memset(&udp, 0, sizeof(udp)); 467 | memset(&tcp, 0, sizeof(tcp)); 468 | memset(&file, 0, sizeof(file)); 469 | 470 | if(load_args(argc, argv)) 471 | exit(0); 472 | 473 | if(!file.filename[0]) 474 | { 475 | print_usage(); 476 | exit(0); 477 | }; 478 | }; 479 | 480 | ~CStreamingOutput() 481 | { 482 | // stop(); 483 | SAFE_RELEASE(m_streamingDiscovery); 484 | }; 485 | 486 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID* ppv) 487 | { 488 | HRESULT result = E_NOINTERFACE; 489 | 490 | if (ppv == NULL) 491 | return E_POINTER; 492 | *ppv = NULL; 493 | 494 | if (iid == IID_IUnknown) 495 | { 496 | *ppv = static_cast(static_cast(this)); 497 | AddRef(); 498 | result = S_OK; 499 | } 500 | else if (iid == IID_IBMDStreamingDeviceNotificationCallback) 501 | { 502 | *ppv = static_cast(this); 503 | AddRef(); 504 | result = S_OK; 505 | } 506 | else if (iid == IID_IBMDStreamingH264InputCallback) 507 | { 508 | *ppv = static_cast(this); 509 | AddRef(); 510 | result = S_OK; 511 | } 512 | 513 | return result; 514 | } 515 | 516 | public: 517 | // IUknown 518 | // We need to correctly implement QueryInterface, but not the AddRef/Release 519 | virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } 520 | virtual ULONG STDMETHODCALLTYPE Release() { return 1; } 521 | 522 | public: 523 | // IBMDStreamingDeviceNotificationCallback 524 | virtual HRESULT STDMETHODCALLTYPE StreamingDeviceArrived(IDeckLink* device) 525 | { 526 | HRESULT result; 527 | 528 | fprintf(stderr, "%s\n", __FUNCTION__); 529 | 530 | // Check we don't already have a device. 531 | if (m_streamingDevice != NULL) 532 | { 533 | fprintf(stderr, "%s: we already use device\n", __FUNCTION__); 534 | return S_OK; 535 | } 536 | 537 | // See if it can do input: 538 | result = device->QueryInterface(IID_IBMDStreamingDeviceInput, (void**)&m_streamingDeviceInput); 539 | if (FAILED(result)) 540 | { 541 | // This device doesn't support input. We can ignore this device. 542 | fprintf(stderr, "%s: ERROR: device does not support input\n", __FUNCTION__); 543 | return S_OK; 544 | } 545 | 546 | // Ok, we're happy with this device, hold a reference to the device (we 547 | // also have a reference held from the QueryInterface, too). 548 | m_streamingDevice = device; 549 | m_streamingDevice->AddRef(); 550 | 551 | // Now install our callbacks. To do this, we must query our own delegate 552 | // to get it's IUnknown interface, and pass this to the device input interface. 553 | // It will then query our interface back to a IBMDStreamingH264InputCallback, 554 | // if that's what it wants. 555 | // Note, although you may be tempted to cast directly to an IUnknown, it's 556 | // not particular safe, and is invalid COM. 557 | IUnknown* ourCallbackDelegate; 558 | this->QueryInterface(IID_IUnknown, (void**)&ourCallbackDelegate); 559 | // 560 | result = m_streamingDeviceInput->SetCallback(ourCallbackDelegate); 561 | // 562 | // Finally release ourCallbackDelegate, since we created a reference to it 563 | // during QueryInterface. The device will hold its own reference. 564 | ourCallbackDelegate->Release(); 565 | 566 | if (FAILED(result)) 567 | { 568 | SAFE_RELEASE(m_streamingDeviceInput); 569 | SAFE_RELEASE(m_streamingDevice); 570 | return S_OK; 571 | } 572 | 573 | fprintf(stderr, "%s!\n", __FUNCTION__); 574 | UpdateUIForNewDevice(); 575 | 576 | return S_OK; 577 | }; 578 | 579 | virtual HRESULT STDMETHODCALLTYPE StreamingDeviceRemoved(IDeckLink* device) 580 | { 581 | // These messages will happen on the main loop as a result 582 | // of the message pump. 583 | 584 | // We only care about removal of the device we are using 585 | if (device != m_streamingDevice) 586 | return S_OK; 587 | 588 | m_streamingDeviceInput->SetCallback(NULL); 589 | SAFE_RELEASE(m_streamingDeviceInput); 590 | SAFE_RELEASE(m_streamingDevice); 591 | 592 | fprintf(stderr, "%s!\n", __FUNCTION__); 593 | exit(0); 594 | 595 | return S_OK; 596 | }; 597 | 598 | virtual HRESULT STDMETHODCALLTYPE StreamingDeviceModeChanged(IDeckLink* device, BMDStreamingDeviceMode mode) 599 | { 600 | fprintf(stderr, "%s!\n", __FUNCTION__); 601 | 602 | if (mode == m_deviceMode) 603 | return S_OK; 604 | 605 | m_deviceMode = mode; 606 | 607 | UpdateUIForModeChanges(); 608 | 609 | return S_OK; 610 | }; 611 | 612 | virtual HRESULT STDMETHODCALLTYPE StreamingDeviceFirmwareUpdateProgress(IDeckLink* device, unsigned char percent) 613 | { 614 | fprintf(stderr, "%s: %d%%\n", __FUNCTION__, percent); 615 | return S_OK; 616 | }; 617 | 618 | void UpdateUIForNewDevice() 619 | { 620 | char* m; 621 | BSTR modelName; 622 | HRESULT hr; 623 | 624 | if (m_streamingDevice->GetModelName(&modelName) != S_OK) 625 | { 626 | fprintf(stderr, "%s: GetModelName failed\n", __FUNCTION__); 627 | return; 628 | } 629 | m = _com_util::ConvertBSTRToString(modelName); 630 | SysFreeString(modelName); 631 | fprintf(stderr, "%s: found model name [%s]\n", __FUNCTION__, m); 632 | delete[] m; 633 | 634 | if (m_streamingDevice->GetDisplayName(&modelName) != S_OK) 635 | { 636 | fprintf(stderr, "%s: GetDisplayName failed\n", __FUNCTION__); 637 | return; 638 | } 639 | m = _com_util::ConvertBSTRToString(modelName); 640 | SysFreeString(modelName); 641 | fprintf(stderr, "%s: found display [%s]\n", __FUNCTION__, m); 642 | delete[] m; 643 | 644 | // Add video input modes: 645 | IDeckLinkDisplayModeIterator* inputModeIterator; 646 | if (FAILED(m_streamingDeviceInput->GetVideoInputModeIterator(&inputModeIterator))) 647 | { 648 | fprintf(stderr, "%s: ERROR, GetVideoInputModeIterator failed\n", __FUNCTION__); 649 | return; 650 | } 651 | 652 | BMDDisplayMode currentInputModeValue; 653 | if (FAILED(m_streamingDeviceInput->GetCurrentDetectedVideoInputMode(¤tInputModeValue))) 654 | { 655 | fprintf(stderr, "%s: ERROR, GetCurrentDetectedVideoInputMode failed\n", __FUNCTION__); 656 | return; 657 | } 658 | 659 | IDeckLinkDisplayMode* inputMode; 660 | while (inputModeIterator->Next(&inputMode) == S_OK) 661 | { 662 | BSTR modeName; 663 | if (inputMode->GetName(&modeName) != S_OK) 664 | { 665 | SAFE_RELEASE(inputMode); 666 | SAFE_RELEASE(inputModeIterator); 667 | fprintf(stderr, "%s: ERROR, inputMode->GetName failed\n", __FUNCTION__); 668 | return; 669 | } 670 | 671 | if (inputMode->GetDisplayMode() == currentInputModeValue) 672 | { 673 | m = _com_util::ConvertBSTRToString(modeName); 674 | fprintf(stderr, "%s: current mode: %s\n", __FUNCTION__, m); 675 | delete[] m; 676 | } 677 | SysFreeString(modeName); 678 | 679 | SAFE_RELEASE(inputMode); 680 | } 681 | 682 | SAFE_RELEASE(inputModeIterator); 683 | 684 | IBMDStreamingVideoEncodingModePresetIterator* presetIterator; 685 | 686 | if (SUCCEEDED(m_streamingDeviceInput->GetVideoEncodingModePresetIterator(currentInputModeValue, &presetIterator))) 687 | { 688 | int f_preset_done = 0; 689 | IBMDStreamingVideoEncodingMode* encodingMode = NULL; 690 | BSTR encodingModeName; 691 | 692 | while (presetIterator->Next(&encodingMode) == S_OK && !f_preset_done) 693 | { 694 | encodingMode->GetName(&encodingModeName); 695 | 696 | m = _com_util::ConvertBSTRToString(encodingModeName); 697 | fprintf(stderr, "%s: supported encoding preset mode: [%s]\n", __FUNCTION__, m); 698 | 699 | if (!strcmp(m_Preset, m)) 700 | { 701 | if (S_OK == m_streamingDeviceInput->SetVideoEncodingMode(encodingMode)) 702 | { 703 | fprintf(stderr, "%s: encoding preset mode set: %s\n", __FUNCTION__, m_Preset); 704 | } 705 | else 706 | fprintf(stderr, "%s: failed to set encoding preset mode: %s\n", __FUNCTION__, m_Preset); 707 | } 708 | 709 | delete[] m; 710 | 711 | SysFreeString(encodingModeName); 712 | 713 | encodingMode->Release(); 714 | }; 715 | 716 | SAFE_RELEASE(presetIterator); 717 | } 718 | 719 | IBMDStreamingVideoEncodingMode* currentVideoEncodingMode; 720 | if (SUCCEEDED(m_streamingDeviceInput->GetVideoEncodingMode(¤tVideoEncodingMode))) 721 | { 722 | char* ms; 723 | long long l; 724 | IBMDStreamingMutableVideoEncodingMode* m = NULL; 725 | if (m_PresetAlter) 726 | { 727 | if (S_OK != currentVideoEncodingMode->CreateMutableVideoEncodingMode(&m)) 728 | fprintf(stderr, "%s: CreateMutableVideoEncodingMode failed\n", __FUNCTION__); 729 | }; 730 | 731 | BSTR encodingModeName; 732 | if (currentVideoEncodingMode->GetName(&encodingModeName) == S_OK) 733 | { 734 | ms = _com_util::ConvertBSTRToString(encodingModeName); 735 | fprintf(stderr, "%s: ENCODING: name=[%s]\n", __FUNCTION__, ms); 736 | delete[] ms; 737 | SysFreeString(encodingModeName); 738 | }; 739 | 740 | /* source rect update */ 741 | fprintf(stderr, "%s: ENCODING: source=[%d, %d, %d, %d]", __FUNCTION__, 742 | currentVideoEncodingMode->GetSourcePositionX(), 743 | currentVideoEncodingMode->GetSourcePositionY(), 744 | currentVideoEncodingMode->GetSourceWidth(), 745 | currentVideoEncodingMode->GetSourceHeight()); 746 | if (m && m_SrcWidth && m_SrcHeight) 747 | { 748 | fprintf(stderr, " => [%d, %d, %d, %d]", m_SrcX, m_SrcY, m_SrcWidth, m_SrcHeight); 749 | if (S_OK != m->SetSourceRect(m_SrcX, m_SrcY, m_SrcWidth, m_SrcHeight)) 750 | fprintf(stderr, " FAILED"); 751 | }; 752 | fprintf(stderr, "\n"); 753 | 754 | /* destination width */ 755 | fprintf(stderr, "%s: ENCODING: destination=[%d, %d]", __FUNCTION__, 756 | currentVideoEncodingMode->GetDestWidth(), 757 | currentVideoEncodingMode->GetDestHeight()); 758 | if (m && m_DstWidth && m_DstHeight) 759 | { 760 | fprintf(stderr, " => [%d, %d]", m_DstWidth, m_DstHeight); 761 | if (S_OK != m->SetDestSize(m_DstWidth, m_DstHeight)) 762 | fprintf(stderr, " FAILED"); 763 | }; 764 | fprintf(stderr, "\n"); 765 | 766 | /* video bitrate */ 767 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyVideoBitRateKbps, &l)) 768 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyVideoBitRateKbps failed\n", __FUNCTION__); 769 | else 770 | { 771 | fprintf(stderr, "%s: ENCODING: video bitrate=[%lld]Kbps", __FUNCTION__, l); 772 | if (m && m_VideoBitrateKbs) 773 | { 774 | fprintf(stderr, " => [%d]Kbps", m_VideoBitrateKbs); 775 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyVideoBitRateKbps, m_VideoBitrateKbs)) 776 | fprintf(stderr, " FAILED"); 777 | }; 778 | }; 779 | fprintf(stderr, "\n"); 780 | 781 | /* audio bitrate */ 782 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyAudioBitRateKbps, &l)) 783 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioBitRateKbps failed\n", __FUNCTION__); 784 | else 785 | { 786 | fprintf(stderr, "%s: ENCODING: audio bitrate=[%lld]Kbps", __FUNCTION__, l); 787 | if (m && m_AudioBitrateKbs) 788 | { 789 | fprintf(stderr, " => [%d]Kbps", m_AudioBitrateKbs); 790 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyAudioBitRateKbps, m_AudioBitrateKbs)) 791 | fprintf(stderr, " FAILED"); 792 | }; 793 | }; 794 | fprintf(stderr, "\n"); 795 | 796 | /* audio samplerate */ 797 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyAudioSampleRate, &l)) 798 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioSampleRate failed\n", __FUNCTION__); 799 | else 800 | { 801 | fprintf(stderr, "%s: ENCODING: audio samplerate=[%lld]Hz", __FUNCTION__, l); 802 | if (m && m_AudioSampleRate) 803 | { 804 | fprintf(stderr, " => [%d]Hz", m_AudioSampleRate); 805 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyAudioSampleRate, m_AudioSampleRate)) 806 | fprintf(stderr, " FAILED"); 807 | }; 808 | }; 809 | fprintf(stderr, "\n"); 810 | 811 | /* audio channels count */ 812 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyAudioChannelCount, &l)) 813 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioChannelCount failed\n", __FUNCTION__); 814 | else 815 | { 816 | fprintf(stderr, "%s: ENCODING: audio channels count=[%lld]", __FUNCTION__, l); 817 | if (m && m_AudioSampleRate) 818 | { 819 | fprintf(stderr, " => [%d]", m_AudioChannelCount); 820 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyAudioChannelCount, m_AudioChannelCount)) 821 | fprintf(stderr, " FAILED"); 822 | }; 823 | }; 824 | fprintf(stderr, "\n"); 825 | 826 | /* H264Level */ 827 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyH264Level, &l)) 828 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264Level failed\n", __FUNCTION__); 829 | else 830 | { 831 | fprintf(stderr, "%s: ENCODING: H264Level=[%s]", __FUNCTION__, 832 | BMDStreamingH264Level_to_str((BMDStreamingH264Level)l)); 833 | if (m && m_Level[0]) 834 | { 835 | if(BMDStreamingH264Level_from_str(m_Level)) 836 | { 837 | fprintf(stderr, " => [%s]", m_Level); 838 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyH264Level, 839 | BMDStreamingH264Level_from_str(m_Level))) 840 | fprintf(stderr, " FAILED"); 841 | } 842 | else 843 | fprintf(stderr, " NOT RECOGNIZED [%s]", m_Level); 844 | }; 845 | }; 846 | fprintf(stderr, "\n"); 847 | 848 | /* Profile */ 849 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyH264Profile, &l)) 850 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264Profile failed\n", __FUNCTION__); 851 | else 852 | { 853 | fprintf(stderr, "%s: ENCODING: H264Profile=[%s]", __FUNCTION__, 854 | BMDStreamingH264Profile_to_str((BMDStreamingH264Profile)l, "UNKNOWN")); 855 | if (m && m_Profile[0]) 856 | { 857 | if (BMDStreamingH264Profile_from_str(m_Profile)) 858 | { 859 | fprintf(stderr, " => [%s]", m_Profile); 860 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyH264Profile, 861 | BMDStreamingH264Profile_from_str(m_Profile))) 862 | fprintf(stderr, " FAILED"); 863 | } 864 | else 865 | fprintf(stderr, " NOT RECOGNIZED [%s]", m_Profile); 866 | }; 867 | }; 868 | fprintf(stderr, "\n"); 869 | 870 | /* Entropy */ 871 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyH264EntropyCoding, &l)) 872 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264EntropyCoding failed\n", __FUNCTION__); 873 | else 874 | { 875 | fprintf(stderr, "%s: ENCODING: H264EntropyCoding=[%s]", __FUNCTION__, 876 | BMDStreamingH264EntropyCoding_to_str((BMDStreamingH264EntropyCoding)l, "UNKNOWN")); 877 | if (m && m_Entropy[0]) 878 | { 879 | if (BMDStreamingH264EntropyCoding_from_str(m_Entropy)) 880 | { 881 | fprintf(stderr, " => [%s]", m_Entropy); 882 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyH264EntropyCoding, 883 | BMDStreamingH264EntropyCoding_from_str(m_Entropy))) 884 | fprintf(stderr, " FAILED"); 885 | } 886 | else 887 | fprintf(stderr, " NOT RECOGNIZED [%s]", m_Entropy); 888 | 889 | }; 890 | }; 891 | fprintf(stderr, "\n"); 892 | 893 | /* Framerate */ 894 | if (S_OK != currentVideoEncodingMode->GetInt(bmdStreamingEncodingPropertyVideoFrameRate, &l)) 895 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyVideoFrameRate failed\n", __FUNCTION__); 896 | else 897 | { 898 | fprintf(stderr, "%s: ENCODING: FrameRate=[%s]", __FUNCTION__, 899 | BMDStreamingEncodingFrameRate_to_str((BMDStreamingEncodingFrameRate)l, "UNKNOWN")); 900 | if (m && m_FrameRate[0]) 901 | { 902 | if (BMDStreamingEncodingFrameRate_from_str(m_FrameRate)) 903 | { 904 | fprintf(stderr, " => [%s]", m_FrameRate); 905 | if (S_OK != m->SetInt(bmdStreamingEncodingPropertyVideoFrameRate, 906 | BMDStreamingEncodingFrameRate_from_str(m_FrameRate))) 907 | fprintf(stderr, " FAILED"); 908 | } 909 | else 910 | fprintf(stderr, " NOT RECOGNIZED [%s]", m_FrameRate); 911 | 912 | }; 913 | }; 914 | fprintf(stderr, "\n"); 915 | 916 | /* try to probe */ 917 | if (m) 918 | { 919 | BMDStreamingEncodingSupport supp; 920 | IBMDStreamingVideoEncodingMode* m_new = NULL; 921 | 922 | hr = m_streamingDeviceInput->DoesSupportVideoEncodingMode(currentInputModeValue, m, &supp, &m_new); 923 | if (S_OK != hr) 924 | fprintf(stderr, "%s: ERROR, DoesSupportVideoEncodingMode failed\n", __FUNCTION__); 925 | else 926 | { 927 | IBMDStreamingVideoEncodingMode* m_sup = NULL; 928 | 929 | if (supp == bmdStreamingEncodingModeNotSupported) 930 | fprintf(stderr, "%s: ERROR, Altered mode not supported\n", __FUNCTION__); 931 | else if (supp == bmdStreamingEncodingModeSupported) 932 | { 933 | fprintf(stderr, "%s: Altered mode fully supported\n", __FUNCTION__); 934 | m_sup = m; 935 | } 936 | else if (supp == bmdStreamingEncodingModeSupportedWithChanges) 937 | { 938 | fprintf(stderr, "%s: Altered mode supported with changes\n", __FUNCTION__); 939 | m_sup = m_new; 940 | 941 | fprintf(stderr, "%s: ALTERED ENCODING: source=[%d, %d, %d, %d]\n", __FUNCTION__, 942 | m_new->GetSourcePositionX(), 943 | m_new->GetSourcePositionY(), 944 | m_new->GetSourceWidth(), 945 | m_new->GetSourceHeight()); 946 | 947 | fprintf(stderr, "%s: ALTERED ENCODING: destination=[%d, %d]\n", __FUNCTION__, 948 | m_new->GetDestWidth(), 949 | m_new->GetDestHeight()); 950 | 951 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyVideoBitRateKbps, &l)) 952 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyVideoBitRateKbps failed\n", __FUNCTION__); 953 | else 954 | fprintf(stderr, "%s: ALTERED ENCODING: video bitrate=[%lld]Kbps\n", __FUNCTION__, l); 955 | 956 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyVideoFrameRate, &l)) 957 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyVideoFrameRate failed\n", __FUNCTION__); 958 | else 959 | fprintf(stderr, "%s: ALTERED ENCODING: video framerate=[%lld]Kbps\n", __FUNCTION__, l); 960 | 961 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyAudioBitRateKbps, &l)) 962 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioBitRateKbps failed\n", __FUNCTION__); 963 | else 964 | fprintf(stderr, "%s: ALTERED ENCODING: audio bitrate=[%lld]Kbps\n", __FUNCTION__, l); 965 | 966 | /* audio samplerate */ 967 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyAudioSampleRate, &l)) 968 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioSampleRate failed\n", __FUNCTION__); 969 | else 970 | fprintf(stderr, "%s: ALTERED ENCODING: audio samplerate=[%lld]Hz\n", __FUNCTION__, l); 971 | 972 | /* audio channels count */ 973 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyAudioChannelCount, &l)) 974 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyAudioChannelCount failed\n", __FUNCTION__); 975 | else 976 | fprintf(stderr, "%s: ALTERED ENCODING: audio channels count=[%lld]\n", __FUNCTION__, l); 977 | 978 | /* level */ 979 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyH264Level, &l)) 980 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264Level failed\n", __FUNCTION__); 981 | else 982 | fprintf(stderr, "%s: ALTERED ENCODING: H264Level=[%s]\n", __FUNCTION__, 983 | BMDStreamingH264Level_to_str((BMDStreamingH264Level)l)); 984 | 985 | /* Profile */ 986 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyH264Profile, &l)) 987 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264Profile failed\n", __FUNCTION__); 988 | else 989 | fprintf(stderr, "%s: ALTERED ENCODING: H264Profile=[%s]\n", __FUNCTION__, 990 | BMDStreamingH264Profile_to_str((BMDStreamingH264Profile)l, "UNKNOWN")); 991 | 992 | /* Entropy */ 993 | if (S_OK != m_new->GetInt(bmdStreamingEncodingPropertyH264EntropyCoding, &l)) 994 | fprintf(stderr, "%s: bmdStreamingEncodingPropertyH264EntropyCoding failed\n", __FUNCTION__); 995 | else 996 | fprintf(stderr, "%s: ALTERED ENCODING: H264EntropyCoding=[%s]\n", __FUNCTION__, 997 | BMDStreamingH264EntropyCoding_to_str((BMDStreamingH264EntropyCoding)l, "UNKNOWN")); 998 | }; 999 | 1000 | if (m_sup) 1001 | { 1002 | hr = m_streamingDeviceInput->SetVideoEncodingMode(m_sup); 1003 | if (S_OK == hr) 1004 | fprintf(stderr, "%s: SetVideoEncodingMode(ALTERED)\n", __FUNCTION__); 1005 | else 1006 | fprintf(stderr, "%s: ERROR SetVideoEncodingMode(ALTERED) %s\n", __FUNCTION__, (E_FAIL == hr) ? "E_FAIL" : "E_INVALIDARG"); 1007 | }; 1008 | 1009 | SAFE_RELEASE(m_new); 1010 | }; 1011 | 1012 | SAFE_RELEASE(m); 1013 | }; 1014 | 1015 | SAFE_RELEASE(currentVideoEncodingMode); 1016 | }; 1017 | 1018 | /* start capture */ 1019 | m_streamingDeviceInput->StartCapture(); 1020 | m_playing = true; 1021 | }; 1022 | 1023 | void UpdateUIForNoDevice(); 1024 | void UpdateUIForModeChanges() 1025 | { 1026 | fprintf(stderr, "%s\n", __FUNCTION__); 1027 | }; 1028 | void UpdateEncodingPresetsUIForInputMode(); 1029 | void EncodingPresetsRemoveItems(); 1030 | 1031 | public: 1032 | virtual HRESULT STDMETHODCALLTYPE H264NALPacketArrived(IBMDStreamingH264NALPacket* nalPacket) 1033 | { 1034 | #if 0 1035 | static unsigned char pp[] = { '/', '|', '\\', '-' }; 1036 | long s = nalPacket->GetPayloadSize(); 1037 | void* data; 1038 | 1039 | nalPacket->GetBytes(&data); 1040 | 1041 | fprintf(stderr, "%s: %5d bytes %c\r", __FUNCTION__, s, pp[(p1++) % 4]); 1042 | 1043 | fwrite(data, 1, s, stdout); 1044 | #endif 1045 | return S_OK; 1046 | }; 1047 | virtual HRESULT STDMETHODCALLTYPE H264AudioPacketArrived(IBMDStreamingAudioPacket* audioPacket){ return S_OK; }; 1048 | virtual HRESULT STDMETHODCALLTYPE MPEG2TSPacketArrived(IBMDStreamingMPEG2TSPacket* mpeg2TSPacket) 1049 | { 1050 | static unsigned char pp[] = { '/', '|', '\\', '-' }; 1051 | long s = mpeg2TSPacket->GetPayloadSize(); 1052 | void* data; 1053 | 1054 | mpeg2TSPacket->GetBytes(&data); 1055 | 1056 | if(!(p1 % DIV_RATIO)) 1057 | { 1058 | fprintf(stderr, "%s: %5d bytes %c\r", __FUNCTION__, s, pp[(p1 / DIV_RATIO) % 4]); 1059 | }; 1060 | 1061 | p1++; 1062 | 1063 | /* save to file */ 1064 | if (file.descriptor) 1065 | fwrite(data, 1, s, file.descriptor); 1066 | 1067 | /* send to stdout */ 1068 | if (file.stdout_descriptor) 1069 | fwrite(data, 1, s, file.stdout_descriptor); 1070 | 1071 | /* send to UDP */ 1072 | if (188 == s && udp.socket > 0) 1073 | { 1074 | memcpy(udp.buf + 188 * udp.idx++, data, s); 1075 | 1076 | if(7 == udp.idx) 1077 | { 1078 | int l, r; 1079 | 1080 | /* send datagram */ 1081 | l = sizeof(struct sockaddr_in); 1082 | r = sendto 1083 | ( 1084 | udp.socket, /* Socket to send result */ 1085 | (const char*)udp.buf, /* The datagram buffer */ 1086 | 188 * 7, /* The datagram lngth */ 1087 | 0, /* Flags: no options */ 1088 | (struct sockaddr *)&udp.addr, /* addr */ 1089 | l /* Server address length */ 1090 | ); 1091 | 1092 | udp.idx = 0; 1093 | }; 1094 | }; 1095 | 1096 | /* send to TCP */ 1097 | if (tcp.socket > 0) 1098 | { 1099 | send(tcp.socket, (const char*)data, s, 0); 1100 | }; 1101 | 1102 | return S_OK; 1103 | }; 1104 | virtual HRESULT STDMETHODCALLTYPE H264VideoInputConnectorScanningChanged(void){ return E_NOTIMPL; }; 1105 | virtual HRESULT STDMETHODCALLTYPE H264VideoInputConnectorChanged(void){ return E_NOTIMPL; }; 1106 | virtual HRESULT STDMETHODCALLTYPE H264VideoInputModeChanged(void) 1107 | { 1108 | fprintf(stderr, "%s!\n", __FUNCTION__); 1109 | 1110 | UpdateUIForModeChanges(); 1111 | 1112 | return S_OK; 1113 | }; 1114 | }; 1115 | 1116 | int main(int argc, char** argv) 1117 | { 1118 | CStreamingOutput* strm; 1119 | 1120 | fprintf(stderr, "bmd_h264_cat built on " __DATE__ " " __TIME__ "\n"); 1121 | 1122 | strm = new CStreamingOutput(argc, argv); 1123 | if (strm->init() >= 0) 1124 | { 1125 | strm->start(); 1126 | 1127 | getc(stdin); 1128 | 1129 | strm->stop(); 1130 | 1131 | strm->release(); 1132 | } 1133 | delete strm; 1134 | 1135 | return 0; 1136 | } 1137 | --------------------------------------------------------------------------------