├── .gitignore ├── COPYING ├── README.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── libv4l-rkmpp1.dirs ├── libv4l-rkmpp1.install └── rules ├── include ├── libv4l-rkmpp-dec.h ├── libv4l-rkmpp-enc.h └── libv4l-rkmpp.h ├── meson.build ├── meson_options.txt └── src ├── libv4l-rkmpp-dec.c ├── libv4l-rkmpp-enc.c ├── libv4l-rkmpp.c └── utils.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.1 3 | *.3 4 | *.5 5 | *.7 6 | *.bin 7 | *.flags 8 | *.ko 9 | *.ko.cmd 10 | *.la 11 | *.lo 12 | *.log 13 | *.mod.c 14 | *.mod.o 15 | *.o 16 | *.o.cmd 17 | *.so 18 | *.sh 19 | *.sw? 20 | *.trs 21 | .depend 22 | .deps 23 | .dirstamp 24 | .libs 25 | .tmp_versions 26 | .*check* 27 | .*install* 28 | Makefile 29 | Makefile.in 30 | TAGS 31 | aclocal.m4 32 | autom4te.cache 33 | build-aux 34 | config.h 35 | config.h.in 36 | config.log 37 | config.status 38 | configure 39 | libtool 40 | stamp-h1 41 | m4 42 | !m4/ac_define_dir.m4 43 | include/libv4l-plugin.h 44 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.] 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations 27 | below. 28 | 29 | When we speak of free software, we are referring to freedom of use, 30 | not price. Our General Public Licenses are designed to make sure that 31 | you have the freedom to distribute copies of free software (and charge 32 | for this service if you wish); that you receive source code or can get 33 | it if you want it; that you can change the software and use pieces of 34 | it in new free programs; and that you are informed that you can do 35 | these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid 38 | distributors to deny you these rights or to ask you to surrender these 39 | rights. These restrictions translate to certain responsibilities for 40 | you if you distribute copies of the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis 43 | or for a fee, you must give the recipients all the rights that we gave 44 | you. You must make sure that they, too, receive or can get the source 45 | code. If you link other code with the library, you must provide 46 | complete object files to the recipients, so that they can relink them 47 | with the library after making changes to the library and recompiling 48 | it. And you must show them these terms so they know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the 51 | library, and (2) we offer you this license, which gives you legal 52 | permission to copy, distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that 55 | there is no warranty for the free library. Also, if the library is 56 | modified by someone else and passed on, the recipients should know 57 | that what they have is not the original version, so that the original 58 | author's reputation will not be affected by problems that might be 59 | introduced by others. 60 | ^L 61 | Finally, software patents pose a constant threat to the existence of 62 | any free program. We wish to make sure that a company cannot 63 | effectively restrict the users of a free program by obtaining a 64 | restrictive license from a patent holder. Therefore, we insist that 65 | any patent license obtained for a version of the library must be 66 | consistent with the full freedom of use specified in this license. 67 | 68 | Most GNU software, including some libraries, is covered by the 69 | ordinary GNU General Public License. This license, the GNU Lesser 70 | General Public License, applies to certain designated libraries, and 71 | is quite different from the ordinary General Public License. We use 72 | this license for certain libraries in order to permit linking those 73 | libraries into non-free programs. 74 | 75 | When a program is linked with a library, whether statically or using 76 | a shared library, the combination of the two is legally speaking a 77 | combined work, a derivative of the original library. The ordinary 78 | General Public License therefore permits such linking only if the 79 | entire combination fits its criteria of freedom. The Lesser General 80 | Public License permits more lax criteria for linking other code with 81 | the library. 82 | 83 | We call this license the "Lesser" General Public License because it 84 | does Less to protect the user's freedom than the ordinary General 85 | Public License. It also provides other free software developers Less 86 | of an advantage over competing non-free programs. These disadvantages 87 | are the reason we use the ordinary General Public License for many 88 | libraries. However, the Lesser license provides advantages in certain 89 | special circumstances. 90 | 91 | For example, on rare occasions, there may be a special need to 92 | encourage the widest possible use of a certain library, so that it 93 | becomes a de-facto standard. To achieve this, non-free programs must 94 | be allowed to use the library. A more frequent case is that a free 95 | library does the same job as widely used non-free libraries. In this 96 | case, there is little to gain by limiting the free library to free 97 | software only, so we use the Lesser General Public License. 98 | 99 | In other cases, permission to use a particular library in non-free 100 | programs enables a greater number of people to use a large body of 101 | free software. For example, permission to use the GNU C Library in 102 | non-free programs enables many more people to use the whole GNU 103 | operating system, as well as its variant, the GNU/Linux operating 104 | system. 105 | 106 | Although the Lesser General Public License is Less protective of the 107 | users' freedom, it does ensure that the user of a program that is 108 | linked with the Library has the freedom and the wherewithal to run 109 | that program using a modified version of the Library. 110 | 111 | The precise terms and conditions for copying, distribution and 112 | modification follow. Pay close attention to the difference between a 113 | "work based on the library" and a "work that uses the library". The 114 | former contains code derived from the library, whereas the latter must 115 | be combined with the library in order to run. 116 | ^L 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 2. You may modify your copy or copies of the Library or any portion 165 | of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | a) The modified work must itself be a software library. 170 | 171 | b) You must cause the files modified to carry prominent notices 172 | stating that you changed the files and the date of any change. 173 | 174 | c) You must cause the whole of the work to be licensed at no 175 | charge to all third parties under the terms of this License. 176 | 177 | d) If a facility in the modified Library refers to a function or a 178 | table of data to be supplied by an application program that uses 179 | the facility, other than as an argument passed when the facility 180 | is invoked, then you must make a good faith effort to ensure that, 181 | in the event an application does not supply such function or 182 | table, the facility still operates, and performs whatever part of 183 | its purpose remains meaningful. 184 | 185 | (For example, a function in a library to compute square roots has 186 | a purpose that is entirely well-defined independent of the 187 | application. Therefore, Subsection 2d requires that any 188 | application-supplied function or table used by this function must 189 | be optional: if the application does not supply it, the square 190 | root function must still compute square roots.) 191 | 192 | These requirements apply to the modified work as a whole. If 193 | identifiable sections of that work are not derived from the Library, 194 | and can be reasonably considered independent and separate works in 195 | themselves, then this License, and its terms, do not apply to those 196 | sections when you distribute them as separate works. But when you 197 | distribute the same sections as part of a whole which is a work based 198 | on the Library, the distribution of the whole must be on the terms of 199 | this License, whose permissions for other licensees extend to the 200 | entire whole, and thus to each and every part regardless of who wrote 201 | it. 202 | 203 | Thus, it is not the intent of this section to claim rights or contest 204 | your rights to work written entirely by you; rather, the intent is to 205 | exercise the right to control the distribution of derivative or 206 | collective works based on the Library. 207 | 208 | In addition, mere aggregation of another work not based on the Library 209 | with the Library (or with a work based on the Library) on a volume of 210 | a storage or distribution medium does not bring the other work under 211 | the scope of this License. 212 | 213 | 3. You may opt to apply the terms of the ordinary GNU General Public 214 | License instead of this License to a given copy of the Library. To do 215 | this, you must alter all the notices that refer to this License, so 216 | that they refer to the ordinary GNU General Public License, version 2, 217 | instead of to this License. (If a newer version than version 2 of the 218 | ordinary GNU General Public License has appeared, then you can specify 219 | that version instead if you wish.) Do not make any other change in 220 | these notices. 221 | ^L 222 | Once this change is made in a given copy, it is irreversible for 223 | that copy, so the ordinary GNU General Public License applies to all 224 | subsequent copies and derivative works made from that copy. 225 | 226 | This option is useful when you wish to copy part of the code of 227 | the Library into a program that is not a library. 228 | 229 | 4. You may copy and distribute the Library (or a portion or 230 | derivative of it, under Section 2) in object code or executable form 231 | under the terms of Sections 1 and 2 above provided that you accompany 232 | it with the complete corresponding machine-readable source code, which 233 | must be distributed under the terms of Sections 1 and 2 above on a 234 | medium customarily used for software interchange. 235 | 236 | If distribution of object code is made by offering access to copy 237 | from a designated place, then offering equivalent access to copy the 238 | source code from the same place satisfies the requirement to 239 | distribute the source code, even though third parties are not 240 | compelled to copy the source along with the object code. 241 | 242 | 5. A program that contains no derivative of any portion of the 243 | Library, but is designed to work with the Library by being compiled or 244 | linked with it, is called a "work that uses the Library". Such a 245 | work, in isolation, is not a derivative work of the Library, and 246 | therefore falls outside the scope of this License. 247 | 248 | However, linking a "work that uses the Library" with the Library 249 | creates an executable that is a derivative of the Library (because it 250 | contains portions of the Library), rather than a "work that uses the 251 | library". The executable is therefore covered by this License. 252 | Section 6 states terms for distribution of such executables. 253 | 254 | When a "work that uses the Library" uses material from a header file 255 | that is part of the Library, the object code for the work may be a 256 | derivative work of the Library even though the source code is not. 257 | Whether this is true is especially significant if the work can be 258 | linked without the Library, or if the work is itself a library. The 259 | threshold for this to be true is not precisely defined by law. 260 | 261 | If such an object file uses only numerical parameters, data 262 | structure layouts and accessors, and small macros and small inline 263 | functions (ten lines or less in length), then the use of the object 264 | file is unrestricted, regardless of whether it is legally a derivative 265 | work. (Executables containing this object code plus portions of the 266 | Library will still fall under Section 6.) 267 | 268 | Otherwise, if the work is a derivative of the Library, you may 269 | distribute the object code for the work under the terms of Section 6. 270 | Any executables containing that work also fall under Section 6, 271 | whether or not they are linked directly with the Library itself. 272 | ^L 273 | 6. As an exception to the Sections above, you may also combine or 274 | link a "work that uses the Library" with the Library to produce a 275 | work containing portions of the Library, and distribute that work 276 | under terms of your choice, provided that the terms permit 277 | modification of the work for the customer's own use and reverse 278 | engineering for debugging such modifications. 279 | 280 | You must give prominent notice with each copy of the work that the 281 | Library is used in it and that the Library and its use are covered by 282 | this License. You must supply a copy of this License. If the work 283 | during execution displays copyright notices, you must include the 284 | copyright notice for the Library among them, as well as a reference 285 | directing the user to the copy of this License. Also, you must do one 286 | of these things: 287 | 288 | a) Accompany the work with the complete corresponding 289 | machine-readable source code for the Library including whatever 290 | changes were used in the work (which must be distributed under 291 | Sections 1 and 2 above); and, if the work is an executable linked 292 | with the Library, with the complete machine-readable "work that 293 | uses the Library", as object code and/or source code, so that the 294 | user can modify the Library and then relink to produce a modified 295 | executable containing the modified Library. (It is understood 296 | that the user who changes the contents of definitions files in the 297 | Library will not necessarily be able to recompile the application 298 | to use the modified definitions.) 299 | 300 | b) Use a suitable shared library mechanism for linking with the 301 | Library. A suitable mechanism is one that (1) uses at run time a 302 | copy of the library already present on the user's computer system, 303 | rather than copying library functions into the executable, and (2) 304 | will operate properly with a modified version of the library, if 305 | the user installs one, as long as the modified version is 306 | interface-compatible with the version that the work was made with. 307 | 308 | c) Accompany the work with a written offer, valid for at least 309 | three years, to give the same user the materials specified in 310 | Subsection 6a, above, for a charge no more than the cost of 311 | performing this distribution. 312 | 313 | d) If distribution of the work is made by offering access to copy 314 | from a designated place, offer equivalent access to copy the above 315 | specified materials from the same place. 316 | 317 | e) Verify that the user has already received a copy of these 318 | materials or that you have already sent this user a copy. 319 | 320 | For an executable, the required form of the "work that uses the 321 | Library" must include any data and utility programs needed for 322 | reproducing the executable from it. However, as a special exception, 323 | the materials to be distributed need not include anything that is 324 | normally distributed (in either source or binary form) with the major 325 | components (compiler, kernel, and so on) of the operating system on 326 | which the executable runs, unless that component itself accompanies 327 | the executable. 328 | 329 | It may happen that this requirement contradicts the license 330 | restrictions of other proprietary libraries that do not normally 331 | accompany the operating system. Such a contradiction means you cannot 332 | use both them and the Library together in an executable that you 333 | distribute. 334 | ^L 335 | 7. You may place library facilities that are a work based on the 336 | Library side-by-side in a single library together with other library 337 | facilities not covered by this License, and distribute such a combined 338 | library, provided that the separate distribution of the work based on 339 | the Library and of the other library facilities is otherwise 340 | permitted, and provided that you do these two things: 341 | 342 | a) Accompany the combined library with a copy of the same work 343 | based on the Library, uncombined with any other library 344 | facilities. This must be distributed under the terms of the 345 | Sections above. 346 | 347 | b) Give prominent notice with the combined library of the fact 348 | that part of it is a work based on the Library, and explaining 349 | where to find the accompanying uncombined form of the same work. 350 | 351 | 8. You may not copy, modify, sublicense, link with, or distribute 352 | the Library except as expressly provided under this License. Any 353 | attempt otherwise to copy, modify, sublicense, link with, or 354 | distribute the Library is void, and will automatically terminate your 355 | rights under this License. However, parties who have received copies, 356 | or rights, from you under this License will not have their licenses 357 | terminated so long as such parties remain in full compliance. 358 | 359 | 9. You are not required to accept this License, since you have not 360 | signed it. However, nothing else grants you permission to modify or 361 | distribute the Library or its derivative works. These actions are 362 | prohibited by law if you do not accept this License. Therefore, by 363 | modifying or distributing the Library (or any work based on the 364 | Library), you indicate your acceptance of this License to do so, and 365 | all its terms and conditions for copying, distributing or modifying 366 | the Library or works based on it. 367 | 368 | 10. Each time you redistribute the Library (or any work based on the 369 | Library), the recipient automatically receives a license from the 370 | original licensor to copy, distribute, link with or modify the Library 371 | subject to these terms and conditions. You may not impose any further 372 | restrictions on the recipients' exercise of the rights granted herein. 373 | You are not responsible for enforcing compliance by third parties with 374 | this License. 375 | ^L 376 | 11. If, as a consequence of a court judgment or allegation of patent 377 | infringement or for any other reason (not limited to patent issues), 378 | conditions are imposed on you (whether by court order, agreement or 379 | otherwise) that contradict the conditions of this License, they do not 380 | excuse you from the conditions of this License. If you cannot 381 | distribute so as to satisfy simultaneously your obligations under this 382 | License and any other pertinent obligations, then as a consequence you 383 | may not distribute the Library at all. For example, if a patent 384 | license would not permit royalty-free redistribution of the Library by 385 | all those who receive copies directly or indirectly through you, then 386 | the only way you could satisfy both it and this License would be to 387 | refrain entirely from distribution of the Library. 388 | 389 | If any portion of this section is held invalid or unenforceable under 390 | any particular circumstance, the balance of the section is intended to 391 | apply, and the section as a whole is intended to apply in other 392 | circumstances. 393 | 394 | It is not the purpose of this section to induce you to infringe any 395 | patents or other property right claims or to contest validity of any 396 | such claims; this section has the sole purpose of protecting the 397 | integrity of the free software distribution system which is 398 | implemented by public license practices. Many people have made 399 | generous contributions to the wide range of software distributed 400 | through that system in reliance on consistent application of that 401 | system; it is up to the author/donor to decide if he or she is willing 402 | to distribute software through any other system and a licensee cannot 403 | impose that choice. 404 | 405 | This section is intended to make thoroughly clear what is believed to 406 | be a consequence of the rest of this License. 407 | 408 | 12. If the distribution and/or use of the Library is restricted in 409 | certain countries either by patents or by copyrighted interfaces, the 410 | original copyright holder who places the Library under this License 411 | may add an explicit geographical distribution limitation excluding those 412 | countries, so that distribution is permitted only in or among 413 | countries not thus excluded. In such case, this License incorporates 414 | the limitation as if written in the body of this License. 415 | 416 | 13. The Free Software Foundation may publish revised and/or new 417 | versions of the Lesser General Public License from time to time. 418 | Such new versions will be similar in spirit to the present version, 419 | but may differ in detail to address new problems or concerns. 420 | 421 | Each version is given a distinguishing version number. If the Library 422 | specifies a version number of this License which applies to it and 423 | "any later version", you have the option of following the terms and 424 | conditions either of that version or of any later version published by 425 | the Free Software Foundation. If the Library does not specify a 426 | license version number, you may choose any version ever published by 427 | the Free Software Foundation. 428 | ^L 429 | 14. If you wish to incorporate parts of the Library into other free 430 | programs whose distribution conditions are incompatible with these, 431 | write to the author to ask for permission. For software which is 432 | copyrighted by the Free Software Foundation, write to the Free 433 | Software Foundation; we sometimes make exceptions for this. Our 434 | decision will be guided by the two goals of preserving the free status 435 | of all derivatives of our free software and of promoting the sharing 436 | and reuse of software generally. 437 | 438 | NO WARRANTY 439 | 440 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 441 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 442 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 443 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 444 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 445 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 446 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 447 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 448 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 449 | 450 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 451 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 452 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 453 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 454 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 455 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 456 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 457 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 458 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 459 | DAMAGES. 460 | 461 | END OF TERMS AND CONDITIONS 462 | ^L 463 | How to Apply These Terms to Your New Libraries 464 | 465 | If you develop a new library, and you want it to be of the greatest 466 | possible use to the public, we recommend making it free software that 467 | everyone can redistribute and change. You can do so by permitting 468 | redistribution under these terms (or, alternatively, under the terms 469 | of the ordinary General Public License). 470 | 471 | To apply these terms, attach the following notices to the library. 472 | It is safest to attach them to the start of each source file to most 473 | effectively convey the exclusion of warranty; and each file should 474 | have at least the "copyright" line and a pointer to where the full 475 | notice is found. 476 | 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02111-1307 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or 498 | your school, if any, to sign a "copyright disclaimer" for the library, 499 | if necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James 503 | Random Hacker. 504 | 505 | , 1 April 1990 506 | Ty Coon, President of Vice 507 | 508 | That's all there is to it! 509 | 510 | 511 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libv4l-rkmpp 2 | 3 | A V4L2 plugin that wraps [rockchip-mpp](http://opensource.rock-chips.com/wiki_Mpp) for the chromium's V4L2 video decoder/VEA (requires custom patches to enable those features). 4 | 5 | The original idea comes from [v4l-gst](https://github.com/igel-oss/v4l-gst). 6 | 7 | ## Dependencies 8 | 9 | * [v4l-utils](https://git.linuxtv.org/v4l-utils.git) - with this patch: 10 | [0001-libv4l2-Support-mmap-to-libv4l-plugin.patch](https://github.com/JeffyCN/meta-rockchip/blob/release-1.3.0_20200915/recipes-multimedia/v4l2apps/v4l-utils/0001-libv4l2-Support-mmap-to-libv4l-plugin.patch) 11 | * [rockchip-mpp](https://github.com/rockchip-linux/mpp) 12 | 13 | ## Building 14 | 15 | ``` 16 | $ meson build 17 | $ meson compile -C build 18 | ``` 19 | 20 | ## Quick Start 21 | 22 | 1. Install libv4l-rkmpp.so into /usr/lib/libv4l/plugins/ 23 | 24 | 2. Create dummy V4L2 device files for chromium V4L2 video decoder/VEA in boot service: 25 | ``` 26 | # echo dec > /dev/video-dec0 27 | # chmod 666 /dev/video-dec0 28 | # echo enc > /dev/video-enc0 29 | # chmod 666 /dev/video-enc0 30 | ``` 31 | 32 | 3. Configure codec capabilities 33 | 34 | The codec capabilities (depends on chip spec) are configurable in device files: 35 | ``` 36 | # cat /dev/video-dec0 37 | log-fps=1 38 | log-level=2 39 | type=dec 40 | codecs=VP8:VP9:H.264:H.265:AV1 41 | max-height=1920 42 | max-width=1080 43 | ``` 44 | 45 | 4. Run with chromium browser: 46 | ``` 47 | export XDG_RUNTIME_DIR=/run/user/0 48 | chromium --no-sandbox --gpu-sandbox-start-early --ignore-gpu-blacklist 49 | ``` 50 | This plugin is tested with [custom chromium](https://github.com/JeffyCN/meta-rockchip/tree/chromium-dunfell/dynamic-layers/recipes-browser/chromium) on rk3588 EVB. 51 | 52 | ## Limitation 53 | 54 | 1. There're a lot of chromium related hacks in it, might not work for other apps. 55 | 56 | ## FAQ 57 | 58 | 1. MPP reports errors? 59 | 60 | Try the newest [MPP](https://github.com/rockchip-linux/mpp) release branch or develop branch or the commit with the closest commit date. 61 | 62 | Also test with the [mpi_dec_test](https://github.com/rockchip-linux/mpp/blob/release/test/mpi_dec_test.c) to check if the MPP works: 63 | ``` 64 | # mpi_dec_test -t 7 -i test-25fps.h264 65 | ``` 66 | 67 | 2. How to get more verbose logs? 68 | 69 | For chromium, use these command line flags to change the log level: [--enable-logging --vmodule=*/media/gpu*=4](https://www.chromium.org/for-testers/enable-logging) 70 | 71 | For libv4l-rkmpp, set the "LIBV4L_RKMPP_LOG_LEVEL" environment variable to change the log level. And set "LIBV4L_RKMPP_LOG_FPS" to enable logging fps. 72 | 73 | For MPP, set the environment variable "mpp_debug", "rkv_h264d_debug", "mpp_dec_debug", "mpi_debug", etc. to change the modules' log levels. 74 | 75 | For vpu driver, write verbose log level to "/sys/module/rk_vcodec/parameters/debug". 76 | 77 | 3. What about the performance? 78 | 79 | The performance should be much the same as other MPP based decoders/encoders (e.g. mpi_dec_test and gstreamer MPP plugin). 80 | 81 | And the performance would mostly related to the video's attributes (e.g. resolution and bitrate) and the vpu clock rates. 82 | 83 | ## Maintainers 84 | 85 | * Jeffy Chen `` 86 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libv4l-rkmpp (1.8.0-1) stable; urgency=medium 2 | 3 | * 23f970c enc: Fix worker_mutex error 4 | * 5257143 Fix dqbuf() dead-lock in blocking mode 5 | * aedea86 enc: Support mjpeg 6 | * ab1ccb1 Fix a try_fmt() crash case 7 | * e27377c enc: Fix hstride error 8 | 9 | -- Jeffy Chen Wed, 08 Jan 2025 12:15:23 +0800 10 | 11 | libv4l-rkmpp (1.7.1-1) stable; urgency=medium 12 | 13 | * df2e70e Fix extra event after dqbuf() 14 | * 6c475e0 Drop rga 15 | * ba058a8 d/control: Update build-deps 16 | * 3d6edc4 Fix compile warning 17 | * fb10c83 Support query profiles 18 | * 574bde8 Fix compile error with older kernel headers 19 | 20 | -- Jeffy Chen Tue, 18 Jun 2024 15:11:03 +0800 21 | 22 | libv4l-rkmpp (1.7.0-1) stable; urgency=medium 23 | 24 | * enc: Support bitrate mode for new chromium V4L2 video encoder 25 | * Refactor a bit 26 | * Fix race between thread and resetting 27 | * Adjust log levels 28 | * Support flushing with encode and decode cmds 29 | 30 | -- Jeffy Chen Mon, 25 Mar 2024 09:57:39 +0800 31 | 32 | libv4l-rkmpp (1.6.0-1) stable; urgency=medium 33 | 34 | * meson: Compat with old meson 35 | * dec: Support AV1 36 | * Support setting codecs 37 | * README.md: Add codec capabilities configs in quick start 38 | * meson.build: Increase meson required version to 0.50.0 39 | * Fix compile warning 40 | * dec: Support chromium V4L2 stateful video decoder 41 | 42 | -- Jeffy Chen Fri, 15 Dec 2023 11:06:11 +0800 43 | 44 | libv4l-rkmpp (1.5.1-1.1) stable; urgency=medium 45 | 46 | * Add more debug logs 47 | * Fix a few streaming status errors 48 | 49 | -- Jeffy Chen Tue, 08 Nov 2022 09:55:20 +0800 50 | 51 | libv4l-rkmpp (1.5.0-1.1) stable; urgency=medium 52 | 53 | * dec: Support HEVC 54 | * Switch to meson build system 55 | * Fix a few compile warnings 56 | * meson: Support setting dec/enc size limits 57 | * Support more device options 58 | 59 | -- Jeffy Chen Wed, 14 Sep 2022 10:16:33 +0800 60 | 61 | libv4l-rkmpp (1.4.0-1) stable; urgency=medium 62 | 63 | * dec: Adjust V4L2_CID_MIN_BUFFERS_FOR_CAPTURE 64 | * README.md: Update information 65 | * debian: Drop source format 66 | * enc: Support V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR 67 | * enc: Support more qp ctrls 68 | * enc: Allow more ctrls 69 | * enc: Fix h264 profile setting error 70 | * enc: Drop crop limits 71 | * Use common mmap instead of drm mmap 72 | * dec: Enable MPP fast mode by default 73 | * Filter out real devices earlier 74 | 75 | -- Jeffy Chen Fri, 29 Jul 2022 10:42:22 +0800 76 | 77 | libv4l-rkmpp (1.3.3-1) stable; urgency=medium 78 | 79 | * dec: Increase poll timeout 80 | * Add ctx type when logging FPS 81 | * enc: Add initial cfg for VP8 82 | 83 | -- Jeffy Chen Tue, 24 Nov 2020 14:51:43 +0800 84 | 85 | libv4l-rkmpp (1.3.2-1) stable; urgency=medium 86 | 87 | * Use pkgconfig for librga 88 | * dec: Support VIDIOC_G_EXT_CTRLS 89 | * enc: Switch to the new enc config API 90 | 91 | -- Jeffy Chen Tue, 01 Sep 2020 10:06:23 +0800 92 | 93 | libv4l-rkmpp (1.3.1-1) stable; urgency=medium 94 | 95 | * Remove out-dated VIDIOC_RESERVED 96 | * Supress some error logs 97 | * enc: Fix mpp rate_in < rate_out error 98 | * enc: Use new MPP_ENC_GET_HDR_SYNC API 99 | 100 | -- Jeffy Chen Mon, 20 Jul 2020 15:42:32 +0800 101 | 102 | libv4l-rkmpp (1.3.0-1) unstable; urgency=medium 103 | 104 | * enc: Drop JPEG 105 | * enc: Adapt to newest MPP 106 | * enc: Fix VP8 encode error 107 | * enc: Support VP8 keyframe requests 108 | * README.md: Update information 109 | * Move more logs to level 1 110 | 111 | -- Jeffy Chen Wed, 08 Apr 2020 11:43:33 +0800 112 | 113 | libv4l-rkmpp (1.2.3-1) unstable; urgency=medium 114 | 115 | * Remove license in Makefile.am 116 | * Add V4L2_PIX_FMT_VP9 to dec.h for old linux headers 117 | * Add encoder support 118 | * README.md: Fix dead link 119 | * Fix wrong RGA format maps for NV12/NV21 120 | * Fix random crash when trying to access packet in mpp 121 | * Fix linking errors 122 | * Define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME for old kernel 123 | headers 124 | * dec: Fix wrong fourcc for VP9 125 | * enc: Ignore keyframe request for VP8 126 | * dec: Support dynamic info changing 127 | * d/control: Fix description error 128 | * d/rules: Add autoreconf 129 | 130 | -- Jeffy Chen Fri, 03 Apr 2020 11:10:35 +0800 131 | 132 | libv4l-rkmpp (1.1.0-1) unstable; urgency=medium 133 | 134 | * Initial release 135 | 136 | -- Jeffy Chen Fri, 31 May 2019 08:43:13 +0000 137 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libv4l-rkmpp 2 | Priority: optional 3 | Maintainer: Jeffy Chen 4 | Build-Depends: debhelper (>= 9), 5 | meson (>= 0.50.0), 6 | ninja-build, 7 | pkg-config, 8 | librockchip-mpp-dev, 9 | libv4l-dev 10 | Standards-Version: 3.9.8 11 | Section: libs 12 | Homepage: https://github.com/JeffyCN/libv4l-rkmpp 13 | #Vcs-Git: https://anonscm.debian.org/collab-maint/libv4l-rkmpp.git 14 | #Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/libv4l-rkmpp.git 15 | 16 | Package: libv4l-rkmpp 17 | Architecture: any 18 | Depends: ${shlibs:Depends}, ${misc:Depends} 19 | Description: A rockchip-mpp V4L2 wrapper plugin for chromium video dec and enc 20 | A V4L2 plugin that wraps rockchip-mpp for the chromium's V4L2 video decoder and VEA. 21 | . 22 | The original idea comes from v4l-gst. 23 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: libv4l-rkmpp 3 | Source: https://github.com/JeffyCN/libv4l-rkmpp 4 | 5 | Files: * 6 | Copyright: 2019 Jeffy Chen 7 | License: LGPL-2.0+ 8 | 9 | Files: debian/* 10 | Copyright: 2019 Jeffy Chen 11 | License: LGPL-2.0+ 12 | 13 | License: LGPL-2.0+ 14 | This package is free software; you can redistribute it and/or 15 | modify it under the terms of the GNU Lesser General Public 16 | License as published by the Free Software Foundation; either 17 | version 2 of the License, or (at your option) any later version. 18 | . 19 | This package is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | . 24 | You should have received a copy of the GNU Lesser General Public License 25 | along with this program. If not, see . 26 | . 27 | On Debian systems, the complete text of the GNU Lesser General 28 | Public License can be found in "/usr/share/common-licenses/LGPL-2". 29 | 30 | # Please also look if there are files or directories which have a 31 | # different copyright/license attached and list them here. 32 | # Please avoid picking licenses with terms that are more restrictive than the 33 | # packaged work, as it may make Debian's contributions unacceptable upstream. 34 | -------------------------------------------------------------------------------- /debian/libv4l-rkmpp1.dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | -------------------------------------------------------------------------------- /debian/libv4l-rkmpp1.install: -------------------------------------------------------------------------------- 1 | usr/lib/libv4l/plugins/lib*.so 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | %: 7 | dh $@ --buildsystem=meson 8 | -------------------------------------------------------------------------------- /include/libv4l-rkmpp-dec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef LIBV4L_RKMPP_DEC_H 16 | #define LIBV4L_RKMPP_DEC_H 17 | 18 | #include "libv4l-rkmpp.h" 19 | 20 | #ifndef V4L2_PIX_FMT_VP9 21 | #define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ 22 | #endif 23 | 24 | #ifndef V4L2_PIX_FMT_HEVC 25 | #define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC */ 26 | #endif 27 | 28 | #ifndef V4L2_PIX_FMT_AV1 29 | #define V4L2_PIX_FMT_AV1 v4l2_fourcc('A', 'V', '0', '1') /* AV1 */ 30 | #endif 31 | 32 | /** 33 | * struct rkmpp_video_info - Video information 34 | * @valid: Data is valid. 35 | * @dirty: Data is dirty(have not applied to mpp). 36 | * @event: Pending V4L2 src_change event. 37 | * @mpp_format: MPP frame format. 38 | * @width: Video width. 39 | * @height: Video height. 40 | * @hor_stride: Video horizontal stride. 41 | * @ver_stride: Video vertical stride. 42 | * @size: Required video buffer size. 43 | */ 44 | struct rkmpp_video_info { 45 | bool valid; 46 | bool dirty; 47 | bool event; 48 | 49 | MppFrameFormat mpp_format; 50 | uint32_t width; 51 | uint32_t height; 52 | uint32_t hor_stride; 53 | uint32_t ver_stride; 54 | 55 | uint32_t size; 56 | }; 57 | 58 | /** 59 | * struct rkmpp_dec_context - Context private data for decoder 60 | * @ctx: Common context data. 61 | * @video_info: Video information. 62 | * @event_subscribed: V4L2 event subscribed. 63 | * @pending_eos: Has pending EOS to return. 64 | */ 65 | struct rkmpp_dec_context { 66 | struct rkmpp_context *ctx; 67 | struct rkmpp_video_info video_info; 68 | 69 | bool event_subscribed; 70 | 71 | bool pending_eos; 72 | }; 73 | 74 | bool rkmpp_dec_has_event(void *data); 75 | void *rkmpp_dec_init(struct rkmpp_context *ctx); 76 | int rkmpp_dec_ioctl(void *data, unsigned long cmd, void *arg); 77 | void rkmpp_dec_deinit(void *data); 78 | 79 | #endif //LIBV4L_RKMPP_DEC_H 80 | -------------------------------------------------------------------------------- /include/libv4l-rkmpp-enc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef LIBV4L_RKMPP_ENC_H 16 | #define LIBV4L_RKMPP_ENC_H 17 | 18 | #include "libv4l-rkmpp.h" 19 | 20 | #ifndef V4L2_CID_MPEG_VIDEO_H264_SPS_PPS_BEFORE_IDR 21 | #define V4L2_CID_MPEG_VIDEO_H264_SPS_PPS_BEFORE_IDR (V4L2_CID_MPEG_BASE+388) 22 | #endif 23 | 24 | #define MPP_H264_PROFILE_BASELINE 66 25 | #define MPP_H264_PROFILE_MAIN 77 26 | #define MPP_H264_PROFILE_HIGH 100 27 | 28 | /* The MPP is using 1K for header buf. */ 29 | #define MAX_HEADER_BYTES (1 << 10) 30 | 31 | /** 32 | * struct rkmpp_enc_context - Context private data for encoder 33 | * @ctx: Common context data. 34 | * @h264: Enc config for H264. 35 | * @vp8: Enc config for VP8. 36 | * @mjpeg: Enc config for MJPEG. 37 | * @type: Encoder format type. 38 | * @needs_header: Needs to process header. 39 | * @header: Header packet. 40 | * @header_mode: MPP header mode. 41 | * @rc_mode: MPP rate-control mode. 42 | * @mb_rc: V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE. 43 | * @rc_reaction_coeff: V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF. 44 | * @gop_size: V4L2_CID_MPEG_VIDEO_GOP_SIZE. 45 | * @fixed_bitrate: V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT. 46 | * @bitrate: Target bitrate. 47 | * @denominator: Target denominator. 48 | * @numerator: Target numerator. 49 | * @keyframe_requested: Number of keyframes requested. 50 | * @width: Video width. 51 | * @height: Video height. 52 | * @hstride: Video hstride. 53 | * @vstride: Video vstride. 54 | */ 55 | struct rkmpp_enc_context { 56 | struct rkmpp_context *ctx; 57 | struct v4l2_rect crop; 58 | 59 | struct { 60 | int profile; 61 | int level; 62 | bool separate_header; /* V4L2_CID_MPEG_VIDEO_HEADER_MODE */ 63 | } h264; 64 | 65 | struct { 66 | bool is_ivf; 67 | } vp8; 68 | 69 | struct { 70 | int quality; 71 | } mjpeg; 72 | 73 | enum { 74 | H264, 75 | VP8, 76 | MJPEG, 77 | } type; 78 | 79 | int max_qp; 80 | int min_qp; 81 | 82 | bool needs_header; 83 | MppPacket header; 84 | MppEncHeaderMode header_mode; 85 | 86 | MppEncRcMode rc_mode; 87 | 88 | bool mb_rc; 89 | int rc_reaction_coeff; 90 | int gop_size; 91 | bool fixed_bitrate; 92 | 93 | int bitrate; 94 | int denominator; 95 | int numerator; 96 | 97 | int keyframe_requested; 98 | 99 | int width; 100 | int height; 101 | int hstride; 102 | int vstride; 103 | }; 104 | 105 | bool rkmpp_enc_has_event(void *data); 106 | void *rkmpp_enc_init(struct rkmpp_context *ctx); 107 | int rkmpp_enc_ioctl(void *data, unsigned long cmd, void *arg); 108 | void rkmpp_enc_deinit(void *data); 109 | 110 | #endif //LIBV4L_RKMPP_ENC_H 111 | -------------------------------------------------------------------------------- /include/libv4l-rkmpp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef LIBV4L_RKMPP_H 16 | #define LIBV4L_RKMPP_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "config.h" 34 | #include "linux/videodev2.h" 35 | 36 | #ifndef V4L2_CTRL_CLASS_CODEC 37 | #define V4L2_CTRL_CLASS_CODEC 0x00990000 38 | #endif 39 | 40 | #ifndef V4L2_CID_CODEC_BASE 41 | #define V4L2_CID_CODEC_BASE (V4L2_CTRL_CLASS_CODEC | 0x900) 42 | #endif 43 | 44 | #ifndef V4L2_CID_MPEG_VIDEO_VP9_PROFILE 45 | #define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_CODEC_BASE+512) 46 | #define V4L2_MPEG_VIDEO_VP9_PROFILE_0 0 47 | #define V4L2_MPEG_VIDEO_VP9_PROFILE_2 2 48 | #endif 49 | 50 | #ifndef V4L2_CID_MPEG_VIDEO_HEVC_PROFILE 51 | #define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE (V4L2_CID_CODEC_BASE + 615) 52 | #define V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN 0 53 | #define V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 2 54 | #endif 55 | 56 | #ifndef V4L2_CID_MPEG_VIDEO_AV1_PROFILE 57 | #define V4L2_CID_MPEG_VIDEO_AV1_PROFILE (V4L2_CID_CODEC_BASE + 655) 58 | #define V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN 0 59 | #endif 60 | 61 | #define LIBV4L_RKMPP_VERSION "1.8.0~20250108" 62 | 63 | extern int rkmpp_log_level; 64 | 65 | #define gettid() syscall(SYS_gettid) 66 | 67 | #define LOG(fmt, ...) do { \ 68 | struct timeval tv; \ 69 | gettimeofday(&tv, NULL); \ 70 | printf("[%03ld.%03ld] [RKMPP] [%ld] %s(%d): " fmt, \ 71 | tv.tv_sec % 1000, tv.tv_usec / 1000, gettid(), \ 72 | __func__, __LINE__, ##__VA_ARGS__); \ 73 | fflush(stdout); \ 74 | } while (0) 75 | 76 | #define LOGV(level, fmt, ...) \ 77 | do { if (rkmpp_log_level >= level) LOG(fmt, ##__VA_ARGS__); } while (0) 78 | 79 | #define LOGE(fmt, ...) LOG("ERR: " fmt, ##__VA_ARGS__) 80 | 81 | #define RETURN_ERR(err, ret) \ 82 | { errno = err; LOGV(2, "errno: %d\n", errno); return ret; } 83 | 84 | #define ENTER() LOGV(5, "ctx(%p): ENTER\n", (void *)ctx) 85 | #define LEAVE() LOGV(5, "ctx(%p): LEAVE\n", (void *)ctx) 86 | 87 | #define min(x, y) ((x) < (y) ? (x) : (y)) 88 | #define max(x, y) ((x) > (y) ? (x) : (y)) 89 | 90 | /* From kernel's linux/kernel.h */ 91 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 92 | 93 | #define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi) 94 | 95 | #define __round_mask(x, y) ((__typeof__(x))((y)-1)) 96 | #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) 97 | 98 | /* From kernel's linux/stddef.h */ 99 | #ifndef offsetof 100 | #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) 101 | #endif 102 | 103 | /* From kernel's v4l2-core/v4l2-ioctl.c */ 104 | #define CLEAR_AFTER_FIELD(p, field) \ 105 | memset((uint8_t *)(p) + \ 106 | offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 107 | 0, sizeof(*(p)) - \ 108 | offsetof(typeof(*(p)), field) - sizeof((p)->field)) 109 | 110 | #define MPP_VIDEO_CodingNone MPP_VIDEO_CodingUnused 111 | 112 | #define RKMPP_MB_DIM 16 113 | #define RKMPP_SB_DIM 64 114 | 115 | #define RKMPP_MAX_PLANES 3 116 | 117 | #define RKMPP_MEM_OFFSET(type, index) \ 118 | ((int64_t) ((type) << 16 | (index))) 119 | #define RKMPP_MEM_OFFSET_TYPE(offset) (int)((offset) >> 16) 120 | #define RKMPP_MEM_OFFSET_INDEX(offset) (int)((offset) & ((1 << 16) - 1)) 121 | 122 | #define RKMPP_HAS_FORMAT(ctx, format) \ 123 | (!((format)->type != MPP_VIDEO_CodingNone && (ctx)->codecs && \ 124 | !strstr((ctx)->codecs, (format)->name))) 125 | 126 | /** 127 | * struct rkmpp_fmt - Information about mpp supported format 128 | * @name: Format's name. 129 | * @fourcc: Format's forcc. 130 | * @num_planes: Number of planes. 131 | * @type: Format's mpp coding type. 132 | * @format: Format's mpp frame format. 133 | * @depth: Format's pixel depth. 134 | * @frmsize: V4L2 frmsize_stepwise. 135 | */ 136 | struct rkmpp_fmt { 137 | char *name; 138 | uint32_t fourcc; 139 | int num_planes; 140 | MppCodingType type; 141 | MppFrameFormat format; 142 | uint8_t depth[VIDEO_MAX_PLANES]; 143 | struct v4l2_frmsize_stepwise frmsize; 144 | }; 145 | 146 | /** 147 | * enum rkmpp_buffer_flag - Flags of rkmpp buffer 148 | * @ERROR: Something wrong in the buffer. 149 | * @LOCKED: Buffer been locked from mpp buffer group. 150 | * @EXPORTED: Buffer been exported to userspace. 151 | * @QUEUED: Buffer been queued. 152 | * @PENDING: Buffer is in pending queue. 153 | * @AVAILABLE: Buffer is in available queue. 154 | * @KEYFRAME: Buffer is keyframe. 155 | * @LAST: Buffer is the last. 156 | */ 157 | enum rkmpp_buffer_flag { 158 | RKMPP_BUFFER_ERROR = 1 << 0, 159 | RKMPP_BUFFER_LOCKED = 1 << 1, 160 | RKMPP_BUFFER_EXPORTED = 1 << 2, 161 | RKMPP_BUFFER_QUEUED = 1 << 3, 162 | RKMPP_BUFFER_PENDING = 1 << 4, 163 | RKMPP_BUFFER_AVAILABLE = 1 << 5, 164 | RKMPP_BUFFER_KEYFRAME = 1 << 6, 165 | RKMPP_BUFFER_LAST = 1 << 7, 166 | }; 167 | 168 | /** 169 | * struct rkmpp_buffer - Information about mpp buffer 170 | * @entry: Queue entry. 171 | * @rkmpp_buf: Handle of mpp buffer. 172 | * @index: Buffer's index. 173 | * @fd: Buffer's dma fd. 174 | * @timestamp: Buffer's timestamp. 175 | * @bytesused: Number of bytes occupied by data in the buffer. 176 | * @length: Buffer's length(planes). 177 | * @size: Buffer's size. 178 | * @flags: Buffer's flags. 179 | * @planes: Buffer's planes info. 180 | */ 181 | struct rkmpp_buffer { 182 | TAILQ_ENTRY(rkmpp_buffer) entry; 183 | MppBuffer rkmpp_buf; 184 | 185 | int index; 186 | 187 | int fd; 188 | uint64_t timestamp; 189 | uint32_t bytesused; 190 | uint32_t length; 191 | uint32_t flags; 192 | uint32_t size; 193 | uint32_t type; 194 | 195 | struct { 196 | unsigned long userptr; 197 | int fd; 198 | uint32_t data_offset; 199 | uint32_t bytesused; 200 | uint32_t plane_size; /* bytesused - data_offset */ 201 | uint32_t length; 202 | } planes[RKMPP_MAX_PLANES]; 203 | }; 204 | 205 | TAILQ_HEAD(rkmpp_buf_head, rkmpp_buffer); 206 | 207 | /** 208 | * struct rkmpp_buf_head - Information about mpp buffer queue 209 | * @memory: V4L2 memory type. 210 | * @streaming: The queue is streaming. 211 | * @internal_group: Handle of mpp internal buffer group. 212 | * @external_group: Handle of mpp external buffer group. 213 | * @buffers: List of buffers. 214 | * @num_buffers: Number of buffers. 215 | * @avail_buffers: Buffers ready to be dequeued. 216 | * @pending_buffers: Pending buffers for mpp. 217 | * @queue_cond: Condition variable for buffer litsts. 218 | * @queue_mutex: Mutex for buffer lists. 219 | * @rkmpp_format: Mpp format. 220 | * @format: V4L2 multi-plane format. 221 | */ 222 | struct rkmpp_buf_queue { 223 | enum v4l2_memory memory; 224 | 225 | bool streaming; 226 | 227 | MppBufferGroup internal_group; 228 | MppBufferGroup external_group; 229 | struct rkmpp_buffer *buffers; 230 | uint32_t num_buffers; 231 | 232 | struct rkmpp_buf_head avail_buffers; 233 | struct rkmpp_buf_head pending_buffers; 234 | pthread_cond_t queue_cond; 235 | pthread_mutex_t queue_mutex; 236 | 237 | const struct rkmpp_fmt *rkmpp_format; 238 | struct v4l2_pix_format_mplane format; 239 | }; 240 | 241 | /** 242 | * struct rkmpp_context - Context data 243 | * @formats: Supported formats. 244 | * @num_formats: Number of formats. 245 | * @is_decoder: Is decoder mode. 246 | * @nonblock: Nonblock mode. 247 | * @eventfd: File descriptor of eventfd. 248 | * @avail_buffers: Buffers ready to be dequeued. 249 | * @pending_buffers: Pending buffers for mpp. 250 | * @mpp: Handler of mpp context. 251 | * @mpi: Handler of mpp api. 252 | * @mpp_streaming: The mpp is streaming. 253 | * @mpp_produced: The mpp has produced packets or frames. 254 | * @pausing: Stop feeding new data to mpp. 255 | * @eos_buffer: A dummy buffer used to represent EOS. 256 | * @output: Output queue. 257 | * @capture: Capture queue. 258 | * @ioctl_mutex: Mutex for ioctl. 259 | * @worker_thread: Handler of the worker thread. 260 | * @worker_cond: Condition variable for streaming status. 261 | * @worker_mutex: Mutex for streaming status. 262 | * @frames: Number of frames reported. 263 | * @last_fps_time: The last time to count fps. 264 | * @data: Private data. 265 | */ 266 | struct rkmpp_context { 267 | struct rkmpp_fmt *formats; 268 | uint32_t num_formats; 269 | 270 | bool is_decoder; 271 | bool nonblock; 272 | int eventfd; 273 | 274 | MppCtx mpp; 275 | MppApi *mpi; 276 | 277 | bool mpp_streaming; 278 | bool mpp_produced; 279 | 280 | bool pausing; 281 | struct rkmpp_buffer eos_buffer; 282 | 283 | struct rkmpp_buf_queue output; 284 | struct rkmpp_buf_queue capture; 285 | 286 | pthread_mutex_t ioctl_mutex; 287 | 288 | pthread_t worker_thread; 289 | pthread_cond_t worker_cond; 290 | pthread_mutex_t worker_mutex; 291 | 292 | uint64_t frames; 293 | uint64_t last_fps_time; 294 | 295 | unsigned int max_width; 296 | unsigned int max_height; 297 | char *codecs; 298 | 299 | void *data; 300 | }; 301 | 302 | static inline 303 | struct rkmpp_buf_queue *rkmpp_get_queue(struct rkmpp_context *ctx, 304 | enum v4l2_buf_type type) 305 | { 306 | LOGV(4, "type = %d\n", type); 307 | 308 | switch (type) { 309 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: 310 | return &ctx->capture; 311 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: 312 | return &ctx->output; 313 | default: 314 | LOGE("invalid buf type\n"); 315 | RETURN_ERR(EINVAL, NULL); 316 | } 317 | } 318 | 319 | void rkmpp_reset_queue(struct rkmpp_context *ctx, 320 | struct rkmpp_buf_queue *queue); 321 | 322 | static inline 323 | const char* rkmpp_cmd2str(unsigned long cmd) 324 | { 325 | #define RKMPP_CMD2STR(cmd) case cmd: return #cmd 326 | 327 | switch (cmd) { 328 | RKMPP_CMD2STR(VIDIOC_QUERYCAP); 329 | RKMPP_CMD2STR(VIDIOC_ENUM_FMT); 330 | RKMPP_CMD2STR(VIDIOC_G_FMT); 331 | RKMPP_CMD2STR(VIDIOC_S_FMT); 332 | RKMPP_CMD2STR(VIDIOC_REQBUFS); 333 | RKMPP_CMD2STR(VIDIOC_QUERYBUF); 334 | RKMPP_CMD2STR(VIDIOC_G_FBUF); 335 | RKMPP_CMD2STR(VIDIOC_S_FBUF); 336 | RKMPP_CMD2STR(VIDIOC_OVERLAY); 337 | RKMPP_CMD2STR(VIDIOC_QBUF); 338 | RKMPP_CMD2STR(VIDIOC_EXPBUF); 339 | RKMPP_CMD2STR(VIDIOC_DQBUF); 340 | RKMPP_CMD2STR(VIDIOC_STREAMON); 341 | RKMPP_CMD2STR(VIDIOC_STREAMOFF); 342 | RKMPP_CMD2STR(VIDIOC_G_PARM); 343 | RKMPP_CMD2STR(VIDIOC_S_PARM); 344 | RKMPP_CMD2STR(VIDIOC_G_STD); 345 | RKMPP_CMD2STR(VIDIOC_S_STD); 346 | RKMPP_CMD2STR(VIDIOC_ENUMSTD); 347 | RKMPP_CMD2STR(VIDIOC_ENUMINPUT); 348 | RKMPP_CMD2STR(VIDIOC_G_CTRL); 349 | RKMPP_CMD2STR(VIDIOC_S_CTRL); 350 | RKMPP_CMD2STR(VIDIOC_G_TUNER); 351 | RKMPP_CMD2STR(VIDIOC_S_TUNER); 352 | RKMPP_CMD2STR(VIDIOC_G_AUDIO); 353 | RKMPP_CMD2STR(VIDIOC_S_AUDIO); 354 | RKMPP_CMD2STR(VIDIOC_QUERYCTRL); 355 | RKMPP_CMD2STR(VIDIOC_QUERYMENU); 356 | RKMPP_CMD2STR(VIDIOC_G_INPUT); 357 | RKMPP_CMD2STR(VIDIOC_S_INPUT); 358 | RKMPP_CMD2STR(VIDIOC_G_EDID); 359 | RKMPP_CMD2STR(VIDIOC_S_EDID); 360 | RKMPP_CMD2STR(VIDIOC_G_OUTPUT); 361 | RKMPP_CMD2STR(VIDIOC_S_OUTPUT); 362 | RKMPP_CMD2STR(VIDIOC_ENUMOUTPUT); 363 | RKMPP_CMD2STR(VIDIOC_G_AUDOUT); 364 | RKMPP_CMD2STR(VIDIOC_S_AUDOUT); 365 | RKMPP_CMD2STR(VIDIOC_G_MODULATOR); 366 | RKMPP_CMD2STR(VIDIOC_S_MODULATOR); 367 | RKMPP_CMD2STR(VIDIOC_G_FREQUENCY); 368 | RKMPP_CMD2STR(VIDIOC_S_FREQUENCY); 369 | RKMPP_CMD2STR(VIDIOC_CROPCAP); 370 | RKMPP_CMD2STR(VIDIOC_G_CROP); 371 | RKMPP_CMD2STR(VIDIOC_S_CROP); 372 | RKMPP_CMD2STR(VIDIOC_G_JPEGCOMP); 373 | RKMPP_CMD2STR(VIDIOC_S_JPEGCOMP); 374 | RKMPP_CMD2STR(VIDIOC_QUERYSTD); 375 | RKMPP_CMD2STR(VIDIOC_TRY_FMT); 376 | RKMPP_CMD2STR(VIDIOC_ENUMAUDIO); 377 | RKMPP_CMD2STR(VIDIOC_ENUMAUDOUT); 378 | RKMPP_CMD2STR(VIDIOC_G_PRIORITY); 379 | RKMPP_CMD2STR(VIDIOC_S_PRIORITY); 380 | RKMPP_CMD2STR(VIDIOC_G_SLICED_VBI_CAP); 381 | RKMPP_CMD2STR(VIDIOC_LOG_STATUS); 382 | RKMPP_CMD2STR(VIDIOC_G_EXT_CTRLS); 383 | RKMPP_CMD2STR(VIDIOC_S_EXT_CTRLS); 384 | RKMPP_CMD2STR(VIDIOC_TRY_EXT_CTRLS); 385 | RKMPP_CMD2STR(VIDIOC_ENUM_FRAMESIZES); 386 | RKMPP_CMD2STR(VIDIOC_ENUM_FRAMEINTERVALS); 387 | RKMPP_CMD2STR(VIDIOC_G_ENC_INDEX); 388 | RKMPP_CMD2STR(VIDIOC_ENCODER_CMD); 389 | RKMPP_CMD2STR(VIDIOC_TRY_ENCODER_CMD); 390 | RKMPP_CMD2STR(VIDIOC_DBG_S_REGISTER); 391 | RKMPP_CMD2STR(VIDIOC_DBG_G_REGISTER); 392 | RKMPP_CMD2STR(VIDIOC_S_HW_FREQ_SEEK); 393 | RKMPP_CMD2STR(VIDIOC_S_DV_TIMINGS); 394 | RKMPP_CMD2STR(VIDIOC_G_DV_TIMINGS); 395 | RKMPP_CMD2STR(VIDIOC_DQEVENT); 396 | RKMPP_CMD2STR(VIDIOC_SUBSCRIBE_EVENT); 397 | RKMPP_CMD2STR(VIDIOC_UNSUBSCRIBE_EVENT); 398 | RKMPP_CMD2STR(VIDIOC_CREATE_BUFS); 399 | RKMPP_CMD2STR(VIDIOC_PREPARE_BUF); 400 | RKMPP_CMD2STR(VIDIOC_G_SELECTION); 401 | RKMPP_CMD2STR(VIDIOC_S_SELECTION); 402 | RKMPP_CMD2STR(VIDIOC_DECODER_CMD); 403 | RKMPP_CMD2STR(VIDIOC_TRY_DECODER_CMD); 404 | RKMPP_CMD2STR(VIDIOC_ENUM_DV_TIMINGS); 405 | RKMPP_CMD2STR(VIDIOC_QUERY_DV_TIMINGS); 406 | RKMPP_CMD2STR(VIDIOC_DV_TIMINGS_CAP); 407 | RKMPP_CMD2STR(VIDIOC_ENUM_FREQ_BANDS); 408 | RKMPP_CMD2STR(VIDIOC_DBG_G_CHIP_INFO); 409 | RKMPP_CMD2STR(VIDIOC_QUERY_EXT_CTRL); 410 | default: 411 | return "UNKNOWN"; 412 | } 413 | } 414 | 415 | #define RKMPP_BUFFER_FLAG_HELPER_GET(flag, name) \ 416 | static inline bool rkmpp_buffer_## name(struct rkmpp_buffer *buffer) \ 417 | { \ 418 | return !!(buffer->flags & flag); \ 419 | } 420 | 421 | #define RKMPP_BUFFER_FLAG_HELPER_SET(flag, name) \ 422 | static inline void rkmpp_buffer_set_ ## name(struct rkmpp_buffer *buffer) \ 423 | { \ 424 | LOGV(4, "buffer: %d type: %d\n", buffer->index, buffer->type); \ 425 | if (rkmpp_buffer_ ## name(buffer)) \ 426 | LOGE("buffer: %d type: %d is already " #name "\n", \ 427 | buffer->index, buffer->type); \ 428 | buffer->flags |= flag; \ 429 | } 430 | 431 | #define RKMPP_BUFFER_FLAG_HELPER_CLR(flag, name) \ 432 | static inline void rkmpp_buffer_clr_ ## name(struct rkmpp_buffer *buffer) \ 433 | { \ 434 | LOGV(4, "buffer: %d type: %d\n", buffer->index, buffer->type); \ 435 | if (!rkmpp_buffer_ ## name(buffer)) \ 436 | LOGE("buffer: %d type: %d is not " #name "\n", \ 437 | buffer->index, buffer->type); \ 438 | buffer->flags &= ~flag; \ 439 | } 440 | 441 | #define RKMPP_BUFFER_FLAG_HELPERS(flag, name) \ 442 | RKMPP_BUFFER_FLAG_HELPER_GET(flag, name) \ 443 | RKMPP_BUFFER_FLAG_HELPER_SET(flag, name) \ 444 | RKMPP_BUFFER_FLAG_HELPER_CLR(flag, name) 445 | 446 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_ERROR, error) 447 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_LOCKED, locked) 448 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_EXPORTED, exported) 449 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_QUEUED, queued) 450 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_PENDING, pending) 451 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_AVAILABLE, available) 452 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_KEYFRAME, keyframe) 453 | RKMPP_BUFFER_FLAG_HELPERS(RKMPP_BUFFER_LAST, last) 454 | 455 | void rkmpp_new_frame(struct rkmpp_context *ctx); 456 | int rkmpp_update_poll_event(struct rkmpp_context *ctx); 457 | int rkmpp_querycap(struct rkmpp_context *ctx, struct v4l2_capability *cap); 458 | int rkmpp_enum_fmt(struct rkmpp_context *ctx, struct v4l2_fmtdesc *f); 459 | int rkmpp_enum_framesizes(struct rkmpp_context *ctx, 460 | struct v4l2_frmsizeenum *fsize); 461 | int rkmpp_try_fmt(struct rkmpp_context *ctx, struct v4l2_format *f); 462 | int rkmpp_s_fmt(struct rkmpp_context *ctx, struct v4l2_format *f); 463 | int rkmpp_g_fmt(struct rkmpp_context *ctx, struct v4l2_format *f); 464 | int rkmpp_reqbufs(struct rkmpp_context *ctx, 465 | struct v4l2_requestbuffers *reqbufs); 466 | int rkmpp_querybuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer); 467 | int rkmpp_expbuf(struct rkmpp_context *ctx, 468 | struct v4l2_exportbuffer *expbuf); 469 | int rkmpp_qbuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer); 470 | int rkmpp_dqbuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer); 471 | 472 | void rkmpp_streamon(struct rkmpp_context *ctx); 473 | void rkmpp_streamoff(struct rkmpp_context *ctx); 474 | 475 | void rkmpp_cancel_flushing(struct rkmpp_context *ctx); 476 | void rkmpp_start_flushing(struct rkmpp_context *ctx); 477 | void rkmpp_exit_flushing(struct rkmpp_context *ctx); 478 | void rkmpp_finish_flushing(struct rkmpp_context *ctx, 479 | struct rkmpp_buffer *rkmpp_buffer); 480 | 481 | /* Utils */ 482 | 483 | int rkmpp_to_v4l2_buffer(struct rkmpp_context *ctx, 484 | struct rkmpp_buffer *rkmpp_buffer, 485 | struct v4l2_buffer *buffer); 486 | int rkmpp_from_v4l2_buffer(struct rkmpp_context *ctx, 487 | struct v4l2_buffer *buffer, 488 | struct rkmpp_buffer *rkmpp_buffer); 489 | 490 | #endif //LIBV4L_RKMPP_H 491 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'libv4l-rkmpp', 3 | 'c', 4 | version : '1.8.0', 5 | meson_version : '>=0.50.0', 6 | default_options : ['buildtype=release', 'warning_level=3'], 7 | ) 8 | 9 | libthreads_dep = dependency('threads') 10 | libv4l_dep = dependency('libv4l2') 11 | mpp_dep = dependency('rockchip_mpp') 12 | 13 | cdata = configuration_data() 14 | 15 | if get_option('verbose') 16 | cdata.set10('DEBUG', 1) 17 | endif 18 | 19 | cdata.set('MAX_DEC_WIDTH', get_option('max-dec-width')) 20 | cdata.set('MAX_DEC_HEIGHT', get_option('max-dec-height')) 21 | cdata.set('MAX_ENC_WIDTH', get_option('max-enc-width')) 22 | cdata.set('MAX_ENC_HEIGHT', get_option('max-enc-height')) 23 | 24 | configure_file(output : 'config.h', configuration : cdata) 25 | 26 | deps = [ 27 | libthreads_dep, 28 | libv4l_dep, 29 | mpp_dep, 30 | ] 31 | 32 | srcs = [ 33 | 'src/libv4l-rkmpp.c', 34 | 'src/libv4l-rkmpp-dec.c', 35 | 'src/libv4l-rkmpp-enc.c', 36 | 'src/utils.c', 37 | ] 38 | 39 | shared_library( 40 | 'v4l-rkmpp', 41 | srcs, 42 | include_directories : include_directories('.', 'include'), 43 | dependencies : deps, 44 | install : true, 45 | install_dir : get_option('libdir') / get_option('libv4l2subdir') / 'plugins', 46 | ) 47 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('verbose', type : 'boolean', value : 'false', 2 | description : 'Enable verbose logging (default: false)') 3 | option('libv4l2subdir', type : 'string', value : 'libv4l', 4 | description : 'Libv4l2 library subdir (default: libv4l)') 5 | option('max-dec-width', type : 'integer', min : 1920, value : 3840, 6 | description : 'Max decode width (default: 3840)') 7 | option('max-dec-height', type : 'integer', min : 1080, value : 2160, 8 | description : 'Max decode height (default: 2160)') 9 | option('max-enc-width', type : 'integer', min : 1920, value : 1920, 10 | description : 'Max encode width (default: 1920)') 11 | option('max-enc-height', type : 'integer', min : 1080, value : 1080, 12 | description : 'Max encode height (default: 1080)') 13 | -------------------------------------------------------------------------------- /src/libv4l-rkmpp-dec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | 17 | #include "libv4l-rkmpp-dec.h" 18 | 19 | #define RKMPP_DEC_POLL_TIMEOUT_MS 500 20 | 21 | static struct rkmpp_fmt rkmpp_dec_fmts[] = { 22 | { 23 | .name = "4:2:0 1 plane Y/CbCr", 24 | .fourcc = V4L2_PIX_FMT_NV12, 25 | .num_planes = 1, 26 | .type = MPP_VIDEO_CodingNone, 27 | .format = MPP_FMT_YUV420SP, 28 | .depth = { 12 }, 29 | }, 30 | { 31 | .name = "AV1", 32 | .fourcc = V4L2_PIX_FMT_AV1, 33 | .num_planes = 1, 34 | .type = MPP_VIDEO_CodingAV1, 35 | .format = MPP_FMT_BUTT, 36 | .frmsize = { 37 | .min_width = 48, 38 | .max_width = 7680, 39 | .step_width = RKMPP_MB_DIM, 40 | .min_height = 48, 41 | .max_height = 4320, 42 | .step_height = RKMPP_MB_DIM, 43 | }, 44 | }, 45 | { 46 | .name = "H.265", 47 | .fourcc = V4L2_PIX_FMT_HEVC, 48 | .num_planes = 1, 49 | .type = MPP_VIDEO_CodingHEVC, 50 | .format = MPP_FMT_BUTT, 51 | .frmsize = { 52 | .min_width = 48, 53 | .max_width = 3840, 54 | .step_width = RKMPP_MB_DIM, 55 | .min_height = 48, 56 | .max_height = 2160, 57 | .step_height = RKMPP_MB_DIM, 58 | }, 59 | }, 60 | { 61 | .name = "H.264", 62 | .fourcc = V4L2_PIX_FMT_H264, 63 | .num_planes = 1, 64 | .type = MPP_VIDEO_CodingAVC, 65 | .format = MPP_FMT_BUTT, 66 | .frmsize = { 67 | .min_width = 48, 68 | .max_width = 3840, 69 | .step_width = RKMPP_MB_DIM, 70 | .min_height = 48, 71 | .max_height = 2160, 72 | .step_height = RKMPP_MB_DIM, 73 | }, 74 | }, 75 | { 76 | .name = "VP8", 77 | .fourcc = V4L2_PIX_FMT_VP8, 78 | .num_planes = 1, 79 | .type = MPP_VIDEO_CodingVP8, 80 | .format = MPP_FMT_BUTT, 81 | .frmsize = { 82 | .min_width = 48, 83 | .max_width = 3840, 84 | .step_width = RKMPP_MB_DIM, 85 | .min_height = 48, 86 | .max_height = 2160, 87 | .step_height = RKMPP_MB_DIM, 88 | }, 89 | }, 90 | { 91 | .name = "VP9", 92 | .fourcc = V4L2_PIX_FMT_VP9, 93 | .num_planes = 1, 94 | .type = MPP_VIDEO_CodingVP9, 95 | .format = MPP_FMT_BUTT, 96 | .frmsize = { 97 | .min_width = 48, 98 | .max_width = 3840, 99 | .step_width = RKMPP_SB_DIM, 100 | .min_height = 48, 101 | .max_height = 2176, 102 | .step_height = RKMPP_SB_DIM, 103 | }, 104 | }, 105 | }; 106 | 107 | /* Feed all available packets to mpp */ 108 | static void rkmpp_put_packets(struct rkmpp_dec_context *dec) 109 | { 110 | struct rkmpp_context *ctx = dec->ctx; 111 | struct rkmpp_buffer *rkmpp_buffer; 112 | MppPacket packet; 113 | MPP_RET ret; 114 | 115 | ENTER(); 116 | 117 | pthread_mutex_lock(&ctx->output.queue_mutex); 118 | while (!TAILQ_EMPTY(&ctx->output.pending_buffers)) { 119 | /* Don't feed packets when pausing */ 120 | if (ctx->pausing) 121 | break; 122 | 123 | rkmpp_buffer = TAILQ_FIRST(&ctx->output.pending_buffers); 124 | if (rkmpp_buffer == &ctx->eos_buffer) { 125 | LOGV(1, "processing flush request\n"); 126 | 127 | ctx->pausing = true; 128 | 129 | mpp_packet_init(&packet, NULL, 0); 130 | mpp_packet_set_eos(packet); 131 | } else { 132 | mpp_packet_init(&packet, 133 | mpp_buffer_get_ptr(rkmpp_buffer->rkmpp_buf), 134 | rkmpp_buffer->bytesused); 135 | mpp_packet_set_pts(packet, rkmpp_buffer->timestamp); 136 | } 137 | 138 | ret = ctx->mpi->decode_put_packet(ctx->mpp, packet); 139 | mpp_packet_deinit(&packet); 140 | 141 | if (ret != MPP_OK) 142 | break; 143 | 144 | TAILQ_REMOVE(&ctx->output.pending_buffers, 145 | rkmpp_buffer, entry); 146 | rkmpp_buffer_clr_pending(rkmpp_buffer); 147 | 148 | /* Done with internal EOS buffer */ 149 | if (rkmpp_buffer == &ctx->eos_buffer) 150 | break; 151 | 152 | LOGV(2, "put packet: %d(%" PRIu64 ") len=%d\n", 153 | rkmpp_buffer->index, rkmpp_buffer->timestamp, 154 | rkmpp_buffer->bytesused); 155 | 156 | rkmpp_buffer->bytesused = 0; 157 | 158 | LOGV(2, "return packet: %d\n", 159 | rkmpp_buffer->index); 160 | 161 | TAILQ_INSERT_TAIL(&ctx->output.avail_buffers, 162 | rkmpp_buffer, entry); 163 | rkmpp_buffer_set_available(rkmpp_buffer); 164 | } 165 | pthread_cond_signal(&ctx->output.queue_cond); 166 | pthread_mutex_unlock(&ctx->output.queue_mutex); 167 | 168 | LEAVE(); 169 | } 170 | 171 | static void rkmpp_try_send_eos(struct rkmpp_dec_context *dec) 172 | { 173 | struct rkmpp_context *ctx = dec->ctx; 174 | struct rkmpp_buffer *rkmpp_buffer; 175 | MppBuffer buffer = NULL; 176 | MPP_RET ret; 177 | int index; 178 | 179 | if (!ctx->capture.streaming) 180 | return; 181 | 182 | /* Require an unused buffer for EOS */ 183 | ret = mpp_buffer_get(ctx->capture.external_group, &buffer, 1); 184 | if (ret != MPP_OK) { 185 | LOGV(2, "unable to lock buffer for EOS\n"); 186 | goto err; 187 | } 188 | 189 | index = mpp_buffer_get_index(buffer); 190 | if (index < 0 || index >= (int)ctx->capture.num_buffers) { 191 | LOGE("invalid buffer index for EOS\n"); 192 | goto err; 193 | } 194 | 195 | rkmpp_buffer = &ctx->capture.buffers[index]; 196 | if (buffer != rkmpp_buffer->rkmpp_buf) { 197 | LOGE("invalid buffer for EOS\n"); 198 | goto err; 199 | } 200 | 201 | rkmpp_buffer_set_locked(rkmpp_buffer); 202 | 203 | rkmpp_finish_flushing(ctx, rkmpp_buffer); 204 | 205 | LOGV(1, "return EOS frame: %d\n", rkmpp_buffer->index); 206 | 207 | dec->pending_eos = false; 208 | return; 209 | err: 210 | if (buffer) 211 | mpp_buffer_put(buffer); 212 | 213 | dec->pending_eos = true; 214 | } 215 | 216 | /* Feed all available frames to mpp */ 217 | static void rkmpp_put_frames(struct rkmpp_dec_context *dec) 218 | { 219 | struct rkmpp_context *ctx = dec->ctx; 220 | struct rkmpp_buffer *rkmpp_buffer; 221 | 222 | ENTER(); 223 | 224 | pthread_mutex_lock(&ctx->capture.queue_mutex); 225 | while (!TAILQ_EMPTY(&ctx->capture.pending_buffers)) { 226 | rkmpp_buffer = 227 | TAILQ_FIRST(&ctx->capture.pending_buffers); 228 | TAILQ_REMOVE(&ctx->capture.pending_buffers, 229 | rkmpp_buffer, entry); 230 | rkmpp_buffer_clr_pending(rkmpp_buffer); 231 | 232 | LOGV(2, "put frame: %d fd: %d\n", rkmpp_buffer->index, 233 | rkmpp_buffer->fd); 234 | 235 | mpp_buffer_put(rkmpp_buffer->rkmpp_buf); 236 | rkmpp_buffer_clr_locked(rkmpp_buffer); 237 | } 238 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 239 | 240 | if (dec->pending_eos) 241 | rkmpp_try_send_eos(dec); 242 | 243 | LEAVE(); 244 | } 245 | 246 | static void rkmpp_apply_info_change(struct rkmpp_dec_context *dec, 247 | MppFrame frame) 248 | { 249 | struct rkmpp_context *ctx = dec->ctx; 250 | struct rkmpp_video_info video_info; 251 | 252 | ENTER(); 253 | 254 | memcpy((void *)&video_info, 255 | (void *)&dec->video_info, sizeof(video_info)); 256 | 257 | video_info.mpp_format = mpp_frame_get_fmt(frame); 258 | video_info.width = mpp_frame_get_width(frame); 259 | video_info.height = mpp_frame_get_height(frame); 260 | video_info.hor_stride = mpp_frame_get_hor_stride(frame); 261 | video_info.ver_stride = mpp_frame_get_ver_stride(frame); 262 | video_info.size = mpp_frame_get_buf_size(frame); 263 | video_info.valid = true; 264 | 265 | if (!memcmp((void *)&video_info, 266 | (void *)&dec->video_info, sizeof(video_info))) { 267 | LOGV(1, "ignore unchanged frame info\n"); 268 | 269 | ctx->mpi->control(ctx->mpp, 270 | MPP_DEC_SET_INFO_CHANGE_READY, 271 | NULL); 272 | return; 273 | } 274 | 275 | dec->video_info = video_info; 276 | dec->video_info.dirty = true; 277 | dec->video_info.event = dec->event_subscribed; 278 | 279 | LOGV(1, "frame info changed: %dx%d(%dx%d:%d), mpp format(%d)\n", 280 | dec->video_info.width, dec->video_info.height, 281 | dec->video_info.hor_stride, 282 | dec->video_info.ver_stride, 283 | dec->video_info.size, dec->video_info.mpp_format); 284 | 285 | /* 286 | * Use ver_stride as new height, the visible rect would be returned 287 | * in g_selection. 288 | */ 289 | ctx->capture.format.num_planes = 1; 290 | ctx->capture.format.width = dec->video_info.hor_stride; 291 | ctx->capture.format.height = dec->video_info.ver_stride; 292 | ctx->capture.format.plane_fmt[0].bytesperline = 293 | dec->video_info.hor_stride; 294 | ctx->capture.format.plane_fmt[0].sizeimage = 295 | dec->video_info.size; 296 | 297 | assert(dec->video_info.mpp_format == MPP_FMT_YUV420SP); 298 | ctx->capture.format.pixelformat = V4L2_PIX_FMT_NV12; 299 | 300 | LEAVE(); 301 | } 302 | 303 | /* Feed available packets and frames to mpp */ 304 | /* NOTE: should be locked with either ioctl_mutex or worker_mutex */ 305 | static void rkmpp_feed_mpp(struct rkmpp_dec_context *dec) 306 | { 307 | struct rkmpp_context *ctx = dec->ctx; 308 | 309 | if (!ctx->mpp_streaming) 310 | return; 311 | 312 | rkmpp_put_packets(dec); 313 | rkmpp_put_frames(dec); 314 | } 315 | 316 | static void *decoder_thread_fn(void *data) 317 | { 318 | struct rkmpp_dec_context *dec = data; 319 | struct rkmpp_context *ctx = dec->ctx; 320 | struct rkmpp_buffer *rkmpp_buffer; 321 | MppFrame frame; 322 | MppBuffer buffer; 323 | MPP_RET ret; 324 | int index; 325 | 326 | ENTER(); 327 | 328 | LOGV(1, "ctx(%p): starting decoder thread\n", (void *)ctx); 329 | 330 | while (1) { 331 | pthread_mutex_lock(&ctx->worker_mutex); 332 | 333 | while (!ctx->mpp_streaming) 334 | pthread_cond_wait(&ctx->worker_cond, 335 | &ctx->worker_mutex); 336 | 337 | rkmpp_feed_mpp(dec); 338 | 339 | frame = NULL; 340 | ret = ctx->mpi->decode_get_frame(ctx->mpp, &frame); 341 | 342 | if (ret != MPP_OK || !frame) { 343 | if (ret != MPP_ERR_TIMEOUT) 344 | LOGE("failed to get frame\n"); 345 | 346 | pthread_mutex_unlock(&ctx->worker_mutex); 347 | goto next; 348 | } 349 | 350 | ctx->mpp_produced = true; 351 | 352 | pthread_mutex_unlock(&ctx->worker_mutex); 353 | 354 | pthread_mutex_lock(&ctx->ioctl_mutex); 355 | 356 | if (!ctx->mpp_streaming || !ctx->mpp_produced) 357 | goto next_locked; 358 | 359 | /* Handle info change frame */ 360 | if (mpp_frame_get_info_change(frame)) { 361 | rkmpp_apply_info_change(dec, frame); 362 | 363 | goto next_locked; 364 | } 365 | 366 | /* Handle flushing */ 367 | if (mpp_frame_get_eos(frame)) { 368 | LOGV(1, "seen EOS frame\n"); 369 | 370 | rkmpp_try_send_eos(dec); 371 | goto next_locked; 372 | } 373 | 374 | if (!ctx->capture.streaming) 375 | goto next_locked; 376 | 377 | /* Handle normal frame */ 378 | buffer = mpp_frame_get_buffer(frame); 379 | if (!buffer) { 380 | LOGE("frame(%lld) doesn't have buf\n", 381 | mpp_frame_get_pts(frame)); 382 | 383 | goto next_locked; 384 | } 385 | 386 | mpp_buffer_inc_ref(buffer); 387 | 388 | index = mpp_buffer_get_index(buffer); 389 | rkmpp_buffer = &ctx->capture.buffers[index]; 390 | 391 | rkmpp_buffer->timestamp = mpp_frame_get_pts(frame); 392 | rkmpp_buffer_set_locked(rkmpp_buffer); 393 | 394 | if (mpp_frame_get_errinfo(frame) || 395 | mpp_frame_get_discard(frame)) { 396 | LOGE("frame err or discard\n"); 397 | rkmpp_buffer->bytesused = 0; 398 | rkmpp_buffer_set_error(rkmpp_buffer); 399 | } else { 400 | /* Size of NV12 image */ 401 | rkmpp_buffer->bytesused = dec->video_info.hor_stride * 402 | dec->video_info.ver_stride * 3 / 2; 403 | } 404 | 405 | LOGV(2, "return frame: %d(%" PRIu64 ")\n", 406 | index, rkmpp_buffer->timestamp); 407 | 408 | /* Report new frame to count fps */ 409 | rkmpp_new_frame(ctx); 410 | 411 | pthread_mutex_lock(&ctx->capture.queue_mutex); 412 | TAILQ_INSERT_TAIL(&ctx->capture.avail_buffers, 413 | rkmpp_buffer, entry); 414 | rkmpp_buffer_set_available(rkmpp_buffer); 415 | pthread_cond_signal(&ctx->capture.queue_cond); 416 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 417 | next_locked: 418 | pthread_mutex_unlock(&ctx->ioctl_mutex); 419 | next: 420 | /* Update poll event after every loop */ 421 | pthread_mutex_lock(&ctx->ioctl_mutex); 422 | rkmpp_update_poll_event(ctx); 423 | pthread_mutex_unlock(&ctx->ioctl_mutex); 424 | 425 | if (frame) 426 | mpp_frame_deinit(&frame); 427 | } 428 | 429 | LEAVE(); 430 | return NULL; 431 | } 432 | 433 | static int rkmpp_dec_qbuf(struct rkmpp_dec_context *dec, 434 | struct v4l2_buffer *buffer) 435 | { 436 | struct rkmpp_context *ctx = dec->ctx; 437 | int ret; 438 | 439 | ENTER(); 440 | 441 | ret = rkmpp_qbuf(ctx, buffer); 442 | if (ret < 0) 443 | RETURN_ERR(errno, -1); 444 | 445 | rkmpp_feed_mpp(dec); 446 | 447 | LEAVE(); 448 | return ret; 449 | } 450 | 451 | static int rkmpp_dec_g_fmt(struct rkmpp_dec_context *dec, 452 | struct v4l2_format *f) 453 | { 454 | struct rkmpp_context *ctx = dec->ctx; 455 | int ret; 456 | 457 | ENTER(); 458 | 459 | LOGV(1, "f->type = %d\n", f->type); 460 | 461 | /* The chromium expected EINVAL when resolution not available */ 462 | if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && 463 | !dec->video_info.valid) { 464 | LOGV(1, "cannot provide resolution yet\n"); 465 | RETURN_ERR(EINVAL, -1); 466 | } 467 | 468 | ret = rkmpp_g_fmt(dec->ctx, f); 469 | 470 | LEAVE(); 471 | return ret; 472 | } 473 | 474 | static int rkmpp_dec_subscribe_event(struct rkmpp_dec_context *dec, 475 | struct v4l2_event_subscription *sub, 476 | bool subscribe) 477 | { 478 | struct rkmpp_context *ctx = dec->ctx; 479 | 480 | ENTER(); 481 | 482 | if (sub->type != V4L2_EVENT_SOURCE_CHANGE) { 483 | LOGE("unsupported event type: %x\n", sub->type); 484 | RETURN_ERR(EINVAL, -1); 485 | } 486 | 487 | dec->event_subscribed = subscribe; 488 | 489 | LEAVE(); 490 | return 0; 491 | } 492 | 493 | static int rkmpp_dec_dqevent(struct rkmpp_dec_context *dec, 494 | struct v4l2_event *event) 495 | { 496 | struct rkmpp_context *ctx = dec->ctx; 497 | 498 | ENTER(); 499 | 500 | if (!dec->event_subscribed || !dec->video_info.event) { 501 | LOGV(4, "no available event\n"); 502 | errno = ENOENT; 503 | return -1; 504 | } 505 | 506 | event->type = V4L2_EVENT_SOURCE_CHANGE; 507 | event->u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION; 508 | dec->video_info.event = false; 509 | LOGV(1, "dequeue resolution change event\n"); 510 | 511 | /* The chromium's stateful decoder needs a last buffer for flushing */ 512 | if (ctx->mpp_streaming) 513 | rkmpp_try_send_eos(dec); 514 | 515 | LEAVE(); 516 | return 0; 517 | } 518 | 519 | static int rkmpp_dec_streamon(struct rkmpp_dec_context *dec, 520 | enum v4l2_buf_type *type) 521 | { 522 | struct rkmpp_context *ctx = dec->ctx; 523 | struct rkmpp_buf_queue *queue; 524 | MppPollType poll_type; 525 | uint32_t split; 526 | MPP_RET ret; 527 | 528 | ENTER(); 529 | 530 | queue = rkmpp_get_queue(ctx, *type); 531 | if (!queue) 532 | RETURN_ERR(errno, -1); 533 | 534 | if (queue->streaming) 535 | goto out; 536 | queue->streaming = true; 537 | 538 | LOGV(1, "queue(%d) start streaming\n", *type); 539 | 540 | /* Commit pending info change to mpp */ 541 | if (ctx->mpp_streaming && dec->video_info.dirty && 542 | *type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { 543 | LOGV(1, "send info change ready\n"); 544 | 545 | ctx->mpi->control(ctx->mpp, 546 | MPP_DEC_SET_INFO_CHANGE_READY, NULL); 547 | dec->video_info.dirty = false; 548 | } 549 | 550 | if (ctx->mpp_streaming) 551 | goto out; 552 | 553 | LOGV(1, "mpp initializing for %s\n", ctx->output.rkmpp_format->name); 554 | 555 | ret = mpp_create(&ctx->mpp, &ctx->mpi); 556 | if (ret != MPP_OK) { 557 | LOGE("failed to create mpp\n"); 558 | errno = ENOMEM; 559 | goto err; 560 | } 561 | 562 | ret = 1; 563 | ctx->mpi->control(ctx->mpp, MPP_DEC_SET_PARSER_FAST_MODE, &ret); 564 | 565 | ret = mpp_init(ctx->mpp, MPP_CTX_DEC, 566 | ctx->output.rkmpp_format->type); 567 | if (ret != MPP_OK) { 568 | LOGE("failed to init mpp\n"); 569 | goto err_destroy_mpp; 570 | } 571 | 572 | /* The chromium will do the split */ 573 | split = 0; 574 | ret = ctx->mpi->control(ctx->mpp, 575 | MPP_DEC_SET_PARSER_SPLIT_MODE, 576 | &split); 577 | if (ret != MPP_OK) { 578 | LOGE("failed to set mpp split mode\n"); 579 | goto err_destroy_mpp; 580 | } 581 | 582 | /* Enable timeout mode to avoid hang during get_frame */ 583 | poll_type = RKMPP_DEC_POLL_TIMEOUT_MS; 584 | ret = ctx->mpi->control(ctx->mpp, MPP_SET_OUTPUT_TIMEOUT, 585 | (MppParam)&poll_type); 586 | if (ret != MPP_OK) { 587 | LOGE("failed to set mpp timeout\n"); 588 | goto err_destroy_mpp; 589 | } 590 | 591 | /* Use external buffer mode for capture queue */ 592 | ret = ctx->mpi->control(ctx->mpp, 593 | MPP_DEC_SET_EXT_BUF_GROUP, 594 | ctx->capture.external_group); 595 | if (ret != MPP_OK) { 596 | LOGE("failed to set buffer group\n"); 597 | goto err_destroy_mpp; 598 | } 599 | 600 | rkmpp_streamon(ctx); 601 | out: 602 | LEAVE(); 603 | return 0; 604 | err_destroy_mpp: 605 | ctx->mpi->reset(ctx->mpp); 606 | mpp_destroy(ctx->mpp); 607 | err: 608 | rkmpp_reset_queue(ctx, queue); 609 | RETURN_ERR(EPIPE, -1); 610 | } 611 | 612 | static int rkmpp_dec_streamoff(struct rkmpp_dec_context *dec, 613 | enum v4l2_buf_type *type) 614 | { 615 | struct rkmpp_context *ctx = dec->ctx; 616 | struct rkmpp_buf_queue *queue; 617 | 618 | ENTER(); 619 | 620 | queue = rkmpp_get_queue(ctx, *type); 621 | if (!queue) 622 | RETURN_ERR(errno, -1); 623 | 624 | if (!queue->streaming) 625 | goto out; 626 | 627 | LOGV(1, "queue(%d) stop streaming\n", *type); 628 | 629 | rkmpp_reset_queue(ctx, queue); 630 | 631 | if (queue == &ctx->capture) 632 | dec->pending_eos = false; 633 | 634 | /* Stop mpp streaming only when all queues stopped */ 635 | if (ctx->mpp_streaming && 636 | !ctx->output.streaming && !ctx->capture.streaming) 637 | rkmpp_streamoff(ctx); 638 | out: 639 | LEAVE(); 640 | return 0; 641 | } 642 | 643 | static int rkmpp_dec_g_selection(struct rkmpp_dec_context *dec, 644 | struct v4l2_selection *selection) 645 | { 646 | struct rkmpp_context *ctx = dec->ctx; 647 | 648 | ENTER(); 649 | 650 | if ((selection->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && 651 | selection->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) || 652 | selection->target != V4L2_SEL_TGT_COMPOSE) { 653 | LOGE("invalid type or target\n"); 654 | RETURN_ERR(EINVAL, -1); 655 | } 656 | 657 | /* The chromium uses g_selection to get visible rect */ 658 | if (!dec->video_info.valid) { 659 | LOGV(1, "cannot provide visible resolution yet\n"); 660 | RETURN_ERR(EBUSY, -1); 661 | } 662 | 663 | /* Return visible rect */ 664 | selection->r.top = selection->r.left = 0; 665 | selection->r.width = dec->video_info.width; 666 | selection->r.height = dec->video_info.height; 667 | 668 | LOGV(1, "visible rect: %dx%d\n", 669 | selection->r.width, selection->r.height); 670 | 671 | LEAVE(); 672 | return 0; 673 | } 674 | 675 | static int rkmpp_dec_g_ctrl(struct rkmpp_dec_context *dec, 676 | struct v4l2_control *ctrl) 677 | { 678 | struct rkmpp_context *ctx = dec->ctx; 679 | 680 | ENTER(); 681 | 682 | if (ctrl->id != V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) { 683 | LOGE("only support V4L2_CID_MIN_BUFFERS_FOR_CAPTURE now\n"); 684 | RETURN_ERR(EINVAL, -1); 685 | } 686 | 687 | if (!ctx->output.rkmpp_format) { 688 | LOGE("cannot get min buffers before s_fmt\n"); 689 | RETURN_ERR(EBUSY, -1); 690 | } 691 | 692 | /* Information provided by Herman Chen */ 693 | switch (ctx->output.rkmpp_format->fourcc) { 694 | case V4L2_PIX_FMT_H264: 695 | case V4L2_PIX_FMT_HEVC: 696 | ctrl->value = 20; 697 | break; 698 | case V4L2_PIX_FMT_AV1: 699 | ctrl->value = 12; 700 | break; 701 | case V4L2_PIX_FMT_VP9: 702 | ctrl->value = 12; 703 | break; 704 | case V4L2_PIX_FMT_VP8: 705 | ctrl->value = 8; 706 | break; 707 | default: 708 | LOGE("unsupported format\n"); 709 | RETURN_ERR(EINVAL, -1); 710 | } 711 | 712 | /* The chromium would try to require at least 2 extra buffers */ 713 | ctrl->value -= 2; 714 | 715 | LEAVE(); 716 | return 0; 717 | } 718 | 719 | static int rkmpp_dec_g_ext_ctrls(struct rkmpp_dec_context *dec, 720 | struct v4l2_ext_controls *ext_ctrls) 721 | { 722 | struct rkmpp_context *ctx = dec->ctx; 723 | struct v4l2_control ctrl; 724 | unsigned int i; 725 | 726 | ENTER(); 727 | 728 | for (i = 0; i < ext_ctrls->count; i++) { 729 | struct v4l2_ext_control *ext_ctrl = &ext_ctrls->controls[i]; 730 | 731 | ctrl.id = ext_ctrl->id; 732 | if (rkmpp_dec_g_ctrl(dec, &ctrl) < 0) 733 | return -1; 734 | 735 | ext_ctrl->value = ctrl.value; 736 | } 737 | 738 | LEAVE(); 739 | return 0; 740 | } 741 | 742 | static int rkmpp_dec_queryctrl(struct rkmpp_dec_context *dec, 743 | struct v4l2_queryctrl *query_ctrl) 744 | { 745 | struct rkmpp_context *ctx = dec->ctx; 746 | 747 | ENTER(); 748 | 749 | switch (query_ctrl->id) { 750 | case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 751 | query_ctrl->minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; 752 | query_ctrl->maximum = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; 753 | break; 754 | case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: 755 | query_ctrl->minimum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; 756 | query_ctrl->maximum = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10; 757 | break; 758 | case V4L2_CID_MPEG_VIDEO_AV1_PROFILE: 759 | query_ctrl->minimum = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN; 760 | query_ctrl->maximum = query_ctrl->minimum; 761 | break; 762 | case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: 763 | query_ctrl->minimum = V4L2_MPEG_VIDEO_VP8_PROFILE_0; 764 | query_ctrl->maximum = query_ctrl->minimum; 765 | break; 766 | case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: 767 | query_ctrl->minimum = V4L2_MPEG_VIDEO_VP9_PROFILE_0; 768 | query_ctrl->maximum = V4L2_MPEG_VIDEO_VP9_PROFILE_2; 769 | break; 770 | /* TODO: fill info for other supported ctrls */ 771 | default: 772 | LOGV(1, "unsupported ctrl: %x\n", query_ctrl->id); 773 | RETURN_ERR(EINVAL, -1); 774 | } 775 | 776 | LEAVE(); 777 | return 0; 778 | } 779 | 780 | static int rkmpp_dec_querymenu(struct rkmpp_dec_context *dec, 781 | struct v4l2_querymenu *query_menu) 782 | { 783 | struct rkmpp_context *ctx = dec->ctx; 784 | 785 | ENTER(); 786 | 787 | switch (query_menu->id) { 788 | case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 789 | switch (query_menu->index) { 790 | case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: 791 | case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: 792 | case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: 793 | case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10: 794 | break; 795 | default: 796 | LOGV(1, "unsupported H264 profile: %x\n", 797 | query_menu->index); 798 | RETURN_ERR(EINVAL, -1); 799 | } 800 | break; 801 | case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: 802 | switch (query_menu->index) { 803 | case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: 804 | case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: 805 | break; 806 | default: 807 | LOGV(1, "unsupported HEVC profile: %x\n", 808 | query_menu->index); 809 | RETURN_ERR(EINVAL, -1); 810 | } 811 | break; 812 | case V4L2_CID_MPEG_VIDEO_AV1_PROFILE: 813 | if (query_menu->index != V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN) { 814 | LOGV(1, "unsupported VP8 profile: %x\n", 815 | query_menu->index); 816 | RETURN_ERR(EINVAL, -1); 817 | } 818 | break; 819 | case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: 820 | if (query_menu->index != V4L2_MPEG_VIDEO_VP8_PROFILE_0) { 821 | LOGV(1, "unsupported VP8 profile: %x\n", 822 | query_menu->index); 823 | RETURN_ERR(EINVAL, -1); 824 | } 825 | break; 826 | case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: 827 | switch (query_menu->index) { 828 | case V4L2_MPEG_VIDEO_VP9_PROFILE_0: 829 | case V4L2_MPEG_VIDEO_VP9_PROFILE_2: 830 | break; 831 | default: 832 | LOGV(1, "unsupported VP9 profile: %x\n", 833 | query_menu->index); 834 | RETURN_ERR(EINVAL, -1); 835 | } 836 | break; 837 | default: 838 | LOGV(1, "unsupported menu: %x\n", query_menu->id); 839 | RETURN_ERR(EINVAL, -1); 840 | } 841 | 842 | LEAVE(); 843 | return 0; 844 | } 845 | 846 | static int rkmpp_try_dec_cmd(struct rkmpp_dec_context *dec, 847 | struct v4l2_decoder_cmd *cmd) 848 | { 849 | struct rkmpp_context *ctx = dec->ctx; 850 | 851 | ENTER(); 852 | 853 | if (cmd->cmd != V4L2_DEC_CMD_START && cmd->cmd != V4L2_DEC_CMD_STOP) { 854 | LOGE("unsupported cmd: %x\n", cmd->cmd); 855 | RETURN_ERR(EINVAL, -1); 856 | } 857 | 858 | LEAVE(); 859 | return 0; 860 | } 861 | 862 | static int rkmpp_dec_cmd(struct rkmpp_dec_context *dec, 863 | struct v4l2_decoder_cmd *cmd) 864 | { 865 | struct rkmpp_context *ctx = dec->ctx; 866 | 867 | ENTER(); 868 | 869 | if (cmd->cmd == V4L2_DEC_CMD_START) { 870 | LOGV(1, "handle start decoding cmd\n"); 871 | 872 | rkmpp_exit_flushing(ctx); 873 | } else if (cmd->cmd == V4L2_DEC_CMD_STOP) { 874 | LOGV(1, "handle stop decoding cmd\n"); 875 | 876 | rkmpp_start_flushing(ctx); 877 | } else { 878 | LOGE("unsupported cmd: %x\n", cmd->cmd); 879 | RETURN_ERR(EINVAL, -1); 880 | } 881 | 882 | LEAVE(); 883 | return 0; 884 | } 885 | 886 | bool rkmpp_dec_has_event(void *data) 887 | { 888 | struct rkmpp_dec_context *dec = data; 889 | 890 | return dec->video_info.event; 891 | } 892 | 893 | void *rkmpp_dec_init(struct rkmpp_context *ctx) 894 | { 895 | struct rkmpp_dec_context *dec; 896 | MPP_RET ret; 897 | 898 | ENTER(); 899 | 900 | dec = (struct rkmpp_dec_context *) 901 | calloc(1, sizeof(struct rkmpp_dec_context)); 902 | if (!dec) 903 | RETURN_ERR(ENOMEM, NULL); 904 | 905 | /* Using external buffer mode to limit buffers */ 906 | ret = mpp_buffer_group_get_external(&ctx->capture.external_group, 907 | MPP_BUFFER_TYPE_DRM); 908 | if (ret != MPP_OK) { 909 | LOGE("failed to use mpp ext drm buf group\n"); 910 | errno = ENODEV; 911 | goto err_free_dec; 912 | } 913 | 914 | ctx->formats = rkmpp_dec_fmts; 915 | ctx->num_formats = ARRAY_SIZE(rkmpp_dec_fmts); 916 | dec->ctx = ctx; 917 | 918 | if (!ctx->max_width) 919 | ctx->max_width = MAX_DEC_WIDTH; 920 | 921 | if (!ctx->max_height) 922 | ctx->max_height = MAX_DEC_HEIGHT; 923 | 924 | pthread_create(&ctx->worker_thread, NULL, 925 | decoder_thread_fn, dec); 926 | 927 | LEAVE(); 928 | return dec; 929 | err_free_dec: 930 | free(dec); 931 | RETURN_ERR(errno, NULL); 932 | } 933 | 934 | void rkmpp_dec_deinit(void *data) 935 | { 936 | struct rkmpp_dec_context *dec = data; 937 | struct rkmpp_context *ctx = dec->ctx; 938 | 939 | ENTER(); 940 | 941 | free(dec); 942 | 943 | LEAVE(); 944 | } 945 | 946 | int rkmpp_dec_ioctl(void *data, unsigned long cmd, void *arg) 947 | { 948 | struct rkmpp_dec_context *dec = data; 949 | struct rkmpp_context *ctx = dec->ctx; 950 | int ret; 951 | 952 | ENTER(); 953 | 954 | switch (cmd) { 955 | 956 | /* Common ioctls */ 957 | case VIDIOC_QUERYCAP: 958 | ret = rkmpp_querycap(ctx, arg); 959 | break; 960 | case VIDIOC_ENUM_FMT: 961 | ret = rkmpp_enum_fmt(ctx, arg); 962 | break; 963 | case VIDIOC_ENUM_FRAMESIZES: 964 | ret = rkmpp_enum_framesizes(ctx, arg); 965 | break; 966 | case VIDIOC_TRY_FMT: 967 | ret = rkmpp_try_fmt(ctx, arg); 968 | break; 969 | case VIDIOC_S_FMT: 970 | ret = rkmpp_s_fmt(ctx, arg); 971 | break; 972 | case VIDIOC_REQBUFS: 973 | ret = rkmpp_reqbufs(ctx, arg); 974 | break; 975 | case VIDIOC_QUERYBUF: 976 | ret = rkmpp_querybuf(ctx, arg); 977 | break; 978 | case VIDIOC_EXPBUF: 979 | ret = rkmpp_expbuf(ctx, arg); 980 | break; 981 | case VIDIOC_DQBUF: 982 | ret = rkmpp_dqbuf(ctx, arg); 983 | break; 984 | 985 | /* Decoder special ioctls */ 986 | case VIDIOC_QBUF: 987 | ret = rkmpp_dec_qbuf(dec, arg); 988 | break; 989 | case VIDIOC_G_FMT: 990 | ret = rkmpp_dec_g_fmt(dec, arg); 991 | break; 992 | case VIDIOC_SUBSCRIBE_EVENT: 993 | ret = rkmpp_dec_subscribe_event(dec, arg, true); 994 | break; 995 | case VIDIOC_UNSUBSCRIBE_EVENT: 996 | ret = rkmpp_dec_subscribe_event(dec, arg, false); 997 | break; 998 | case VIDIOC_DQEVENT: 999 | ret = rkmpp_dec_dqevent(dec, arg); 1000 | break; 1001 | case VIDIOC_STREAMON: 1002 | ret = rkmpp_dec_streamon(dec, arg); 1003 | break; 1004 | case VIDIOC_STREAMOFF: 1005 | ret = rkmpp_dec_streamoff(dec, arg); 1006 | break; 1007 | case VIDIOC_G_SELECTION: 1008 | ret = rkmpp_dec_g_selection(dec, arg); 1009 | break; 1010 | case VIDIOC_G_CTRL: 1011 | ret = rkmpp_dec_g_ctrl(dec, arg); 1012 | break; 1013 | case VIDIOC_G_EXT_CTRLS: 1014 | ret = rkmpp_dec_g_ext_ctrls(dec, arg); 1015 | break; 1016 | case VIDIOC_QUERYCTRL: 1017 | ret = rkmpp_dec_queryctrl(dec, arg); 1018 | break; 1019 | case VIDIOC_QUERYMENU: 1020 | ret = rkmpp_dec_querymenu(dec, arg); 1021 | break; 1022 | case VIDIOC_TRY_DECODER_CMD: 1023 | ret = rkmpp_try_dec_cmd(dec, arg); 1024 | break; 1025 | case VIDIOC_DECODER_CMD: 1026 | ret = rkmpp_dec_cmd(dec, arg); 1027 | break; 1028 | default: 1029 | LOGV(1, "unsupported ioctl cmd: %s(%lu)!\n", 1030 | rkmpp_cmd2str(cmd), cmd); 1031 | RETURN_ERR(ENOTTY, -1); 1032 | } 1033 | 1034 | LEAVE(); 1035 | return ret; 1036 | } 1037 | -------------------------------------------------------------------------------- /src/libv4l-rkmpp-enc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include "libv4l-rkmpp-enc.h" 19 | 20 | #ifndef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME 21 | #define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_MPEG_BASE+229) 22 | #endif 23 | 24 | #ifndef V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR 25 | #define V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (V4L2_CID_MPEG_BASE + 644) 26 | #endif 27 | 28 | #ifndef V4L2_CID_MPEG_VIDEO_BITRATE_MODE 29 | #define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_MPEG_BASE+206) 30 | enum v4l2_mpeg_video_bitrate_mode { 31 | V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0, 32 | V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1, 33 | V4L2_MPEG_VIDEO_BITRATE_MODE_CQ = 2, 34 | }; 35 | #endif 36 | 37 | #define RKMPP_ENC_POLL_TIMEOUT_MS 100 38 | 39 | static struct rkmpp_fmt rkmpp_enc_fmts[] = { 40 | { 41 | .name = "4:2:0 3 plane Y/Cb/Cr", 42 | .fourcc = V4L2_PIX_FMT_YUV420M, 43 | .num_planes = 3, 44 | .type = MPP_VIDEO_CodingNone, 45 | .format = MPP_FMT_YUV420P, 46 | .depth = { 8, 4, 4 }, 47 | }, 48 | { 49 | .name = "4:2:0 2 plane Y/CbCr", 50 | .fourcc = V4L2_PIX_FMT_NV12M, 51 | .num_planes = 2, 52 | .type = MPP_VIDEO_CodingNone, 53 | .format = MPP_FMT_YUV420SP, 54 | .depth = { 8, 8 }, 55 | }, 56 | { 57 | .name = "4:2:2 1 plane YUYV", 58 | .fourcc = V4L2_PIX_FMT_YUYV, 59 | .num_planes = 1, 60 | .type = MPP_VIDEO_CodingNone, 61 | .format = MPP_FMT_YUV422_YUYV, 62 | .depth = { 16 }, 63 | }, 64 | { 65 | .name = "4:2:2 1 plane UYVY", 66 | .fourcc = V4L2_PIX_FMT_UYVY, 67 | .num_planes = 1, 68 | .type = MPP_VIDEO_CodingNone, 69 | .format = MPP_FMT_YUV422_UYVY, 70 | .depth = { 16 }, 71 | }, 72 | { 73 | .name = "H.264", 74 | .fourcc = V4L2_PIX_FMT_H264, 75 | .num_planes = 1, 76 | .type = MPP_VIDEO_CodingAVC, 77 | .format = MPP_FMT_BUTT, 78 | .frmsize = { 79 | .min_width = 96, 80 | .max_width = 1920, 81 | .step_width = RKMPP_MB_DIM, 82 | .min_height = 96, 83 | .max_height = 1088, 84 | .step_height = RKMPP_MB_DIM, 85 | }, 86 | }, 87 | { 88 | .name = "VP8", 89 | .fourcc = V4L2_PIX_FMT_VP8, 90 | .num_planes = 1, 91 | .type = MPP_VIDEO_CodingVP8, 92 | .format = MPP_FMT_BUTT, 93 | .frmsize = { 94 | .min_width = 96, 95 | .max_width = 1920, 96 | .step_width = RKMPP_MB_DIM, 97 | .min_height = 96, 98 | .max_height = 1088, 99 | .step_height = RKMPP_MB_DIM, 100 | }, 101 | }, 102 | { 103 | .name = "MJPEG", 104 | .fourcc = V4L2_PIX_FMT_MJPEG, 105 | .num_planes = 1, 106 | .type = MPP_VIDEO_CodingMJPEG, 107 | .format = MPP_FMT_BUTT, 108 | .frmsize = { 109 | .min_width = 96, 110 | .max_width = 1920, 111 | .step_width = RKMPP_MB_DIM, 112 | .min_height = 96, 113 | .max_height = 1088, 114 | .step_height = RKMPP_MB_DIM, 115 | } 116 | } 117 | }; 118 | 119 | static int rkmpp_enc_apply_rc_cfg(struct rkmpp_enc_context *enc); 120 | 121 | static int rkmpp_put_frame(struct rkmpp_enc_context *enc) 122 | { 123 | struct rkmpp_context *ctx = enc->ctx; 124 | const struct rkmpp_fmt *rkmpp_fmt = ctx->output.rkmpp_format; 125 | struct rkmpp_buffer *rkmpp_buffer; 126 | MppFrame frame; 127 | MPP_RET ret; 128 | 129 | ENTER(); 130 | 131 | pthread_mutex_lock(&ctx->output.queue_mutex); 132 | rkmpp_buffer = TAILQ_FIRST(&ctx->output.pending_buffers); 133 | pthread_mutex_unlock(&ctx->output.queue_mutex); 134 | 135 | /* Handle flushing */ 136 | if (rkmpp_buffer == &ctx->eos_buffer) { 137 | LOGV(1, "processing flush request\n"); 138 | 139 | ctx->pausing = true; 140 | 141 | pthread_mutex_lock(&ctx->output.queue_mutex); 142 | TAILQ_REMOVE(&ctx->output.pending_buffers, rkmpp_buffer, entry); 143 | rkmpp_buffer_clr_pending(rkmpp_buffer); 144 | pthread_mutex_unlock(&ctx->output.queue_mutex); 145 | 146 | /* Pick an unused buffer for EOS */ 147 | pthread_mutex_lock(&ctx->capture.queue_mutex); 148 | rkmpp_buffer = TAILQ_FIRST(&ctx->capture.pending_buffers); 149 | TAILQ_REMOVE(&ctx->capture.pending_buffers, 150 | rkmpp_buffer, entry); 151 | rkmpp_buffer_clr_pending(rkmpp_buffer); 152 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 153 | 154 | rkmpp_finish_flushing(ctx, rkmpp_buffer); 155 | 156 | LOGV(1, "return EOS packet: %d\n", rkmpp_buffer->index); 157 | 158 | LEAVE(); 159 | return 0; 160 | } 161 | 162 | ret = mpp_frame_init(&frame); 163 | if (ret != MPP_OK) { 164 | LOGE("failed to init frame\n"); 165 | return -1; 166 | } 167 | 168 | mpp_frame_set_width(frame, enc->width); 169 | mpp_frame_set_height(frame, enc->height); 170 | mpp_frame_set_hor_stride(frame, enc->hstride); 171 | mpp_frame_set_ver_stride(frame, enc->vstride); 172 | mpp_frame_set_fmt(frame, rkmpp_fmt->format); 173 | 174 | mpp_frame_set_buffer(frame, rkmpp_buffer->rkmpp_buf); 175 | 176 | /* Use pts to track frame buffer */ 177 | mpp_frame_set_pts(frame, rkmpp_buffer->index); 178 | 179 | ret = ctx->mpi->encode_put_frame(ctx->mpp, frame); 180 | mpp_frame_deinit(&frame); 181 | 182 | if (ret != MPP_OK) { 183 | LOGE("failed to put frame\n"); 184 | return -1; 185 | } 186 | 187 | pthread_mutex_lock(&ctx->output.queue_mutex); 188 | TAILQ_REMOVE(&ctx->output.pending_buffers, rkmpp_buffer, entry); 189 | rkmpp_buffer_clr_pending(rkmpp_buffer); 190 | pthread_mutex_unlock(&ctx->output.queue_mutex); 191 | 192 | LOGV(2, "put frame: %d(%" PRIu64 ")\n", 193 | rkmpp_buffer->index, rkmpp_buffer->timestamp); 194 | 195 | rkmpp_buffer->bytesused = 0; 196 | 197 | LEAVE(); 198 | return 0; 199 | } 200 | 201 | static void rkmpp_packet_to_buffer(MppPacket packet, 202 | struct rkmpp_buffer *rkmpp_buffer) 203 | { 204 | char *src = mpp_packet_get_pos(packet); 205 | char *dst = mpp_buffer_get_ptr(rkmpp_buffer->rkmpp_buf); 206 | uint32_t src_size = mpp_packet_get_length(packet); 207 | uint32_t offset = rkmpp_buffer->bytesused; 208 | uint32_t dst_size = rkmpp_buffer->size; 209 | 210 | if (src_size > dst_size - offset) { 211 | LOGE("packet overflow! %d > (%d - %d)\n", 212 | src_size, dst_size, offset); 213 | src_size = dst_size - offset; 214 | } 215 | 216 | memcpy(dst + offset, src, src_size); 217 | rkmpp_buffer->bytesused += src_size; 218 | } 219 | 220 | static MppPacket rkmpp_get_header(struct rkmpp_enc_context *enc) 221 | { 222 | struct rkmpp_context *ctx = enc->ctx; 223 | MppPacket tmp, header; 224 | MPP_RET ret; 225 | uint8_t buf[MAX_HEADER_BYTES]; 226 | 227 | ENTER(); 228 | 229 | LOGV(1, "get header packet\n"); 230 | 231 | mpp_packet_init(&tmp, buf, sizeof(buf)); 232 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_HDR_SYNC, tmp); 233 | if (ret != MPP_OK) { 234 | LOGE("failed to get header\n"); 235 | mpp_packet_deinit(&tmp); 236 | return NULL; 237 | } 238 | 239 | mpp_packet_copy_init(&header, tmp); 240 | mpp_packet_deinit(&tmp); 241 | LOGV(1, "header packet size: %ld\n", mpp_packet_get_length(header)); 242 | 243 | LEAVE(); 244 | return header; 245 | } 246 | 247 | static void rkmpp_send_header(struct rkmpp_enc_context *enc) 248 | { 249 | struct rkmpp_context *ctx = enc->ctx; 250 | struct rkmpp_buffer *rkmpp_buffer; 251 | 252 | ENTER(); 253 | 254 | LOGV(1, "sending header\n"); 255 | 256 | pthread_mutex_lock(&ctx->capture.queue_mutex); 257 | rkmpp_buffer = TAILQ_FIRST(&ctx->capture.pending_buffers); 258 | TAILQ_REMOVE(&ctx->capture.pending_buffers, 259 | rkmpp_buffer, entry); 260 | rkmpp_buffer_clr_pending(rkmpp_buffer); 261 | 262 | rkmpp_buffer->bytesused = 0; 263 | rkmpp_packet_to_buffer(enc->header, rkmpp_buffer); 264 | 265 | TAILQ_INSERT_TAIL(&ctx->capture.avail_buffers, 266 | rkmpp_buffer, entry); 267 | rkmpp_buffer_set_available(rkmpp_buffer); 268 | pthread_cond_signal(&ctx->capture.queue_cond); 269 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 270 | 271 | LEAVE(); 272 | } 273 | 274 | static void *encoder_thread_fn(void *data) 275 | { 276 | struct rkmpp_enc_context *enc = data; 277 | struct rkmpp_context *ctx = enc->ctx; 278 | struct rkmpp_buffer *rkmpp_buffer, *frame_buffer; 279 | MppPacket packet = NULL; 280 | MppMeta meta; 281 | MPP_RET ret; 282 | int index, is_keyframe; 283 | 284 | ENTER(); 285 | 286 | LOGV(1, "ctx(%p): starting encoder thread\n", (void *)ctx); 287 | 288 | while (1) { 289 | pthread_mutex_lock(&ctx->worker_mutex); 290 | 291 | while (!ctx->mpp_streaming) 292 | pthread_cond_wait(&ctx->worker_cond, 293 | &ctx->worker_mutex); 294 | 295 | /* Store header before 1st frame */ 296 | if (enc->needs_header && !enc->header) 297 | enc->header = rkmpp_get_header(enc); 298 | 299 | /* Wait for buffers */ 300 | while (ctx->pausing || 301 | TAILQ_EMPTY(&ctx->capture.pending_buffers) || 302 | TAILQ_EMPTY(&ctx->output.pending_buffers)) 303 | pthread_cond_wait(&ctx->worker_cond, 304 | &ctx->worker_mutex); 305 | 306 | if (enc->type == H264 && enc->needs_header && 307 | enc->h264.separate_header) { 308 | if (enc->header) { 309 | /* Send separate header before 1st frame */ 310 | rkmpp_send_header(enc); 311 | enc->needs_header = false; 312 | } 313 | pthread_mutex_unlock(&ctx->worker_mutex); 314 | goto next; 315 | } 316 | 317 | if (rkmpp_put_frame(enc) < 0) { 318 | pthread_mutex_unlock(&ctx->worker_mutex); 319 | continue; 320 | } 321 | 322 | packet = NULL; 323 | while (!packet) { 324 | ret = ctx->mpi->encode_get_packet(ctx->mpp, &packet); 325 | if (ret != MPP_OK) { 326 | LOGE("failed to get packet\n"); 327 | pthread_mutex_unlock(&ctx->worker_mutex); 328 | goto next; 329 | } 330 | } 331 | 332 | ctx->mpp_produced = true; 333 | 334 | pthread_mutex_unlock(&ctx->worker_mutex); 335 | 336 | pthread_mutex_lock(&ctx->ioctl_mutex); 337 | 338 | if (!ctx->mpp_streaming || !ctx->mpp_produced) 339 | goto next_locked; 340 | 341 | pthread_mutex_lock(&ctx->capture.queue_mutex); 342 | rkmpp_buffer = TAILQ_FIRST(&ctx->capture.pending_buffers); 343 | TAILQ_REMOVE(&ctx->capture.pending_buffers, 344 | rkmpp_buffer, entry); 345 | rkmpp_buffer_clr_pending(rkmpp_buffer); 346 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 347 | 348 | rkmpp_buffer->bytesused = 0; 349 | 350 | if (enc->type == H264 && enc->needs_header && 351 | !enc->h264.separate_header) { 352 | /* Join the header to the 1st frame */ 353 | rkmpp_packet_to_buffer(enc->header, rkmpp_buffer); 354 | enc->needs_header = false; 355 | } 356 | 357 | rkmpp_packet_to_buffer(packet, rkmpp_buffer); 358 | 359 | meta = mpp_packet_get_meta(packet); 360 | if (meta) { 361 | mpp_meta_get_s32(meta, KEY_OUTPUT_INTRA, &is_keyframe); 362 | 363 | if (is_keyframe) { 364 | rkmpp_buffer_set_keyframe(rkmpp_buffer); 365 | 366 | if (enc->keyframe_requested > 0) { 367 | enc->keyframe_requested--; 368 | rkmpp_enc_apply_rc_cfg(enc); 369 | } 370 | } 371 | } 372 | 373 | /* Use pts to track frame buffer */ 374 | index = mpp_packet_get_pts(packet); 375 | frame_buffer = &ctx->output.buffers[index]; 376 | 377 | rkmpp_buffer->timestamp = frame_buffer->timestamp; 378 | 379 | LOGV(2, "return frame: %d(%" PRIu64 ")\n", 380 | index, frame_buffer->timestamp); 381 | 382 | pthread_mutex_lock(&ctx->output.queue_mutex); 383 | TAILQ_INSERT_TAIL(&ctx->output.avail_buffers, 384 | frame_buffer, entry); 385 | rkmpp_buffer_set_available(frame_buffer); 386 | pthread_cond_signal(&ctx->output.queue_cond); 387 | pthread_mutex_unlock(&ctx->output.queue_mutex); 388 | 389 | /* Report new frame to count fps */ 390 | rkmpp_new_frame(ctx); 391 | 392 | LOGV(2, "return packet: %d(%" PRIu64 ") len=%d\n", 393 | rkmpp_buffer->index, rkmpp_buffer->timestamp, 394 | rkmpp_buffer->bytesused); 395 | 396 | pthread_mutex_lock(&ctx->capture.queue_mutex); 397 | TAILQ_INSERT_TAIL(&ctx->capture.avail_buffers, 398 | rkmpp_buffer, entry); 399 | rkmpp_buffer_set_available(rkmpp_buffer); 400 | pthread_cond_signal(&ctx->capture.queue_cond); 401 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 402 | next_locked: 403 | pthread_mutex_unlock(&ctx->ioctl_mutex); 404 | next: 405 | /* Update poll event after every loop */ 406 | pthread_mutex_lock(&ctx->ioctl_mutex); 407 | rkmpp_update_poll_event(ctx); 408 | pthread_mutex_unlock(&ctx->ioctl_mutex); 409 | 410 | if (packet) 411 | mpp_packet_deinit(&packet); 412 | } 413 | 414 | LEAVE(); 415 | return NULL; 416 | } 417 | 418 | static int rkmpp_enc_qbuf(struct rkmpp_enc_context *enc, 419 | struct v4l2_buffer *buffer) 420 | { 421 | struct rkmpp_context *ctx = enc->ctx; 422 | int ret; 423 | 424 | ENTER(); 425 | 426 | ret = rkmpp_qbuf(ctx, buffer); 427 | if (ret < 0) 428 | RETURN_ERR(errno, -1); 429 | 430 | /* Wakeup worker thread */ 431 | pthread_mutex_lock(&ctx->worker_mutex); 432 | pthread_cond_signal(&ctx->worker_cond); 433 | pthread_mutex_unlock(&ctx->worker_mutex); 434 | 435 | LEAVE(); 436 | return ret; 437 | } 438 | 439 | static int rkmpp_enc_apply_h264_cfg(struct rkmpp_enc_context *enc) 440 | { 441 | struct rkmpp_context *ctx = enc->ctx; 442 | MppEncCfg cfg; 443 | MPP_RET ret; 444 | 445 | if (mpp_enc_cfg_init(&cfg)) { 446 | LOGE("failed to init enc config\n"); 447 | RETURN_ERR(ENOMEM, -1); 448 | } 449 | 450 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_CFG, cfg); 451 | if (ret != MPP_OK) { 452 | LOGE("failed to get enc config\n"); 453 | goto err; 454 | } 455 | 456 | mpp_enc_cfg_set_s32(cfg, "h264:profile", enc->h264.profile); 457 | mpp_enc_cfg_set_s32(cfg, "h264:level", enc->h264.level); 458 | 459 | mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 460 | enc->h264.profile == MPP_H264_PROFILE_HIGH); 461 | mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 462 | enc->h264.profile != MPP_H264_PROFILE_BASELINE); 463 | mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0); 464 | 465 | mpp_enc_cfg_set_s32(cfg, "h264:qp_max", enc->max_qp ? enc->max_qp : 28); 466 | mpp_enc_cfg_set_s32(cfg, "h264:qp_min", enc->min_qp ? enc->min_qp : 4); 467 | 468 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_CFG, cfg); 469 | if (ret != MPP_OK) { 470 | LOGE("failed to set enc config: %d\n", ret); 471 | goto err; 472 | } 473 | 474 | mpp_enc_cfg_deinit(cfg); 475 | return 0; 476 | err: 477 | mpp_enc_cfg_deinit(cfg); 478 | RETURN_ERR(EINVAL, -1); 479 | } 480 | 481 | static int rkmpp_enc_apply_vp8_cfg(struct rkmpp_enc_context *enc) 482 | { 483 | struct rkmpp_context *ctx = enc->ctx; 484 | MppEncCfg cfg; 485 | MPP_RET ret; 486 | 487 | if (mpp_enc_cfg_init(&cfg)) { 488 | LOGE("failed to init enc config\n"); 489 | RETURN_ERR(ENOMEM, -1); 490 | } 491 | 492 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_CFG, cfg); 493 | if (ret != MPP_OK) { 494 | LOGE("failed to get enc config\n"); 495 | goto err; 496 | } 497 | 498 | mpp_enc_cfg_set_s32(cfg, "vp8:qp_init", 40); 499 | mpp_enc_cfg_set_s32(cfg, "vp8:qp_max", enc->max_qp ? enc->max_qp : 127); 500 | mpp_enc_cfg_set_s32(cfg, "vp8:qp_min", enc->min_qp); 501 | 502 | mpp_enc_cfg_set_s32(cfg, "vp8:disable_ivf", 1); 503 | 504 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_CFG, cfg); 505 | if (ret != MPP_OK) { 506 | LOGE("failed to set enc config: %d\n", ret); 507 | goto err; 508 | } 509 | 510 | mpp_enc_cfg_deinit(cfg); 511 | return 0; 512 | err: 513 | mpp_enc_cfg_deinit(cfg); 514 | RETURN_ERR(EINVAL, -1); 515 | } 516 | 517 | static int rkmpp_enc_apply_mjpeg_cfg(struct rkmpp_enc_context *enc) 518 | { 519 | struct rkmpp_context *ctx = enc->ctx; 520 | MppEncCfg cfg; 521 | MPP_RET ret; 522 | 523 | if (mpp_enc_cfg_init(&cfg)) { 524 | LOGE("failed to init enc config\n"); 525 | RETURN_ERR(ENOMEM, -1); 526 | } 527 | 528 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_CFG, cfg); 529 | if (ret != MPP_OK) { 530 | LOGE("failed to get enc config\n"); 531 | goto err; 532 | } 533 | 534 | mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", enc->mjpeg.quality ? enc->mjpeg.quality : 80); 535 | 536 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_CFG, cfg); 537 | if (ret != MPP_OK) { 538 | LOGE("failed to set enc config: %d\n", ret); 539 | goto err; 540 | } 541 | 542 | mpp_enc_cfg_deinit(cfg); 543 | return 0; 544 | err: 545 | mpp_enc_cfg_deinit(cfg); 546 | RETURN_ERR(EINVAL, -1); 547 | } 548 | 549 | static int rkmpp_enc_apply_input_cfg(struct rkmpp_enc_context *enc) 550 | { 551 | struct rkmpp_context *ctx = enc->ctx; 552 | struct v4l2_pix_format_mplane *fmt = &ctx->output.format; 553 | const struct rkmpp_fmt *rkmpp_fmt = ctx->output.rkmpp_format; 554 | MppEncCfg cfg; 555 | MPP_RET ret; 556 | 557 | if (mpp_enc_cfg_init(&cfg)) { 558 | LOGE("failed to init enc config\n"); 559 | RETURN_ERR(ENOMEM, -1); 560 | } 561 | 562 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_CFG, cfg); 563 | if (ret != MPP_OK) { 564 | LOGE("failed to get enc config\n"); 565 | goto err; 566 | } 567 | 568 | enc->width = enc->crop.width ? enc->crop.width : fmt->width; 569 | enc->height = enc->crop.height ? enc->crop.height : fmt->height; 570 | enc->hstride = fmt->plane_fmt[0].bytesperline; 571 | enc->vstride = fmt->plane_fmt[0].sizeimage / 572 | fmt->plane_fmt[0].bytesperline; 573 | 574 | mpp_enc_cfg_set_s32(cfg, "prep:format", rkmpp_fmt->format); 575 | mpp_enc_cfg_set_s32(cfg, "prep:width", enc->width); 576 | mpp_enc_cfg_set_s32(cfg, "prep:height", enc->height); 577 | mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", enc->hstride); 578 | mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", enc->vstride); 579 | 580 | LOGV(1, "apply input size: %dx%d(%dx%d)\n", 581 | enc->width, enc->height, enc->hstride, enc->vstride); 582 | 583 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_CFG, cfg); 584 | if (ret != MPP_OK) { 585 | LOGE("failed to set enc config: %d\n", ret); 586 | goto err; 587 | } 588 | 589 | mpp_enc_cfg_deinit(cfg); 590 | return 0; 591 | err: 592 | mpp_enc_cfg_deinit(cfg); 593 | RETURN_ERR(EINVAL, -1); 594 | } 595 | 596 | static int rkmpp_enc_apply_rc_cfg(struct rkmpp_enc_context *enc) 597 | { 598 | struct rkmpp_context *ctx = enc->ctx; 599 | MppEncCfg cfg; 600 | MppEncRcMode rc_mode; 601 | MPP_RET ret; 602 | int bitrate; 603 | 604 | if (mpp_enc_cfg_init(&cfg)) { 605 | LOGE("failed to init enc config\n"); 606 | RETURN_ERR(ENOMEM, -1); 607 | } 608 | 609 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_GET_CFG, cfg); 610 | if (ret != MPP_OK) { 611 | LOGE("failed to get enc config\n"); 612 | goto err; 613 | } 614 | 615 | if (enc->rc_mode != MPP_ENC_RC_MODE_BUTT) { 616 | rc_mode = enc->rc_mode; 617 | } else if (enc->mb_rc) { 618 | if (enc->rc_reaction_coeff < 10) 619 | /* The "tight" bitrate mode */ 620 | rc_mode = MPP_ENC_RC_MODE_CBR; 621 | else 622 | rc_mode = MPP_ENC_RC_MODE_VBR; 623 | } else { 624 | /* Disable macroblock-level bitrate control */ 625 | rc_mode = MPP_ENC_RC_MODE_FIXQP; 626 | } 627 | 628 | mpp_enc_cfg_set_s32(cfg, "rc:mode", rc_mode); 629 | 630 | mpp_enc_cfg_set_u32(cfg, "rc:max_reenc_times", 1); 631 | 632 | mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", 0); 633 | mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", enc->numerator); 634 | mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denorm", enc->denominator); 635 | mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", 0); 636 | mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", enc->numerator); 637 | mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denorm", enc->denominator); 638 | 639 | /* Use gop(1) for keyframe requests */ 640 | mpp_enc_cfg_set_s32(cfg, "rc:gop", 641 | !enc->keyframe_requested ? enc->gop_size : 1); 642 | 643 | bitrate = enc->bitrate; 644 | if (!bitrate) 645 | bitrate = enc->width * enc->height / 8 * 646 | enc->numerator / enc->denominator; 647 | 648 | if (enc->fixed_bitrate) { 649 | mpp_enc_cfg_set_s32(cfg, "rc:bps_target", bitrate); 650 | mpp_enc_cfg_set_s32(cfg, "rc:bps_max", bitrate); 651 | mpp_enc_cfg_set_s32(cfg, "rc:bps_min", bitrate); 652 | } else if (rc_mode == MPP_ENC_RC_MODE_FIXQP) { 653 | /* BPS settings are ignored in FIXQP mode */ 654 | mpp_enc_cfg_set_s32(cfg, "rc:bps_target", -1); 655 | mpp_enc_cfg_set_s32(cfg, "rc:bps_max", -1); 656 | mpp_enc_cfg_set_s32(cfg, "rc:bps_min", -1); 657 | } else if (rc_mode == MPP_ENC_RC_MODE_CBR) { 658 | /* Constant bitrate has very small bps range of 1/16 bps */ 659 | mpp_enc_cfg_set_s32(cfg, "rc:bps_target", bitrate); 660 | mpp_enc_cfg_set_s32(cfg, "rc:bps_max", bitrate * 17 / 16); 661 | mpp_enc_cfg_set_s32(cfg, "rc:bps_min", bitrate * 15 / 16); 662 | } else { 663 | /* Variable bitrate has large bps range */ 664 | mpp_enc_cfg_set_s32(cfg, "rc:bps_target", bitrate); 665 | mpp_enc_cfg_set_s32(cfg, "rc:bps_max", bitrate * 17 / 16); 666 | mpp_enc_cfg_set_s32(cfg, "rc:bps_min", bitrate * 1 / 16); 667 | } 668 | 669 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_CFG, cfg); 670 | if (ret != MPP_OK) { 671 | LOGE("failed to set enc config: %d\n", ret); 672 | goto err; 673 | } 674 | 675 | ret = ctx->mpi->control(ctx->mpp, MPP_ENC_SET_HEADER_MODE, 676 | &enc->header_mode); 677 | if (ret != MPP_OK) { 678 | LOGE("failed to set header mode: %d\n", ret); 679 | goto err; 680 | } 681 | 682 | mpp_enc_cfg_deinit(cfg); 683 | return 0; 684 | err: 685 | mpp_enc_cfg_deinit(cfg); 686 | RETURN_ERR(EINVAL, -1); 687 | } 688 | 689 | static int rkmpp_enc_streamon(struct rkmpp_enc_context *enc, 690 | enum v4l2_buf_type *type) 691 | { 692 | struct rkmpp_context *ctx = enc->ctx; 693 | const struct rkmpp_fmt *rkmpp_fmt = ctx->capture.rkmpp_format; 694 | struct rkmpp_buf_queue *queue; 695 | MppPollType poll_type; 696 | MPP_RET ret; 697 | 698 | ENTER(); 699 | 700 | queue = rkmpp_get_queue(ctx, *type); 701 | if (!queue) 702 | RETURN_ERR(errno, -1); 703 | 704 | if (queue->streaming) 705 | goto out; 706 | queue->streaming = true; 707 | 708 | LOGV(1, "queue(%d) start streaming\n", *type); 709 | 710 | /* Start mpp streaming only when all queues started */ 711 | if (ctx->mpp_streaming || 712 | !ctx->output.streaming || !ctx->capture.streaming) 713 | goto out; 714 | 715 | switch (rkmpp_fmt->fourcc) { 716 | case V4L2_PIX_FMT_H264: 717 | enc->type = H264; 718 | break; 719 | case V4L2_PIX_FMT_VP8: 720 | enc->type = VP8; 721 | break; 722 | case V4L2_PIX_FMT_MJPEG: 723 | enc->type = MJPEG; 724 | break; 725 | default: 726 | RETURN_ERR(errno, -1); 727 | } 728 | 729 | LOGV(1, "mpp initializing for %s\n", ctx->capture.rkmpp_format->name); 730 | 731 | ret = mpp_create(&ctx->mpp, &ctx->mpi); 732 | if (ret != MPP_OK) { 733 | LOGE("failed to create mpp\n"); 734 | errno = ENOMEM; 735 | goto err; 736 | } 737 | 738 | ret = mpp_init(ctx->mpp, MPP_CTX_ENC, 739 | ctx->capture.rkmpp_format->type); 740 | if (ret != MPP_OK) { 741 | LOGE("failed to init mpp\n"); 742 | goto err_destroy_mpp; 743 | } 744 | 745 | /* The mpp encoder only work in block mode */ 746 | poll_type = MPP_POLL_BLOCK; 747 | ret = ctx->mpi->control(ctx->mpp, MPP_SET_OUTPUT_TIMEOUT, 748 | (MppParam)&poll_type); 749 | if (ret != MPP_OK) { 750 | LOGE("failed to set mpp timeout\n"); 751 | goto err_destroy_mpp; 752 | } 753 | 754 | if (enc->type == H264) { 755 | /* Apply h264's special configs */ 756 | if (rkmpp_enc_apply_h264_cfg(enc) < 0) { 757 | LOGE("failed to apply h264 cfg\n"); 758 | goto err_destroy_mpp; 759 | } 760 | 761 | enc->needs_header = true; 762 | } else if (enc->type == VP8) { 763 | /* Apply vp8's special configs */ 764 | if (rkmpp_enc_apply_vp8_cfg(enc) < 0) { 765 | LOGE("failed to apply vp8 cfg\n"); 766 | goto err_destroy_mpp; 767 | } 768 | } else if (enc->type == MJPEG) { 769 | /* Apply mjpeg's special configs */ 770 | if (rkmpp_enc_apply_mjpeg_cfg(enc) < 0) { 771 | LOGE("failed to apply mjpeg cfg\n"); 772 | goto err_destroy_mpp; 773 | } 774 | } 775 | 776 | if (enc->header) 777 | mpp_packet_deinit(&enc->header); 778 | enc->header = NULL; 779 | 780 | /* Apply configs about input frames */ 781 | if (rkmpp_enc_apply_input_cfg(enc) < 0) { 782 | LOGE("failed to apply input cfg\n"); 783 | goto err_destroy_mpp; 784 | } 785 | 786 | /* Apply configs about rate control */ 787 | if (rkmpp_enc_apply_rc_cfg(enc) < 0) { 788 | LOGE("failed to apply rc cfg\n"); 789 | goto err_destroy_mpp; 790 | } 791 | 792 | rkmpp_streamon(ctx); 793 | out: 794 | LEAVE(); 795 | return 0; 796 | err_destroy_mpp: 797 | ctx->mpi->reset(ctx->mpp); 798 | mpp_destroy(ctx->mpp); 799 | err: 800 | rkmpp_reset_queue(ctx, queue); 801 | RETURN_ERR(EPIPE, -1); 802 | } 803 | 804 | static int rkmpp_enc_streamoff(struct rkmpp_enc_context *enc, 805 | enum v4l2_buf_type *type) 806 | { 807 | struct rkmpp_context *ctx = enc->ctx; 808 | struct rkmpp_buf_queue *queue; 809 | 810 | ENTER(); 811 | 812 | queue = rkmpp_get_queue(ctx, *type); 813 | if (!queue) 814 | RETURN_ERR(errno, -1); 815 | 816 | if (!queue->streaming) 817 | goto out; 818 | 819 | LOGV(1, "queue(%d) stop streaming\n", *type); 820 | 821 | rkmpp_reset_queue(ctx, queue); 822 | 823 | /* Stop mpp streaming when any queue stopped */ 824 | rkmpp_streamoff(ctx); 825 | out: 826 | LEAVE(); 827 | return 0; 828 | } 829 | 830 | static int rkmpp_enc_s_selection(struct rkmpp_enc_context *enc, 831 | struct v4l2_selection *selection) 832 | { 833 | struct rkmpp_context *ctx = enc->ctx; 834 | struct v4l2_rect *rect = &selection->r; 835 | 836 | ENTER(); 837 | 838 | if (selection->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 839 | selection->target != V4L2_SEL_TGT_CROP) { 840 | LOGE("invalid type or target\n"); 841 | RETURN_ERR(EINVAL, -1); 842 | } 843 | 844 | if (ctx->output.streaming) { 845 | LOGE("output is streaming\n"); 846 | RETURN_ERR(EBUSY, -1); 847 | } 848 | 849 | if (rect->top || rect->left) { 850 | LOGE("not support offsets\n"); 851 | rect->width += rect->left; 852 | rect->height += rect->top; 853 | rect->top = rect->left = 0; 854 | } 855 | 856 | enc->crop = *rect; 857 | 858 | LOGV(1, "crop rect: %dx%d\n", enc->crop.width, enc->crop.height); 859 | 860 | LEAVE(); 861 | return 0; 862 | } 863 | 864 | static int rkmpp_enc_s_parm(struct rkmpp_enc_context *enc, 865 | struct v4l2_streamparm *parms) 866 | { 867 | struct rkmpp_context *ctx = enc->ctx; 868 | 869 | ENTER(); 870 | 871 | if (parms->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 872 | LOGE("only support s_parm for output now\n"); 873 | RETURN_ERR(EINVAL, -1); 874 | } 875 | 876 | /* V4L2 provide "time per frame", but mpp needs "frames per second" */ 877 | enc->denominator = parms->parm.output.timeperframe.numerator; 878 | enc->numerator = parms->parm.output.timeperframe.denominator; 879 | 880 | LOGV(1, "numerator: %d, denominator: %d\n", 881 | parms->parm.output.timeperframe.numerator, 882 | parms->parm.output.timeperframe.denominator); 883 | 884 | if (ctx->mpp_streaming && 885 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 886 | LOGE("failed to apply framerate\n"); 887 | RETURN_ERR(errno, -1); 888 | } 889 | 890 | LEAVE(); 891 | return 0; 892 | } 893 | 894 | static int rkmpp_enc_queryctrl(struct rkmpp_enc_context *enc, 895 | struct v4l2_queryctrl *query_ctrl) 896 | { 897 | struct rkmpp_context *ctx = enc->ctx; 898 | 899 | ENTER(); 900 | 901 | switch (query_ctrl->id) { 902 | case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: 903 | break; 904 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: 905 | query_ctrl->minimum = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; 906 | query_ctrl->maximum = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; 907 | break; 908 | case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 909 | query_ctrl->minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; 910 | query_ctrl->maximum = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; 911 | break; 912 | case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: 913 | query_ctrl->minimum = V4L2_MPEG_VIDEO_VP8_PROFILE_0; 914 | query_ctrl->maximum = query_ctrl->minimum; 915 | break; 916 | /* TODO: fill info for other supported ctrls */ 917 | default: 918 | LOGV(1, "unsupported ctrl: %x\n", query_ctrl->id); 919 | RETURN_ERR(EINVAL, -1); 920 | } 921 | 922 | LEAVE(); 923 | return 0; 924 | } 925 | 926 | static int rkmpp_enc_querymenu(struct rkmpp_enc_context *enc, 927 | struct v4l2_querymenu *query_menu) 928 | { 929 | struct rkmpp_context *ctx = enc->ctx; 930 | 931 | ENTER(); 932 | 933 | switch (query_menu->id) { 934 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: 935 | switch (query_menu->index) { 936 | case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: 937 | break; 938 | case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: 939 | break; 940 | default: 941 | LOGV(1, "unsupported bitrate mode: %x\n", 942 | query_menu->index); 943 | RETURN_ERR(EINVAL, -1); 944 | } 945 | break; 946 | case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 947 | switch (query_menu->index) { 948 | case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: 949 | case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: 950 | case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: 951 | break; 952 | default: 953 | LOGV(1, "unsupported H264 profile: %x\n", 954 | query_menu->index); 955 | RETURN_ERR(EINVAL, -1); 956 | } 957 | break; 958 | case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: 959 | if (query_menu->index != V4L2_MPEG_VIDEO_VP8_PROFILE_0) { 960 | LOGV(1, "unsupported VP8 profile: %x\n", 961 | query_menu->index); 962 | RETURN_ERR(EINVAL, -1); 963 | } 964 | break; 965 | default: 966 | LOGV(1, "unsupported menu: %x\n", query_menu->id); 967 | RETURN_ERR(EINVAL, -1); 968 | } 969 | 970 | LEAVE(); 971 | return 0; 972 | } 973 | 974 | static int rkmpp_enc_h264_convert_profile(int profile) 975 | { 976 | switch (profile) { 977 | case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: 978 | return MPP_H264_PROFILE_BASELINE; 979 | case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: 980 | return MPP_H264_PROFILE_MAIN; 981 | case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: 982 | return MPP_H264_PROFILE_HIGH; 983 | default: 984 | return -1; 985 | } 986 | } 987 | 988 | static int rkmpp_enc_s_ext_ctrls(struct rkmpp_enc_context *enc, 989 | struct v4l2_ext_controls *ext_ctrls) 990 | { 991 | struct rkmpp_context *ctx = enc->ctx; 992 | struct v4l2_ext_control *ctrl; 993 | unsigned int i; 994 | 995 | ENTER(); 996 | 997 | if (ext_ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG && 998 | ext_ctrls->ctrl_class != V4L2_CID_MPEG_CLASS) 999 | RETURN_ERR(EINVAL, -1); 1000 | 1001 | for (i = 0; i < ext_ctrls->count; i++) { 1002 | ctrl = &ext_ctrls->controls[i]; 1003 | 1004 | switch (ctrl->id) { 1005 | case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: 1006 | if (ctrl->value) { 1007 | LOGE("not supporting I-period\n"); 1008 | RETURN_ERR(EINVAL, -1); 1009 | } 1010 | break; 1011 | case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: 1012 | if (ctrl->value != 1013 | V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED) { 1014 | LOGE("not supporting disabling loop filter\n"); 1015 | RETURN_ERR(EINVAL, -1); 1016 | } 1017 | break; 1018 | case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: 1019 | case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: 1020 | /* Automatically configured */ 1021 | break; 1022 | case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: 1023 | if (ctrl->value) 1024 | enc->header_mode = MPP_ENC_HEADER_MODE_EACH_IDR; 1025 | else 1026 | enc->header_mode = MPP_ENC_HEADER_MODE_DEFAULT; 1027 | 1028 | LOGV(1, "header mode: %d\n", ctrl->value); 1029 | 1030 | if (ctx->mpp_streaming && 1031 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1032 | LOGE("failed to apply header mode\n"); 1033 | RETURN_ERR(errno, -1); 1034 | } 1035 | break; 1036 | case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: 1037 | enc->keyframe_requested++; 1038 | LOGV(1, "request keyframes: %d\n", 1039 | enc->keyframe_requested); 1040 | 1041 | if (ctx->mpp_streaming && 1042 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1043 | LOGE("failed to request keyframe\n"); 1044 | RETURN_ERR(errno, -1); 1045 | } 1046 | break; 1047 | case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: 1048 | switch (ctrl->value) { 1049 | case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: 1050 | enc->rc_mode = MPP_ENC_RC_MODE_VBR; 1051 | break; 1052 | case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: 1053 | enc->rc_mode = MPP_ENC_RC_MODE_CBR; 1054 | break; 1055 | default: 1056 | LOGV(1, "unsupported bitrate mode: %x\n", 1057 | ctrl->value); 1058 | RETURN_ERR(errno, -1); 1059 | } 1060 | 1061 | LOGV(1, "bitrate mode: %d\n", ctrl->value); 1062 | 1063 | if (ctx->mpp_streaming && 1064 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1065 | LOGE("failed to apply bitrate mode\n"); 1066 | RETURN_ERR(errno, -1); 1067 | } 1068 | break; 1069 | case V4L2_CID_MPEG_VIDEO_BITRATE: 1070 | enc->bitrate = ctrl->value; 1071 | LOGV(1, "bitrate: %d\n", enc->bitrate); 1072 | 1073 | if (ctx->mpp_streaming && 1074 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1075 | LOGE("failed to apply bitrate\n"); 1076 | RETURN_ERR(errno, -1); 1077 | } 1078 | break; 1079 | case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: 1080 | if (!ctrl->value) { 1081 | LOGE("unable to disable bitrate control\n"); 1082 | RETURN_ERR(EINVAL, -1); 1083 | } 1084 | break; 1085 | case V4L2_CID_MPEG_VIDEO_B_FRAMES: 1086 | if (ctrl->value) { 1087 | LOGE("not supporting B-frames\n"); 1088 | RETURN_ERR(EINVAL, -1); 1089 | } 1090 | break; 1091 | case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: 1092 | enc->max_qp = ctrl->value; 1093 | LOGV(1, "h264 max qp: %d\n", enc->max_qp); 1094 | 1095 | if (ctx->mpp_streaming && 1096 | rkmpp_enc_apply_h264_cfg(enc) < 0) { 1097 | LOGE("failed to apply h264 max qp\n"); 1098 | RETURN_ERR(errno, -1); 1099 | } 1100 | break; 1101 | case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: 1102 | enc->min_qp = ctrl->value; 1103 | LOGV(1, "h264 min qp: %d\n", enc->min_qp); 1104 | 1105 | if (ctx->mpp_streaming && 1106 | rkmpp_enc_apply_h264_cfg(enc) < 0) { 1107 | LOGE("failed to apply h264 min qp\n"); 1108 | RETURN_ERR(errno, -1); 1109 | } 1110 | break; 1111 | case V4L2_CID_MPEG_VIDEO_H264_PROFILE: 1112 | enc->h264.profile = 1113 | rkmpp_enc_h264_convert_profile(ctrl->value); 1114 | if (enc->h264.profile < 0) { 1115 | enc->h264.profile = MPP_H264_PROFILE_HIGH; 1116 | LOGE("only support baseline|main|high\n"); 1117 | RETURN_ERR(EINVAL, -1); 1118 | } 1119 | 1120 | LOGV(1, "h264 profile: %d\n", enc->h264.profile); 1121 | 1122 | if (ctx->mpp_streaming && 1123 | rkmpp_enc_apply_h264_cfg(enc) < 0) { 1124 | LOGE("failed to apply h264 profile\n"); 1125 | RETURN_ERR(errno, -1); 1126 | } 1127 | break; 1128 | case V4L2_CID_MPEG_VIDEO_H264_LEVEL: 1129 | enc->h264.level = ctrl->value; 1130 | LOGV(1, "h264 level: %d\n", enc->h264.level); 1131 | 1132 | if (ctx->mpp_streaming && 1133 | rkmpp_enc_apply_h264_cfg(enc) < 0) { 1134 | LOGE("failed to apply h264 level\n"); 1135 | RETURN_ERR(errno, -1); 1136 | } 1137 | break; 1138 | case V4L2_CID_MPEG_VIDEO_HEADER_MODE: 1139 | if (ctrl->value == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) 1140 | enc->h264.separate_header = true; 1141 | else 1142 | enc->h264.separate_header = false; 1143 | 1144 | LOGV(1, "h264 separate header: %d\n", enc->h264.separate_header); 1145 | break; 1146 | case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: 1147 | enc->max_qp = ctrl->value; 1148 | LOGV(1, "vpx max qp: %d\n", enc->max_qp); 1149 | 1150 | if (ctx->mpp_streaming && 1151 | rkmpp_enc_apply_vp8_cfg(enc) < 0) { 1152 | LOGE("failed to apply vpx max qp\n"); 1153 | RETURN_ERR(errno, -1); 1154 | } 1155 | break; 1156 | case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: 1157 | enc->min_qp = ctrl->value; 1158 | LOGV(1, "vpx min qp: %d\n", enc->min_qp); 1159 | 1160 | if (ctx->mpp_streaming && 1161 | rkmpp_enc_apply_vp8_cfg(enc) < 0) { 1162 | LOGE("failed to apply vpx min qp\n"); 1163 | RETURN_ERR(errno, -1); 1164 | } 1165 | break; 1166 | case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: 1167 | enc->mb_rc = !!ctrl->value; 1168 | LOGV(1, "mb rc: %d\n", enc->mb_rc); 1169 | 1170 | if (ctx->mpp_streaming && 1171 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1172 | LOGE("failed to apply mb bitrate control\n"); 1173 | RETURN_ERR(errno, -1); 1174 | } 1175 | break; 1176 | case V4L2_CID_MPEG_VIDEO_GOP_SIZE: 1177 | enc->gop_size = ctrl->value; 1178 | LOGV(1, "gop size: %d\n", enc->gop_size); 1179 | 1180 | if (ctx->mpp_streaming && 1181 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1182 | LOGE("failed to apply gop size\n"); 1183 | RETURN_ERR(errno, -1); 1184 | } 1185 | break; 1186 | case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF: 1187 | enc->rc_reaction_coeff = ctrl->value; 1188 | LOGV(1, "rc reaction coeff: %d\n", 1189 | enc->rc_reaction_coeff); 1190 | 1191 | if (ctx->mpp_streaming && 1192 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1193 | LOGE("failed to apply rc reaction coeff\n"); 1194 | RETURN_ERR(errno, -1); 1195 | } 1196 | break; 1197 | case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: 1198 | enc->fixed_bitrate = !!ctrl->value; 1199 | LOGV(1, "fixed bitrate: %d\n", enc->fixed_bitrate); 1200 | 1201 | if (ctx->mpp_streaming && 1202 | rkmpp_enc_apply_rc_cfg(enc) < 0) { 1203 | LOGE("failed to apply fixed bitrate\n"); 1204 | RETURN_ERR(errno, -1); 1205 | } 1206 | break; 1207 | case V4L2_CID_JPEG_COMPRESSION_QUALITY: 1208 | enc->mjpeg.quality = ctrl->value; 1209 | LOGV(1, "jpeg quality: %d\n", enc->mjpeg.quality); 1210 | 1211 | if (ctx->mpp_streaming && 1212 | rkmpp_enc_apply_mjpeg_cfg(enc) < 0) { 1213 | LOGE("failed to apply compression quality\n"); 1214 | RETURN_ERR(errno, -1); 1215 | } 1216 | break; 1217 | default: 1218 | LOGE("unsupported ctrl: %x\n", ctrl->id); 1219 | RETURN_ERR(EINVAL, -1); 1220 | } 1221 | } 1222 | 1223 | LEAVE(); 1224 | return 0; 1225 | } 1226 | 1227 | static int rkmpp_enc_cmd(struct rkmpp_enc_context *enc, 1228 | struct v4l2_encoder_cmd *cmd) 1229 | { 1230 | struct rkmpp_context *ctx = enc->ctx; 1231 | 1232 | ENTER(); 1233 | 1234 | if (cmd->cmd == V4L2_ENC_CMD_START) { 1235 | LOGV(1, "handle start encoding cmd\n"); 1236 | 1237 | rkmpp_exit_flushing(ctx); 1238 | } else if (cmd->cmd == V4L2_ENC_CMD_STOP) { 1239 | LOGV(1, "handle stop encoding cmd\n"); 1240 | 1241 | rkmpp_start_flushing(ctx); 1242 | } else { 1243 | LOGE("unsupported cmd: %x\n", cmd->cmd); 1244 | RETURN_ERR(EINVAL, -1); 1245 | } 1246 | 1247 | LEAVE(); 1248 | return 0; 1249 | } 1250 | 1251 | bool rkmpp_enc_has_event(void *data) 1252 | { 1253 | (void)data; /* unused */ 1254 | return false; 1255 | } 1256 | 1257 | void *rkmpp_enc_init(struct rkmpp_context *ctx) 1258 | { 1259 | struct rkmpp_enc_context *enc; 1260 | 1261 | ENTER(); 1262 | 1263 | enc = (struct rkmpp_enc_context *) 1264 | calloc(1, sizeof(struct rkmpp_enc_context)); 1265 | if (!enc) 1266 | RETURN_ERR(ENOMEM, NULL); 1267 | 1268 | ctx->formats = rkmpp_enc_fmts; 1269 | ctx->num_formats = ARRAY_SIZE(rkmpp_enc_fmts); 1270 | enc->ctx = ctx; 1271 | 1272 | if (!ctx->max_width) 1273 | ctx->max_width = MAX_ENC_WIDTH; 1274 | 1275 | if (!ctx->max_height) 1276 | ctx->max_height = MAX_ENC_HEIGHT; 1277 | 1278 | enc->h264.profile = MPP_H264_PROFILE_HIGH; 1279 | enc->h264.level = 40; /* 1080p@30fps */ 1280 | enc->h264.separate_header = true; 1281 | 1282 | enc->max_qp = enc->min_qp = 0; 1283 | 1284 | enc->rc_mode = MPP_ENC_RC_MODE_BUTT; 1285 | enc->mb_rc = true; 1286 | enc->rc_reaction_coeff = 1; 1287 | enc->gop_size = 30; 1288 | enc->fixed_bitrate = false; 1289 | 1290 | enc->bitrate = 0; 1291 | enc->denominator = 1; 1292 | enc->numerator = 30; 1293 | 1294 | pthread_create(&ctx->worker_thread, NULL, encoder_thread_fn, enc); 1295 | 1296 | LEAVE(); 1297 | return enc; 1298 | } 1299 | 1300 | void rkmpp_enc_deinit(void *data) 1301 | { 1302 | struct rkmpp_enc_context *enc = data; 1303 | struct rkmpp_context *ctx = enc->ctx; 1304 | 1305 | ENTER(); 1306 | 1307 | if (enc->header) 1308 | mpp_packet_deinit(&enc->header); 1309 | 1310 | free(enc); 1311 | 1312 | LEAVE(); 1313 | } 1314 | 1315 | int rkmpp_enc_ioctl(void *data, unsigned long cmd, void *arg) 1316 | { 1317 | struct rkmpp_enc_context *enc = data; 1318 | struct rkmpp_context *ctx = enc->ctx; 1319 | int ret; 1320 | 1321 | ENTER(); 1322 | 1323 | switch (cmd) { 1324 | 1325 | /* Common ioctls */ 1326 | case VIDIOC_QUERYCAP: 1327 | ret = rkmpp_querycap(ctx, arg); 1328 | break; 1329 | case VIDIOC_ENUM_FMT: 1330 | ret = rkmpp_enum_fmt(ctx, arg); 1331 | break; 1332 | case VIDIOC_ENUM_FRAMESIZES: 1333 | ret = rkmpp_enum_framesizes(ctx, arg); 1334 | break; 1335 | case VIDIOC_TRY_FMT: 1336 | ret = rkmpp_try_fmt(ctx, arg); 1337 | break; 1338 | case VIDIOC_S_FMT: 1339 | ret = rkmpp_s_fmt(ctx, arg); 1340 | break; 1341 | case VIDIOC_REQBUFS: 1342 | ret = rkmpp_reqbufs(ctx, arg); 1343 | break; 1344 | case VIDIOC_QUERYBUF: 1345 | ret = rkmpp_querybuf(ctx, arg); 1346 | break; 1347 | case VIDIOC_EXPBUF: 1348 | ret = rkmpp_expbuf(ctx, arg); 1349 | break; 1350 | case VIDIOC_DQBUF: 1351 | ret = rkmpp_dqbuf(ctx, arg); 1352 | break; 1353 | case VIDIOC_G_FMT: 1354 | ret = rkmpp_g_fmt(ctx, arg); 1355 | break; 1356 | 1357 | /* Encoder special ioctls */ 1358 | case VIDIOC_QBUF: 1359 | ret = rkmpp_enc_qbuf(enc, arg); 1360 | break; 1361 | case VIDIOC_STREAMON: 1362 | ret = rkmpp_enc_streamon(enc, arg); 1363 | break; 1364 | case VIDIOC_STREAMOFF: 1365 | ret = rkmpp_enc_streamoff(enc, arg); 1366 | break; 1367 | case VIDIOC_S_SELECTION: 1368 | ret = rkmpp_enc_s_selection(enc, arg); 1369 | break; 1370 | case VIDIOC_S_PARM: 1371 | ret = rkmpp_enc_s_parm(enc, arg); 1372 | break; 1373 | case VIDIOC_QUERYCTRL: 1374 | ret = rkmpp_enc_queryctrl(enc, arg); 1375 | break; 1376 | case VIDIOC_QUERYMENU: 1377 | ret = rkmpp_enc_querymenu(enc, arg); 1378 | break; 1379 | case VIDIOC_S_EXT_CTRLS: 1380 | ret = rkmpp_enc_s_ext_ctrls(enc, arg); 1381 | break; 1382 | case VIDIOC_ENCODER_CMD: 1383 | ret = rkmpp_enc_cmd(enc, arg); 1384 | break; 1385 | default: 1386 | LOGV(1, "unsupported ioctl cmd: %s(%lu)!\n", 1387 | rkmpp_cmd2str(cmd), cmd); 1388 | RETURN_ERR(ENOTTY, -1); 1389 | } 1390 | 1391 | LEAVE(); 1392 | return ret; 1393 | } 1394 | -------------------------------------------------------------------------------- /src/libv4l-rkmpp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "libv4l-plugin.h" 23 | #include "libv4l-rkmpp.h" 24 | #include "libv4l-rkmpp-dec.h" 25 | #include "libv4l-rkmpp-enc.h" 26 | 27 | #if HAVE_VISIBILITY 28 | #define PLUGIN_PUBLIC __attribute__ ((__visibility__("default"))) 29 | #else 30 | #define PLUGIN_PUBLIC 31 | #endif 32 | 33 | #define FPS_UPDATE_INTERVAL 120 34 | 35 | #ifdef DEBUG 36 | int rkmpp_log_level = 10; 37 | static bool rkmpp_log_fps = true; 38 | #else 39 | int rkmpp_log_level = 0; 40 | static bool rkmpp_log_fps = false; 41 | #endif 42 | 43 | static pthread_once_t g_rkmpp_global_init_once = PTHREAD_ONCE_INIT; 44 | 45 | static void rkmpp_global_init() 46 | { 47 | char *env = getenv("LIBV4L_RKMPP_LOG_LEVEL"); 48 | if (env != NULL) 49 | rkmpp_log_level = atoi(env); 50 | 51 | env = getenv("LIBV4L_RKMPP_LOG_FPS"); 52 | if (env != NULL) 53 | rkmpp_log_fps = !!atoi(env); 54 | 55 | LOGV(1, "libv4l-rkmpp version: %s log_level: %d, log_fps: %d\n", 56 | LIBV4L_RKMPP_VERSION, rkmpp_log_level, rkmpp_log_fps); 57 | } 58 | 59 | void rkmpp_new_frame(struct rkmpp_context *ctx) 60 | { 61 | struct timeval tv; 62 | uint64_t curr_time; 63 | float fps; 64 | 65 | if (!rkmpp_log_fps) 66 | return; 67 | 68 | if (!ctx->last_fps_time) { 69 | gettimeofday(&tv, NULL); 70 | ctx->last_fps_time = tv.tv_sec * 1000 + tv.tv_usec / 1000; 71 | } 72 | 73 | if (++ctx->frames % FPS_UPDATE_INTERVAL) 74 | return; 75 | 76 | gettimeofday(&tv, NULL); 77 | curr_time = tv.tv_sec * 1000 + tv.tv_usec / 1000; 78 | 79 | fps = 1000.0f * FPS_UPDATE_INTERVAL / (curr_time - ctx->last_fps_time); 80 | ctx->last_fps_time = curr_time; 81 | 82 | LOG("%s FPS: %6.1f || Frames: %" PRIu64 "\n", 83 | ctx->is_decoder ? "DEC" : "ENC", fps, ctx->frames); 84 | } 85 | 86 | static void rkmpp_destroy_buffers(struct rkmpp_buf_queue *queue) 87 | { 88 | unsigned int i; 89 | 90 | if (!queue->num_buffers) 91 | return; 92 | 93 | if (queue->buffers) { 94 | for (i = 0; i < queue->num_buffers; i++) { 95 | if (rkmpp_buffer_locked(&queue->buffers[i])) 96 | mpp_buffer_put(queue->buffers[i].rkmpp_buf); 97 | } 98 | 99 | free(queue->buffers); 100 | queue->buffers = NULL; 101 | } 102 | 103 | mpp_buffer_group_clear(queue->internal_group); 104 | 105 | if (queue->external_group) 106 | mpp_buffer_group_clear(queue->external_group); 107 | 108 | queue->num_buffers = 0; 109 | } 110 | 111 | void rkmpp_reset_queue(struct rkmpp_context *ctx, 112 | struct rkmpp_buf_queue *queue) 113 | { 114 | struct rkmpp_buffer *rkmpp_buffer; 115 | unsigned int i; 116 | 117 | ENTER(); 118 | 119 | /* Hand over all buffers to userspace */ 120 | pthread_mutex_lock(&queue->queue_mutex); 121 | queue->streaming = false; 122 | TAILQ_INIT(&queue->avail_buffers); 123 | TAILQ_INIT(&queue->pending_buffers); 124 | pthread_cond_signal(&queue->queue_cond); 125 | pthread_mutex_unlock(&queue->queue_mutex); 126 | 127 | /* Update poll event after avail list changed */ 128 | rkmpp_update_poll_event(ctx); 129 | 130 | /* Reset buffer states */ 131 | for (i = 0; i < queue->num_buffers; i++) { 132 | rkmpp_buffer = &queue->buffers[i]; 133 | 134 | if (rkmpp_buffer_error(rkmpp_buffer)) 135 | rkmpp_buffer_clr_error(rkmpp_buffer); 136 | 137 | if (!rkmpp_buffer_locked(rkmpp_buffer)) { 138 | mpp_buffer_inc_ref(rkmpp_buffer->rkmpp_buf); 139 | rkmpp_buffer_set_locked(rkmpp_buffer); 140 | } 141 | 142 | if (rkmpp_buffer_queued(rkmpp_buffer)) 143 | rkmpp_buffer_clr_queued(rkmpp_buffer); 144 | 145 | if (rkmpp_buffer_pending(rkmpp_buffer)) 146 | rkmpp_buffer_clr_pending(rkmpp_buffer); 147 | 148 | if (rkmpp_buffer_available(rkmpp_buffer)) 149 | rkmpp_buffer_clr_available(rkmpp_buffer); 150 | 151 | if (rkmpp_buffer_keyframe(rkmpp_buffer)) 152 | rkmpp_buffer_clr_keyframe(rkmpp_buffer); 153 | 154 | if (rkmpp_buffer_last(rkmpp_buffer)) 155 | rkmpp_buffer_clr_last(rkmpp_buffer); 156 | } 157 | 158 | LEAVE(); 159 | } 160 | 161 | static const 162 | struct rkmpp_fmt *rkmpp_find_fmt(struct rkmpp_context *ctx, 163 | uint32_t fourcc) 164 | { 165 | unsigned int i; 166 | 167 | for (i = 0; i < ctx->num_formats; i++) { 168 | if (!RKMPP_HAS_FORMAT(ctx, &ctx->formats[i])) 169 | continue; 170 | 171 | if (ctx->formats[i].fourcc == fourcc) 172 | return &ctx->formats[i]; 173 | } 174 | 175 | return NULL; 176 | } 177 | 178 | int rkmpp_querycap(struct rkmpp_context *ctx, struct v4l2_capability *cap) 179 | { 180 | ENTER(); 181 | 182 | strncpy((char *)cap->driver, "rkmpp", sizeof(cap->driver)); 183 | strncpy((char *)cap->card, "rkmpp", sizeof(cap->card)); 184 | strncpy((char *)cap->bus_info, "platform: rkmpp", 185 | sizeof(cap->bus_info)); 186 | 187 | cap->version = LINUX_VERSION_CODE; 188 | 189 | /* This is only a mem-to-mem video device. */ 190 | cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; 191 | cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 192 | 193 | cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; 194 | cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; 195 | 196 | LEAVE(); 197 | return 0; 198 | } 199 | 200 | int rkmpp_enum_fmt(struct rkmpp_context *ctx, struct v4l2_fmtdesc *f) 201 | { 202 | const struct rkmpp_fmt *fmt; 203 | bool compressed; 204 | unsigned int i, j; 205 | 206 | ENTER(); 207 | 208 | switch (f->type) { 209 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: 210 | compressed = !ctx->is_decoder; 211 | break; 212 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: 213 | compressed = ctx->is_decoder; 214 | break; 215 | default: 216 | LOGE("invalid buf type\n"); 217 | RETURN_ERR(EINVAL, -1); 218 | } 219 | 220 | for (i = 0, j = 0; i < ctx->num_formats; ++i) { 221 | fmt = &ctx->formats[i]; 222 | if (!compressed && (fmt->type != MPP_VIDEO_CodingNone)) 223 | continue; 224 | else if (compressed && (fmt->type == MPP_VIDEO_CodingNone)) 225 | continue; 226 | else if (!RKMPP_HAS_FORMAT(ctx, &ctx->formats[i])) 227 | continue; 228 | 229 | if (j == f->index) { 230 | strncpy((char *)f->description, fmt->name, 231 | sizeof(f->description) - 1); 232 | f->pixelformat = fmt->fourcc; 233 | 234 | f->flags = 0; 235 | if (fmt->type != MPP_VIDEO_CodingNone) 236 | f->flags |= V4L2_FMT_FLAG_COMPRESSED; 237 | 238 | LEAVE(); 239 | return 0; 240 | } 241 | 242 | ++j; 243 | } 244 | 245 | LOGV(1, "%s format(%d) not found\n", 246 | compressed ? "compressed" : "raw", f->index); 247 | RETURN_ERR(EINVAL, -1); 248 | } 249 | 250 | int rkmpp_enum_framesizes(struct rkmpp_context *ctx, 251 | struct v4l2_frmsizeenum *fsize) 252 | { 253 | const struct rkmpp_fmt *fmt; 254 | 255 | ENTER(); 256 | 257 | if (fsize->index != 0) { 258 | LOGE("invalid frame size index (expected 0, got %d)\n", 259 | fsize->index); 260 | RETURN_ERR(EINVAL, -1); 261 | } 262 | 263 | fmt = rkmpp_find_fmt(ctx, fsize->pixel_format); 264 | if (!fmt) { 265 | LOGE("unsupported bitstream format (%08x)\n", 266 | fsize->pixel_format); 267 | RETURN_ERR(EINVAL, -1); 268 | } 269 | 270 | fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 271 | fsize->stepwise = fmt->frmsize; 272 | 273 | LEAVE(); 274 | return 0; 275 | } 276 | 277 | static void calculate_plane_sizes(const struct rkmpp_fmt *fmt, 278 | struct v4l2_pix_format_mplane *pix_fmt_mp) 279 | { 280 | unsigned int w = pix_fmt_mp->width; 281 | unsigned int h = pix_fmt_mp->height; 282 | int i; 283 | 284 | for (i = 0; i < fmt->num_planes; ++i) { 285 | pix_fmt_mp->plane_fmt[i].bytesperline = w * fmt->depth[i] / 8; 286 | pix_fmt_mp->plane_fmt[i].sizeimage = h * 287 | pix_fmt_mp->plane_fmt[i].bytesperline; 288 | /* 289 | * All of multiplanar formats we support have chroma 290 | * planes subsampled by 2 vertically. 291 | */ 292 | if (i != 0) 293 | pix_fmt_mp->plane_fmt[i].sizeimage /= 2; 294 | } 295 | } 296 | 297 | static uint32_t calculate_format_size(const struct rkmpp_fmt *fmt, 298 | struct v4l2_pix_format_mplane *pix_fmt_mp) 299 | { 300 | uint32_t sizeimage; 301 | int i; 302 | 303 | for (i = 0, sizeimage = 0; i < fmt->num_planes; ++i) 304 | sizeimage += pix_fmt_mp->plane_fmt[i].sizeimage; 305 | 306 | return sizeimage; 307 | } 308 | 309 | int rkmpp_try_fmt(struct rkmpp_context *ctx, struct v4l2_format *f) 310 | { 311 | const struct rkmpp_fmt *fmt; 312 | struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; 313 | bool compressed; 314 | 315 | ENTER(); 316 | 317 | switch (f->type) { 318 | case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: 319 | compressed = !ctx->is_decoder; 320 | break; 321 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: 322 | compressed = ctx->is_decoder; 323 | break; 324 | default: 325 | LOGE("invalid buf type\n"); 326 | RETURN_ERR(EINVAL, -1); 327 | } 328 | 329 | fmt = rkmpp_find_fmt(ctx, pix_fmt_mp->pixelformat); 330 | if (!fmt) { 331 | LOGE("failed to find %s format\n", 332 | compressed ? "compressed" : "raw"); 333 | RETURN_ERR(EINVAL, -1); 334 | } 335 | 336 | if (compressed) { 337 | if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { 338 | LOGE("sizeimage of compressed format must be given\n"); 339 | RETURN_ERR(EINVAL, -1); 340 | } 341 | 342 | pix_fmt_mp->num_planes = fmt->num_planes; 343 | pix_fmt_mp->plane_fmt[0].bytesperline = 0; 344 | } else if (ctx->is_decoder && ctx->capture.format.pixelformat) { 345 | struct rkmpp_buf_queue *queue = &ctx->capture; 346 | assert(queue->format.pixelformat == pix_fmt_mp->pixelformat); 347 | 348 | /* Use the decoded video format info */ 349 | *pix_fmt_mp = queue->format; 350 | } else { 351 | const struct rkmpp_fmt *codec_fmt; 352 | uint32_t sizeimage; 353 | 354 | if (ctx->is_decoder) 355 | codec_fmt = ctx->output.rkmpp_format; 356 | else 357 | codec_fmt = ctx->capture.rkmpp_format; 358 | 359 | pix_fmt_mp->num_planes = fmt->num_planes; 360 | 361 | /* Round up to macroblocks. */ 362 | pix_fmt_mp->width = round_up(pix_fmt_mp->width, RKMPP_MB_DIM); 363 | pix_fmt_mp->height = round_up(pix_fmt_mp->height, RKMPP_MB_DIM); 364 | 365 | if (!codec_fmt) { 366 | LOGE("the codec format isn't configured\n"); 367 | } else { 368 | /* Special alignment for VP9. */ 369 | if (codec_fmt->fourcc == V4L2_PIX_FMT_VP9) { 370 | pix_fmt_mp->width = 371 | round_up(pix_fmt_mp->width, 256) | 256; 372 | pix_fmt_mp->height = 373 | round_up(pix_fmt_mp->height, 64); 374 | } 375 | 376 | /* Limit to hardware min/max. */ 377 | pix_fmt_mp->width = clamp(pix_fmt_mp->width, 378 | codec_fmt->frmsize.min_width, 379 | codec_fmt->frmsize.max_width); 380 | pix_fmt_mp->height = clamp(pix_fmt_mp->height, 381 | codec_fmt->frmsize.min_height, 382 | codec_fmt->frmsize.max_height); 383 | } 384 | 385 | /* Fill in remaining fields. */ 386 | calculate_plane_sizes(fmt, pix_fmt_mp); 387 | 388 | sizeimage = calculate_format_size(fmt, pix_fmt_mp); 389 | 390 | LOGV(1, "calculated %dx%d:%d for %s format\n", 391 | pix_fmt_mp->width, pix_fmt_mp->height, sizeimage, 392 | codec_fmt ? codec_fmt->name : "unknown"); 393 | } 394 | 395 | LEAVE(); 396 | return 0; 397 | } 398 | 399 | int rkmpp_s_fmt(struct rkmpp_context *ctx, struct v4l2_format *f) 400 | { 401 | struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; 402 | struct rkmpp_buf_queue *queue; 403 | int ret; 404 | 405 | ENTER(); 406 | 407 | queue = rkmpp_get_queue(ctx, f->type); 408 | if (!queue) 409 | RETURN_ERR(errno, -1); 410 | 411 | if (queue->streaming) { 412 | LOGE("cannot do s_fmt during streaming\n"); 413 | RETURN_ERR(EBUSY, -1); 414 | } 415 | 416 | if (queue->num_buffers) { 417 | LOGE("cannot do s_fmt after reqbufs\n"); 418 | RETURN_ERR(EBUSY, -1); 419 | } 420 | 421 | CLEAR_AFTER_FIELD(f, fmt.pix_mp); 422 | 423 | ret = rkmpp_try_fmt(ctx, f); 424 | if (ret) { 425 | LOGE("failed to try fmt\n"); 426 | RETURN_ERR(EINVAL, -1); 427 | } 428 | 429 | queue->rkmpp_format = 430 | rkmpp_find_fmt(ctx, pix_fmt_mp->pixelformat); 431 | queue->format = *pix_fmt_mp; 432 | 433 | LEAVE(); 434 | return 0; 435 | } 436 | 437 | int rkmpp_g_fmt(struct rkmpp_context *ctx, struct v4l2_format *f) 438 | { 439 | struct rkmpp_buf_queue *queue; 440 | 441 | ENTER(); 442 | 443 | LOGV(1, "f->type = %d\n", f->type); 444 | 445 | queue = rkmpp_get_queue(ctx, f->type); 446 | if (!queue) 447 | RETURN_ERR(errno, -1); 448 | 449 | f->fmt.pix_mp = queue->format; 450 | 451 | LEAVE(); 452 | return 0; 453 | } 454 | 455 | int rkmpp_reqbufs(struct rkmpp_context *ctx, 456 | struct v4l2_requestbuffers *reqbufs) 457 | { 458 | struct rkmpp_buf_queue *queue; 459 | MppBufferInfo commit; 460 | MppBuffer buffer; 461 | MPP_RET ret; 462 | uint32_t sizeimage; 463 | unsigned int i; 464 | 465 | ENTER(); 466 | 467 | queue = rkmpp_get_queue(ctx, reqbufs->type); 468 | if (!queue) 469 | RETURN_ERR(errno, -1); 470 | 471 | if (queue->streaming) { 472 | LOGE("cannot do reqbufs during streaming\n"); 473 | RETURN_ERR(EBUSY, -1); 474 | } 475 | 476 | if (!reqbufs->count) { 477 | LOGV(1, "release buffers\n"); 478 | rkmpp_destroy_buffers(queue); 479 | goto out; 480 | } 481 | 482 | if (queue->num_buffers) 483 | rkmpp_destroy_buffers(queue); 484 | 485 | for (i = 0, sizeimage = 0; i < queue->format.num_planes; i++) 486 | sizeimage += queue->format.plane_fmt[i].sizeimage; 487 | 488 | if (!sizeimage) { 489 | LOGE("unable to create buffers\n"); 490 | goto err; 491 | } 492 | 493 | LOGV(1, "sizeimage: %d, count: %d\n", sizeimage, reqbufs->count); 494 | 495 | pthread_mutex_lock(&queue->queue_mutex); 496 | TAILQ_INIT(&queue->avail_buffers); 497 | TAILQ_INIT(&queue->pending_buffers); 498 | pthread_mutex_unlock(&queue->queue_mutex); 499 | 500 | /* Update poll event after avail list changed */ 501 | rkmpp_update_poll_event(ctx); 502 | 503 | queue->memory = reqbufs->memory; 504 | queue->num_buffers = reqbufs->count; 505 | 506 | queue->buffers = (struct rkmpp_buffer *) 507 | calloc(queue->num_buffers, sizeof(struct rkmpp_buffer)); 508 | if (!queue->buffers) 509 | goto err; 510 | 511 | /* Allocate all buffers from main buffer pool */ 512 | for (i = 0; i < queue->num_buffers; i++) { 513 | ret = mpp_buffer_get(queue->internal_group, 514 | &buffer, sizeimage); 515 | if (ret != MPP_OK) { 516 | LOGE("unable to alloc buffer\n"); 517 | goto err; 518 | } 519 | 520 | mpp_buffer_set_index(buffer, i); 521 | queue->buffers[i].rkmpp_buf = buffer; 522 | queue->buffers[i].fd = mpp_buffer_get_fd(buffer); 523 | queue->buffers[i].size = sizeimage; 524 | queue->buffers[i].index = i; 525 | queue->buffers[i].type = reqbufs->type; 526 | queue->buffers[i].length = queue->format.num_planes; 527 | queue->buffers[i].planes[0].length = sizeimage; 528 | rkmpp_buffer_set_locked(&queue->buffers[i]); 529 | 530 | LOGV(1, "create buffer(%d), fd: %d\n", 531 | i, queue->buffers[i].fd); 532 | } 533 | 534 | if (!queue->external_group) 535 | goto out; 536 | 537 | /* External buffer mode (for pre-allocated buffers) */ 538 | for (i = 0; i < queue->num_buffers; i++) { 539 | buffer = queue->buffers[i].rkmpp_buf; 540 | 541 | /* Move buffers into queue's buffer pool */ 542 | mpp_buffer_info_get(buffer, &commit); 543 | ret = mpp_buffer_commit(queue->external_group, &commit); 544 | if (ret != MPP_OK) { 545 | LOGE("unable to commit buffer\n"); 546 | goto err; 547 | } 548 | mpp_buffer_put(buffer); 549 | 550 | /* Lock all buffers again */ 551 | ret = mpp_buffer_get(queue->external_group, &buffer, sizeimage); 552 | if (ret != MPP_OK) { 553 | LOGE("unable to lock buffer\n"); 554 | goto err; 555 | } 556 | 557 | queue->buffers[i].rkmpp_buf = buffer; 558 | queue->buffers[i].fd = mpp_buffer_get_fd(buffer); 559 | 560 | LOGV(1, "create external buffer(%d), fd: %d\n", 561 | i, queue->buffers[i].fd); 562 | } 563 | 564 | out: 565 | LEAVE(); 566 | return 0; 567 | err: 568 | rkmpp_destroy_buffers(queue); 569 | RETURN_ERR(EIO, -1); 570 | } 571 | 572 | int rkmpp_querybuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer) 573 | { 574 | struct rkmpp_buf_queue *queue; 575 | struct rkmpp_buffer *rkmpp_buffer; 576 | int ret; 577 | 578 | ENTER(); 579 | 580 | queue = rkmpp_get_queue(ctx, buffer->type); 581 | if (!queue) 582 | RETURN_ERR(errno, -1); 583 | 584 | if (queue->num_buffers <= buffer->index) { 585 | LOGE("invalid buf index: %d\n", buffer->index); 586 | RETURN_ERR(EINVAL, -1); 587 | } 588 | 589 | rkmpp_buffer = &queue->buffers[buffer->index]; 590 | 591 | ret = rkmpp_to_v4l2_buffer(ctx, rkmpp_buffer, buffer); 592 | if (ret < 0) { 593 | LOGE("failed to convert buffer\n"); 594 | RETURN_ERR(EINVAL, -1); 595 | } 596 | 597 | LEAVE(); 598 | return 0; 599 | } 600 | 601 | int rkmpp_expbuf(struct rkmpp_context *ctx, struct v4l2_exportbuffer *expbuf) 602 | { 603 | struct rkmpp_buf_queue *queue; 604 | struct rkmpp_buffer *rkmpp_buffer; 605 | 606 | ENTER(); 607 | 608 | queue = rkmpp_get_queue(ctx, expbuf->type); 609 | if (!queue) 610 | RETURN_ERR(errno, -1); 611 | 612 | if (queue->num_buffers <= expbuf->index) { 613 | LOGE("invalid buf index: %d\n", expbuf->index); 614 | RETURN_ERR(EINVAL, -1); 615 | } 616 | 617 | if (expbuf->plane != 0) { 618 | LOGE("invalid buf plane: %d\n", expbuf->plane); 619 | RETURN_ERR(EINVAL, -1); 620 | } 621 | 622 | if (queue->memory != V4L2_MEMORY_MMAP) { 623 | LOGE("only support expbuf for MMAP\n"); 624 | RETURN_ERR(EINVAL, -1); 625 | } 626 | 627 | rkmpp_buffer = &queue->buffers[expbuf->index]; 628 | 629 | /* The userspace would close it at the end */ 630 | expbuf->fd = dup(rkmpp_buffer->fd); 631 | 632 | LOGV(1, "export buf(%d), type: %d, fd: %d(%d)\n", 633 | expbuf->index, expbuf->type, expbuf->fd, rkmpp_buffer->fd); 634 | 635 | rkmpp_buffer_set_exported(rkmpp_buffer); 636 | 637 | LEAVE(); 638 | return 0; 639 | } 640 | 641 | int rkmpp_qbuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer) 642 | { 643 | struct rkmpp_buf_queue *queue; 644 | struct rkmpp_buffer *rkmpp_buffer; 645 | int ret; 646 | 647 | ENTER(); 648 | 649 | queue = rkmpp_get_queue(ctx, buffer->type); 650 | if (!queue) 651 | RETURN_ERR(errno, -1); 652 | 653 | if (queue->num_buffers <= buffer->index) { 654 | LOGE("invalid buf index: %d\n", buffer->index); 655 | RETURN_ERR(EINVAL, -1); 656 | } 657 | 658 | rkmpp_buffer = &queue->buffers[buffer->index]; 659 | 660 | ret = rkmpp_from_v4l2_buffer(ctx, buffer, rkmpp_buffer); 661 | if (ret < 0) { 662 | LOGE("failed to convert buffer\n"); 663 | RETURN_ERR(EINVAL, -1); 664 | } 665 | 666 | rkmpp_buffer_set_queued(rkmpp_buffer); 667 | 668 | pthread_mutex_lock(&queue->queue_mutex); 669 | TAILQ_INSERT_TAIL(&queue->pending_buffers, 670 | rkmpp_buffer, entry); 671 | rkmpp_buffer_set_pending(rkmpp_buffer); 672 | pthread_mutex_unlock(&queue->queue_mutex); 673 | 674 | LOGV(2, "enqueue buffer: %d(%ld), size: %d, type: %d, fd: %d\n", 675 | buffer->index, buffer->timestamp.tv_sec, 676 | rkmpp_buffer->bytesused, buffer->type, 677 | rkmpp_buffer->fd); 678 | 679 | LEAVE(); 680 | return 0; 681 | } 682 | 683 | static int rkmpp_try_dqbuf(struct rkmpp_context *ctx, 684 | struct rkmpp_buf_queue *queue, 685 | struct v4l2_buffer *buffer) 686 | { 687 | struct rkmpp_buffer *rkmpp_buffer; 688 | int ret; 689 | 690 | if (!queue->streaming) { 691 | LOGE("cannot dequeue buffer when not streaming\n"); 692 | RETURN_ERR(EINVAL, -1); 693 | } 694 | 695 | if (TAILQ_EMPTY(&queue->avail_buffers)) { 696 | errno = EAGAIN; 697 | return -1; 698 | } 699 | 700 | pthread_mutex_lock(&queue->queue_mutex); 701 | rkmpp_buffer = TAILQ_FIRST(&queue->avail_buffers); 702 | pthread_mutex_unlock(&queue->queue_mutex); 703 | 704 | ret = rkmpp_to_v4l2_buffer(ctx, rkmpp_buffer, buffer); 705 | if (ret < 0) { 706 | LOGE("failed to convert buffer\n"); 707 | RETURN_ERR(EINVAL, -1); 708 | } 709 | 710 | pthread_mutex_lock(&queue->queue_mutex); 711 | rkmpp_buffer_clr_available(rkmpp_buffer); 712 | TAILQ_REMOVE(&queue->avail_buffers, rkmpp_buffer, entry); 713 | pthread_mutex_unlock(&queue->queue_mutex); 714 | 715 | /* Update poll event after avail list changed */ 716 | rkmpp_update_poll_event(ctx); 717 | 718 | rkmpp_buffer_clr_queued(rkmpp_buffer); 719 | 720 | LOGV(2, "dequeue buffer: %d(%" PRIu64 "), size: %d, type: %d\n", 721 | buffer->index, rkmpp_buffer->timestamp, 722 | rkmpp_buffer->bytesused, buffer->type); 723 | 724 | LEAVE(); 725 | return 0; 726 | } 727 | 728 | int rkmpp_dqbuf(struct rkmpp_context *ctx, struct v4l2_buffer *buffer) 729 | { 730 | struct rkmpp_buf_queue *queue; 731 | int ret; 732 | 733 | ENTER(); 734 | 735 | queue = rkmpp_get_queue(ctx, buffer->type); 736 | if (!queue) 737 | RETURN_ERR(errno, -1); 738 | 739 | while (true) { 740 | ret = rkmpp_try_dqbuf(ctx, queue, buffer); 741 | if (!ret || errno != EAGAIN || ctx->nonblock) 742 | break; 743 | 744 | /* Wait for buffers in block mode */ 745 | 746 | /* Unlock it to let the worker thread run */ 747 | pthread_mutex_unlock(&ctx->ioctl_mutex); 748 | pthread_mutex_lock(&queue->queue_mutex); 749 | pthread_cond_wait(&queue->queue_cond, 750 | &queue->queue_mutex); 751 | pthread_mutex_unlock(&queue->queue_mutex); 752 | pthread_mutex_lock(&ctx->ioctl_mutex); 753 | } 754 | 755 | LEAVE(); 756 | return ret; 757 | } 758 | 759 | void rkmpp_streamon(struct rkmpp_context *ctx) 760 | { 761 | if (ctx->mpp_streaming) 762 | return; 763 | 764 | ENTER(); 765 | 766 | LOGV(1, "mpp start streaming\n"); 767 | 768 | /* Notify the worker thread to start streaming */ 769 | pthread_mutex_lock(&ctx->worker_mutex); 770 | ctx->pausing = false; 771 | ctx->mpp_produced = false; 772 | ctx->mpp_streaming = true; 773 | pthread_cond_signal(&ctx->worker_cond); 774 | pthread_mutex_unlock(&ctx->worker_mutex); 775 | 776 | LEAVE(); 777 | } 778 | 779 | void rkmpp_streamoff(struct rkmpp_context *ctx) 780 | { 781 | if (!ctx->mpp_streaming) 782 | return; 783 | 784 | ENTER(); 785 | 786 | LOGV(1, "mpp stop streaming\n"); 787 | 788 | pthread_mutex_lock(&ctx->worker_mutex); 789 | ctx->mpp_streaming = false; 790 | 791 | ctx->mpi->reset(ctx->mpp); 792 | mpp_destroy(ctx->mpp); 793 | pthread_mutex_unlock(&ctx->worker_mutex); 794 | 795 | LEAVE(); 796 | } 797 | 798 | void rkmpp_cancel_flushing(struct rkmpp_context *ctx) 799 | { 800 | if (!rkmpp_buffer_pending(&ctx->eos_buffer)) 801 | return; 802 | 803 | /* Clear EOS if pending */ 804 | pthread_mutex_lock(&ctx->output.queue_mutex); 805 | TAILQ_REMOVE(&ctx->output.pending_buffers, &ctx->eos_buffer, entry); 806 | rkmpp_buffer_clr_pending(&ctx->eos_buffer); 807 | pthread_mutex_unlock(&ctx->output.queue_mutex); 808 | } 809 | 810 | void rkmpp_exit_flushing(struct rkmpp_context *ctx) 811 | { 812 | LOGV(1, "mpp exit flushing\n"); 813 | 814 | rkmpp_cancel_flushing(ctx); 815 | 816 | /* Unpause and wakeup worker thread */ 817 | pthread_mutex_lock(&ctx->worker_mutex); 818 | ctx->pausing = false; 819 | pthread_cond_signal(&ctx->worker_cond); 820 | pthread_mutex_unlock(&ctx->worker_mutex); 821 | } 822 | 823 | void rkmpp_start_flushing(struct rkmpp_context *ctx) 824 | { 825 | LOGV(1, "mpp start flushing\n"); 826 | 827 | rkmpp_cancel_flushing(ctx); 828 | 829 | /* Enqueue EOS for flushing */ 830 | pthread_mutex_lock(&ctx->output.queue_mutex); 831 | TAILQ_INSERT_TAIL(&ctx->output.pending_buffers, 832 | &ctx->eos_buffer, entry); 833 | rkmpp_buffer_set_pending(&ctx->eos_buffer); 834 | pthread_mutex_unlock(&ctx->output.queue_mutex); 835 | 836 | /* Wakeup worker thread */ 837 | pthread_mutex_lock(&ctx->worker_mutex); 838 | pthread_cond_signal(&ctx->worker_cond); 839 | pthread_mutex_unlock(&ctx->worker_mutex); 840 | } 841 | 842 | void rkmpp_finish_flushing(struct rkmpp_context *ctx, 843 | struct rkmpp_buffer *rkmpp_buffer) 844 | { 845 | LOGV(1, "mpp finish flushing\n"); 846 | 847 | /* Return a last empty buffer to represent flush finished */ 848 | assert(ctx->capture.streaming); 849 | 850 | rkmpp_buffer->bytesused = 0; 851 | rkmpp_buffer->timestamp = 0; 852 | rkmpp_buffer_set_last(rkmpp_buffer); 853 | 854 | pthread_mutex_lock(&ctx->capture.queue_mutex); 855 | TAILQ_INSERT_TAIL(&ctx->capture.avail_buffers, 856 | rkmpp_buffer, entry); 857 | rkmpp_buffer_set_available(rkmpp_buffer); 858 | pthread_mutex_unlock(&ctx->capture.queue_mutex); 859 | } 860 | 861 | int rkmpp_update_poll_event(struct rkmpp_context *ctx) 862 | { 863 | eventfd_t event; 864 | bool has_event; 865 | int ret; 866 | 867 | ENTER(); 868 | 869 | if (ctx->is_decoder) 870 | has_event = rkmpp_dec_has_event(ctx->data); 871 | else 872 | has_event = rkmpp_enc_has_event(ctx->data); 873 | 874 | has_event |= !TAILQ_EMPTY(&ctx->output.avail_buffers); 875 | has_event |= !TAILQ_EMPTY(&ctx->capture.avail_buffers); 876 | 877 | /* Report POLLIN event */ 878 | if (has_event) 879 | ret = eventfd_write(ctx->eventfd, 1); 880 | else 881 | ret = eventfd_read(ctx->eventfd, &event); 882 | 883 | LEAVE(); 884 | return ret; 885 | } 886 | 887 | static int rkmpp_parse_options(struct rkmpp_context *ctx, int fd) 888 | { 889 | ENTER(); 890 | 891 | #define MAX_OPT_LEN 1024 892 | #define OPT_DEC "dec" 893 | #define OPT_ENC "enc" 894 | #define OPT_TYPE "type=" 895 | #define OPT_LOG_LEVEL "log-level=" 896 | #define OPT_LOG_FPS "log-fps=" 897 | #define OPT_MAX_WIDTH "max-width=" 898 | #define OPT_MAX_HEIGHT "max-height=" 899 | #define OPT_CODECS "codecs=" 900 | #define OPT_MATCH(o) (!strncmp(option, o, strlen(o))) 901 | #define OPT_VALUE_INT(o) (atoi(&option[strlen(o)])) 902 | #define OPT_VALUE_STR(o) (&option[strlen(o)]) 903 | #define IS_SPACE(c) \ 904 | ((c) == '\r' || (c) == '\n' || (c) == ' ' || (c) == '\t') 905 | 906 | while (1) { 907 | char option[MAX_OPT_LEN + 1] = {0,}; 908 | char c; 909 | int n = 0; 910 | 911 | while (read(fd, &c, 1) > 0) { 912 | if (IS_SPACE(c)) { 913 | /* End of one option */ 914 | if (n) 915 | break; 916 | 917 | /* Eat leading spaces */ 918 | continue; 919 | } 920 | 921 | option[n++] = c; 922 | if (n == MAX_OPT_LEN) 923 | break; 924 | } 925 | 926 | /* End of options */ 927 | if (!n) 928 | break; 929 | 930 | LOGV(1, "parsing option: %s\n", option); 931 | 932 | if (OPT_MATCH(OPT_DEC)) { 933 | ctx->is_decoder = true; 934 | } else if (OPT_MATCH(OPT_ENC)) { 935 | ctx->is_decoder = false; 936 | } else if (OPT_MATCH(OPT_TYPE)) { 937 | ctx->is_decoder = 938 | !strcmp(OPT_VALUE_STR(OPT_TYPE), "dec"); 939 | } else if (OPT_MATCH(OPT_LOG_LEVEL)) { 940 | rkmpp_log_level = OPT_VALUE_INT(OPT_LOG_LEVEL); 941 | } else if (OPT_MATCH(OPT_LOG_FPS)) { 942 | rkmpp_log_fps = OPT_VALUE_INT(OPT_LOG_FPS); 943 | } else if (OPT_MATCH(OPT_MAX_WIDTH)) { 944 | ctx->max_width = OPT_VALUE_INT(OPT_MAX_WIDTH); 945 | } else if (OPT_MATCH(OPT_MAX_HEIGHT)) { 946 | ctx->max_height = OPT_VALUE_INT(OPT_MAX_HEIGHT); 947 | } else if (OPT_MATCH(OPT_CODECS)) { 948 | if (ctx->codecs) 949 | free(ctx->codecs); 950 | ctx->codecs = strdup(OPT_VALUE_STR(OPT_CODECS)); 951 | } else { 952 | LOGV(1, "unknown options\n"); 953 | RETURN_ERR(ENODEV, -1); 954 | } 955 | } 956 | 957 | if (fcntl(fd, F_GETFL) & O_NONBLOCK) 958 | ctx->nonblock = true; 959 | 960 | LEAVE(); 961 | return 0; 962 | } 963 | 964 | static void *plugin_init(int fd) 965 | { 966 | struct rkmpp_context *ctx = NULL; 967 | struct epoll_event ev; 968 | struct stat stat; 969 | unsigned int i; 970 | int epollfd; 971 | MPP_RET ret; 972 | 973 | ENTER(); 974 | 975 | /* Filter out invalid fd and real devices */ 976 | if (fstat(fd, &stat) < 0 || S_ISCHR(stat.st_mode)) 977 | RETURN_ERR(errno, NULL); 978 | 979 | pthread_once(&g_rkmpp_global_init_once, rkmpp_global_init); 980 | 981 | ctx = (struct rkmpp_context *) 982 | calloc(1, sizeof(struct rkmpp_context)); 983 | if (!ctx) 984 | RETURN_ERR(ENOMEM, NULL); 985 | 986 | if (rkmpp_parse_options(ctx, fd) < 0){ 987 | LOGV(1, "failed to parse option\n"); 988 | goto err_free_ctx; 989 | } 990 | 991 | /* Create eventfd to fake poll events */ 992 | ctx->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 993 | if (ctx->eventfd < 0) { 994 | LOGE("failed to create eventfd\n"); 995 | goto err_free_ctx; 996 | } 997 | 998 | epollfd = epoll_create(1); 999 | if (epollfd < 0) { 1000 | LOGE("failed to create epollfd\n"); 1001 | goto err_close_eventfd; 1002 | } 1003 | 1004 | /* Filter out eventfd's POLLOUT, since it would be always generated */ 1005 | ev.events = EPOLLIN | EPOLLET; 1006 | ev.data.fd = ctx->eventfd; 1007 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctx->eventfd, &ev) < 0) { 1008 | LOGE("failed to add eventfd\n"); 1009 | goto err_close_epollfd; 1010 | } 1011 | 1012 | if (dup2(epollfd, fd) < 0) { 1013 | LOGE("failed to dup fd\n"); 1014 | goto err_close_epollfd; 1015 | } 1016 | close(epollfd); 1017 | 1018 | pthread_mutex_init(&ctx->ioctl_mutex, NULL); 1019 | pthread_cond_init(&ctx->output.queue_cond, NULL); 1020 | pthread_mutex_init(&ctx->output.queue_mutex, NULL); 1021 | pthread_cond_init(&ctx->capture.queue_cond, NULL); 1022 | pthread_mutex_init(&ctx->capture.queue_mutex, NULL); 1023 | pthread_cond_init(&ctx->worker_cond, NULL); 1024 | pthread_mutex_init(&ctx->worker_mutex, NULL); 1025 | 1026 | ret = mpp_buffer_group_get_internal(&ctx->output.internal_group, 1027 | MPP_BUFFER_TYPE_DRM); 1028 | if (ret != MPP_OK) { 1029 | LOGE("failed to use mpp drm buf group\n"); 1030 | errno = ENODEV; 1031 | goto err_close_eventfd; 1032 | } 1033 | 1034 | ret = mpp_buffer_group_get_internal(&ctx->capture.internal_group, 1035 | MPP_BUFFER_TYPE_DRM); 1036 | if (ret != MPP_OK) { 1037 | LOGE("failed to use mpp drm buf group\n"); 1038 | errno = ENODEV; 1039 | goto err_put_group; 1040 | } 1041 | 1042 | if (ctx->is_decoder) 1043 | ctx->data = rkmpp_dec_init(ctx); 1044 | else 1045 | ctx->data = rkmpp_enc_init(ctx); 1046 | 1047 | if (!ctx->data) 1048 | goto err_put_group; 1049 | 1050 | for (i = 0; i < ctx->num_formats; i++) { 1051 | struct rkmpp_fmt *fmt = &ctx->formats[i]; 1052 | if (fmt->type == MPP_VIDEO_CodingNone) 1053 | continue; 1054 | 1055 | fmt->frmsize.max_width = ctx->max_width; 1056 | fmt->frmsize.max_height = ctx->max_height; 1057 | } 1058 | 1059 | ctx->eos_buffer.index = -1; 1060 | rkmpp_buffer_set_last(&ctx->eos_buffer); 1061 | 1062 | LOGV(1, "ctx(%p): plugin inited\n", (void *)ctx); 1063 | 1064 | LEAVE(); 1065 | return ctx; 1066 | err_put_group: 1067 | if (ctx->output.internal_group) 1068 | mpp_buffer_group_put(ctx->output.internal_group); 1069 | 1070 | if (ctx->capture.internal_group) 1071 | mpp_buffer_group_put(ctx->capture.internal_group); 1072 | err_close_epollfd: 1073 | close(epollfd); 1074 | err_close_eventfd: 1075 | close(ctx->eventfd); 1076 | err_free_ctx: 1077 | free(ctx); 1078 | RETURN_ERR(errno, NULL); 1079 | } 1080 | 1081 | static void plugin_close(void *dev_ops_priv) 1082 | { 1083 | struct rkmpp_context *ctx = dev_ops_priv; 1084 | 1085 | ENTER(); 1086 | 1087 | LOGV(1, "ctx(%p): closing plugin\n", (void *)ctx); 1088 | 1089 | if (ctx->worker_thread) { 1090 | pthread_cancel(ctx->worker_thread); 1091 | pthread_join(ctx->worker_thread, NULL); 1092 | } 1093 | 1094 | if (ctx->is_decoder) 1095 | rkmpp_dec_deinit(ctx->data); 1096 | else 1097 | rkmpp_enc_deinit(ctx->data); 1098 | 1099 | if (ctx->mpp_streaming) { 1100 | ctx->mpi->reset(ctx->mpp); 1101 | mpp_destroy(ctx->mpp); 1102 | } 1103 | 1104 | rkmpp_destroy_buffers(&ctx->output); 1105 | 1106 | if (ctx->output.internal_group) 1107 | mpp_buffer_group_put(ctx->output.internal_group); 1108 | 1109 | if (ctx->output.external_group) 1110 | mpp_buffer_group_put(ctx->output.external_group); 1111 | 1112 | rkmpp_destroy_buffers(&ctx->capture); 1113 | 1114 | if (ctx->capture.external_group) 1115 | mpp_buffer_group_put(ctx->capture.external_group); 1116 | 1117 | if (ctx->capture.internal_group) 1118 | mpp_buffer_group_put(ctx->capture.internal_group); 1119 | 1120 | if (ctx->codecs) 1121 | free(ctx->codecs); 1122 | 1123 | close(ctx->eventfd); 1124 | 1125 | LEAVE(); 1126 | 1127 | free(ctx); 1128 | } 1129 | 1130 | static int plugin_ioctl(void *dev_ops_priv, int fd, 1131 | unsigned long cmd, void *arg) 1132 | { 1133 | (void)fd; /* unused */ 1134 | 1135 | struct rkmpp_context *ctx = dev_ops_priv; 1136 | int ret; 1137 | 1138 | ENTER(); 1139 | 1140 | pthread_mutex_lock(&ctx->ioctl_mutex); 1141 | 1142 | LOGV(4, "ctx(%p): %s\n", (void *)ctx, rkmpp_cmd2str(cmd)); 1143 | 1144 | if (ctx->is_decoder) 1145 | ret = rkmpp_dec_ioctl(ctx->data, cmd, arg); 1146 | else 1147 | ret = rkmpp_enc_ioctl(ctx->data, cmd, arg); 1148 | 1149 | LOGV(4, "ctx(%p): %s ret: %d\n", (void *)ctx, rkmpp_cmd2str(cmd), ret); 1150 | 1151 | pthread_mutex_unlock(&ctx->ioctl_mutex); 1152 | 1153 | LEAVE(); 1154 | return ret; 1155 | } 1156 | 1157 | static void *plugin_mmap(void *dev_ops_priv, void *start, 1158 | size_t length, int prot, int flags, 1159 | int fd, int64_t offset) 1160 | { 1161 | (void)fd; /* unused */ 1162 | 1163 | struct rkmpp_context *ctx = dev_ops_priv; 1164 | struct rkmpp_buffer *rkmpp_buffer; 1165 | struct rkmpp_buf_queue *queue; 1166 | void *ptr; 1167 | unsigned int index; 1168 | 1169 | ENTER(); 1170 | 1171 | if (start) { 1172 | LOGE("only support start=NULL\n"); 1173 | RETURN_ERR(EINVAL, NULL); 1174 | } 1175 | 1176 | if (!offset) { 1177 | LOGE("only support mapping plane 0\n"); 1178 | RETURN_ERR(EINVAL, NULL); 1179 | } 1180 | 1181 | queue = rkmpp_get_queue(ctx, RKMPP_MEM_OFFSET_TYPE(offset)); 1182 | if (!queue) 1183 | RETURN_ERR(errno, NULL); 1184 | 1185 | if (queue->memory != V4L2_MEMORY_MMAP) { 1186 | LOGE("only support mmap for V4L2_MEMORY_MMAP\n"); 1187 | RETURN_ERR(EINVAL, NULL); 1188 | } 1189 | 1190 | index = RKMPP_MEM_OFFSET_INDEX(offset); 1191 | if (queue->num_buffers <= index) { 1192 | LOGE("invalid buf index: %d\n", index); 1193 | RETURN_ERR(EINVAL, NULL); 1194 | } 1195 | 1196 | rkmpp_buffer = &queue->buffers[index]; 1197 | ptr = mmap(start, length, prot, flags, rkmpp_buffer->fd, 0); 1198 | 1199 | LOGV(1, "mmap buffer(%d): %p, fd: %d\n", index, ptr, rkmpp_buffer->fd); 1200 | 1201 | LEAVE(); 1202 | return ptr; 1203 | } 1204 | 1205 | PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = { 1206 | .init = &plugin_init, 1207 | .close = &plugin_close, 1208 | .ioctl = &plugin_ioctl, 1209 | .mmap = &plugin_mmap, 1210 | }; 1211 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | 17 | #include "libv4l-rkmpp.h" 18 | 19 | static int rkmpp_copy_buffer(struct rkmpp_context *ctx, 20 | struct rkmpp_buffer *rkmpp_buffer, 21 | struct v4l2_buffer *buffer, int copy_to) 22 | { 23 | struct rkmpp_buf_queue *queue; 24 | char *rkmpp_ptr; 25 | char *addrs[3] = {0}; 26 | uint32_t sizes[3], offsets[3]; 27 | unsigned int i; 28 | int ret = -1; 29 | 30 | ENTER(); 31 | 32 | if (buffer->length > 3) { 33 | LOGE("wrong buffer planes: %d\n", buffer->length); 34 | return -1; 35 | } 36 | 37 | /* Nothing to do for mmap buffer */ 38 | if (buffer->memory == V4L2_MEMORY_MMAP) 39 | return 0; 40 | 41 | queue = rkmpp_get_queue(ctx, buffer->type); 42 | if (!queue) 43 | return -1; 44 | 45 | rkmpp_ptr = mpp_buffer_get_ptr(rkmpp_buffer->rkmpp_buf); 46 | 47 | /* Prepare access */ 48 | for (i = 0; i < buffer->length; i++) { 49 | sizes[i] = rkmpp_buffer->planes[i].plane_size; 50 | offsets[i] = rkmpp_buffer->planes[i].data_offset; 51 | 52 | if (!sizes[i]) 53 | goto out; 54 | 55 | if (buffer->memory == V4L2_MEMORY_DMABUF) { 56 | int fd = rkmpp_buffer->planes[i].fd; 57 | addrs[i] = mmap(NULL, sizes[i], 58 | PROT_READ | PROT_WRITE, 59 | MAP_SHARED, fd, offsets[i]); 60 | if (addrs[i] == MAP_FAILED) 61 | goto out; 62 | } else { 63 | addrs[i] = (void *)rkmpp_buffer->planes[i].userptr; 64 | if (!addrs[i]) 65 | goto out; 66 | 67 | addrs[i] += offsets[i]; 68 | } 69 | 70 | offsets[i] = i == 0 ? 0 : offsets[i - 1] + sizes[i - 1]; 71 | } 72 | 73 | LOGV(3, "doing software copy\n"); 74 | 75 | for (i = 0; i < buffer->length; i++) { 76 | if (offsets[i] + sizes[i] > rkmpp_buffer->size) { 77 | LOGE("buffer overflow!\n"); 78 | goto out; 79 | } 80 | 81 | if (copy_to) { 82 | uint32_t size = rkmpp_buffer->planes[i].bytesused; 83 | 84 | if (!size) 85 | size = sizes[i]; 86 | 87 | memcpy(addrs[i], rkmpp_ptr + offsets[i], size); 88 | buffer->m.planes[i].bytesused = size; 89 | } else { 90 | uint32_t size = buffer->m.planes[i].bytesused; 91 | 92 | if (!size) 93 | size = sizes[i]; 94 | 95 | memcpy(rkmpp_ptr + offsets[i], addrs[i], size); 96 | rkmpp_buffer->planes[i].bytesused = size; 97 | } 98 | } 99 | 100 | ret = 0; 101 | out: 102 | if (buffer->memory == V4L2_MEMORY_DMABUF) { 103 | /* Finish access for dma buffer */ 104 | for (i = 0; i < buffer->length; i++) { 105 | if (!addrs[i] || addrs[i] == MAP_FAILED) 106 | break; 107 | 108 | munmap(addrs[i], sizes[i]); 109 | } 110 | } 111 | 112 | LEAVE(); 113 | return ret; 114 | } 115 | 116 | int rkmpp_to_v4l2_buffer(struct rkmpp_context *ctx, 117 | struct rkmpp_buffer *rkmpp_buffer, 118 | struct v4l2_buffer *buffer) 119 | { 120 | unsigned int i; 121 | 122 | ENTER(); 123 | 124 | if (buffer->length != rkmpp_buffer->length) { 125 | LOGE("wrong buffer planes: %d(expected: %d)\n", 126 | buffer->length, rkmpp_buffer->length); 127 | return -1; 128 | } 129 | 130 | for (i = 0; i < buffer->length; i++) { 131 | buffer->m.planes[i].length = rkmpp_buffer->planes[i].length; 132 | buffer->m.planes[i].data_offset = 133 | rkmpp_buffer->planes[i].data_offset; 134 | buffer->m.planes[i].bytesused = 0; 135 | 136 | if (buffer->memory == V4L2_MEMORY_MMAP) 137 | /* Only support mem_offset for plane 0 */ 138 | buffer->m.planes[i].m.mem_offset = i ? 0 : 139 | RKMPP_MEM_OFFSET(buffer->type, buffer->index); 140 | else if (buffer->memory == V4L2_MEMORY_USERPTR) 141 | buffer->m.planes[i].m.userptr = 142 | rkmpp_buffer->planes[i].userptr; 143 | else if (buffer->memory == V4L2_MEMORY_DMABUF) 144 | buffer->m.planes[i].m.fd = 145 | rkmpp_buffer->planes[i].fd; 146 | } 147 | 148 | if (rkmpp_buffer_available(rkmpp_buffer) && rkmpp_buffer->bytesused) { 149 | /* Returning data are always in plane 0 */ 150 | rkmpp_buffer->planes[0].bytesused = rkmpp_buffer->bytesused; 151 | buffer->m.planes[0].bytesused = rkmpp_buffer->bytesused; 152 | 153 | if (rkmpp_copy_buffer(ctx, rkmpp_buffer, buffer, 1) < 0) 154 | return -1; 155 | } 156 | 157 | buffer->timestamp.tv_sec = rkmpp_buffer->timestamp / 1000000; 158 | buffer->timestamp.tv_usec = rkmpp_buffer->timestamp % 1000000; 159 | 160 | buffer->flags = 0; 161 | if (rkmpp_buffer_keyframe(rkmpp_buffer)) 162 | buffer->flags |= V4L2_BUF_FLAG_KEYFRAME; 163 | if (rkmpp_buffer_error(rkmpp_buffer)) 164 | buffer->flags |= V4L2_BUF_FLAG_ERROR; 165 | if (rkmpp_buffer_last(rkmpp_buffer)) 166 | buffer->flags |= V4L2_BUF_FLAG_LAST; 167 | if (rkmpp_buffer_queued(rkmpp_buffer)) { 168 | buffer->flags |= V4L2_BUF_FLAG_QUEUED; 169 | if (rkmpp_buffer_available(rkmpp_buffer)) 170 | buffer->flags |= V4L2_BUF_FLAG_DONE; 171 | else 172 | buffer->flags |= V4L2_BUF_FLAG_PREPARED; 173 | } 174 | 175 | buffer->field = V4L2_FIELD_NONE; 176 | memset(&buffer->timecode, 0, sizeof(buffer->timecode)); 177 | buffer->sequence = 0; 178 | 179 | buffer->index = rkmpp_buffer->index; 180 | 181 | LEAVE(); 182 | return 0; 183 | } 184 | 185 | int rkmpp_from_v4l2_buffer(struct rkmpp_context *ctx, 186 | struct v4l2_buffer *buffer, 187 | struct rkmpp_buffer *rkmpp_buffer) 188 | { 189 | unsigned int i; 190 | 191 | ENTER(); 192 | 193 | rkmpp_buffer->length = buffer->length; 194 | rkmpp_buffer->bytesused = 0; 195 | 196 | for (i = 0; i < buffer->length; i++) { 197 | rkmpp_buffer->planes[i].length = buffer->m.planes[i].length; 198 | rkmpp_buffer->planes[i].data_offset = 199 | buffer->m.planes[i].data_offset; 200 | rkmpp_buffer->planes[i].bytesused = 201 | buffer->m.planes[i].bytesused; 202 | rkmpp_buffer->planes[i].plane_size = 203 | rkmpp_buffer->planes[i].bytesused - 204 | rkmpp_buffer->planes[i].data_offset; 205 | 206 | rkmpp_buffer->bytesused += 207 | rkmpp_buffer->planes[i].plane_size; 208 | 209 | if (buffer->memory == V4L2_MEMORY_USERPTR) 210 | rkmpp_buffer->planes[i].userptr = 211 | buffer->m.planes[i].m.userptr; 212 | else if (buffer->memory == V4L2_MEMORY_DMABUF) 213 | rkmpp_buffer->planes[i].fd = 214 | buffer->m.planes[i].m.fd; 215 | } 216 | 217 | if (rkmpp_buffer->bytesused) { 218 | if (rkmpp_copy_buffer(ctx, rkmpp_buffer, buffer, 0) < 0) 219 | return -1; 220 | } 221 | 222 | rkmpp_buffer->timestamp = 223 | (uint64_t)buffer->timestamp.tv_sec * 1000000 + 224 | buffer->timestamp.tv_usec; 225 | 226 | /* Clear special flags */ 227 | if (rkmpp_buffer_keyframe(rkmpp_buffer)) 228 | rkmpp_buffer_clr_keyframe(rkmpp_buffer); 229 | if (rkmpp_buffer_error(rkmpp_buffer)) 230 | rkmpp_buffer_clr_error(rkmpp_buffer); 231 | if (rkmpp_buffer_last(rkmpp_buffer)) 232 | rkmpp_buffer_clr_last(rkmpp_buffer); 233 | 234 | LEAVE(); 235 | return 0; 236 | } 237 | --------------------------------------------------------------------------------