From b0e4b1e8e961faba4b74ddf81f1c49534c7f1af5 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Tue, 13 Aug 2013 11:46:09 +0200 Subject: [PATCH] Initial commit Signed-off-by: Carlos Rafael Giani --- .gitignore | 3 + LICENSE | 481 ++++++++++++++++ README.md | 64 +++ src/common/vpu_bufferpool.c | 366 +++++++++++++ src/common/vpu_bufferpool.h | 92 ++++ src/common/vpu_framebuffers.c | 189 +++++++ src/common/vpu_framebuffers.h | 77 +++ src/common/vpu_utils.c | 145 +++++ src/common/vpu_utils.h | 47 ++ src/decoder/vpu_decoder.c | 997 ++++++++++++++++++++++++++++++++++ src/decoder/vpu_decoder.h | 80 +++ src/decoder/vpu_decplugin.c | 49 ++ waf | Bin 0 -> 91066 bytes wscript | 123 +++++ 14 files changed, 2713 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/common/vpu_bufferpool.c create mode 100644 src/common/vpu_bufferpool.h create mode 100644 src/common/vpu_framebuffers.c create mode 100644 src/common/vpu_framebuffers.h create mode 100644 src/common/vpu_utils.c create mode 100644 src/common/vpu_utils.h create mode 100644 src/decoder/vpu_decoder.c create mode 100644 src/decoder/vpu_decoder.h create mode 100644 src/decoder/vpu_decplugin.c create mode 100755 waf create mode 100644 wscript diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54f4107 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.lock-* +/build +/.waf* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/README.md b/README.md new file mode 100644 index 0000000..991384f --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +gst-fsl +======= + +IMPORTANT NOTE +-------------- + +**THIS SOFTWARE IS IN AN ALPHA STAGE. DO NOT USE FOR PRODUCTION YET.** + +About +----- + +This is a set of [GStreamer 1.0](http://gstreamer.freedesktop.org/) plugins for plugins for Freescale's +i.MX platform, with emphasis on video en/decoding using the i.MX VPU engine. Currently, a decoder is +implemented. Encoders, video sinks and more will follow soon. + +Currently, this software has been tested on the i.MX 6 Sabre SD Dual Lite platform only. + + +License +------- + +These plugins are licensed under the LGPL v2. + + +Available plugins +----------------- + +* `fslvpudec` : video decoder plugin + + +Dependencies +------------ + +You'll need a GStreamer 1.0 installation, and Freescale's VPU wrapper library (at least version 1.0.17). + + +Building and installing +----------------------- + +This project uses the [waf meta build system](https://code.google.com/p/waf/). To configure , first set +the following environment variables to whatever is necessary for cross compilation for your platform: + +* `CC` +* `CFLAGS` +* `LDFLAGS` +* `PKG_CONFIG_PATH` +* `PKG_CONFIG_SYSROOT_DIR` + +Then, run: + + ./waf configure --prefix=PREFIX + +(The aforementioned environment variables are only necessary for this configure call.) +PREFIX defines the installation prefix, that is, where the built binaries will be installed. + +Once configuration is complete, run: + + ./waf + +This builds the plugins. +Finally, to install, run: + + ./waf install + diff --git a/src/common/vpu_bufferpool.c b/src/common/vpu_bufferpool.c new file mode 100644 index 0000000..e1ae2a0 --- /dev/null +++ b/src/common/vpu_bufferpool.c @@ -0,0 +1,366 @@ +/* GStreamer buffer pool for wrapped VPU framebuffers + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include "vpu_bufferpool.h" +#include "vpu_utils.h" + + +GST_DEBUG_CATEGORY_STATIC(vpubufferpool_debug); +#define GST_CAT_DEFAULT vpubufferpool_debug + + +static gboolean gst_fsl_vpu_buffer_meta_init(GstMeta *meta, gpointer params, GstBuffer *buffer); +static void gst_fsl_vpu_buffer_meta_free(GstMeta *meta, GstBuffer *buffer); + +static void gst_fsl_vpu_buffer_pool_finalize(GObject *object); +static const gchar ** gst_fsl_vpu_buffer_pool_get_options(GstBufferPool *pool); +static gboolean gst_fsl_vpu_buffer_pool_set_config(GstBufferPool *pool, GstStructure *config); +static GstFlowReturn gst_fsl_vpu_buffer_pool_alloc_buffer(GstBufferPool *pool, GstBuffer **buffer, GstBufferPoolAcquireParams *params); +static void gst_fsl_vpu_buffer_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer); + + +G_DEFINE_TYPE(GstFslVpuBufferPool, gst_fsl_vpu_buffer_pool, GST_TYPE_BUFFER_POOL) + + + + +static gboolean gst_fsl_vpu_buffer_meta_init(GstMeta *meta, G_GNUC_UNUSED gpointer params, G_GNUC_UNUSED GstBuffer *buffer) +{ + GstFslVpuBufferMeta *fsl_vpu_meta = (GstFslVpuBufferMeta *)meta; + fsl_vpu_meta->framebuffer = NULL; + fsl_vpu_meta->not_displayed_yet = FALSE; + return TRUE; +} + + +static void gst_fsl_vpu_buffer_meta_free(GstMeta *meta, G_GNUC_UNUSED GstBuffer *buffer) +{ + GstFslVpuBufferMeta *fsl_vpu_meta = (GstFslVpuBufferMeta *)meta; + fsl_vpu_meta->framebuffer = NULL; +} + + +GType gst_fsl_vpu_buffer_meta_api_get_type(void) +{ + static volatile GType type; + static gchar const *tags[] = { "memory", "fsl_vpu", NULL }; + + if (g_once_init_enter(&type)) + { + GType _type = gst_meta_api_type_register("GstFslVpuBufferMetaAPI", tags); + g_once_init_leave(&type, _type); + } + + return type; +} + + +GstMetaInfo const * gst_fsl_vpu_buffer_meta_get_info(void) +{ + static GstMetaInfo const *meta_buffer_fsl_vpu_info = NULL; + + if (g_once_init_enter(&meta_buffer_fsl_vpu_info)) + { + GstMetaInfo const *meta = gst_meta_register( + gst_fsl_vpu_buffer_meta_api_get_type(), + "GstFslVpuBufferMeta", + sizeof(GstFslVpuBufferMeta), + GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_meta_init), + GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_meta_free), + (GstMetaTransformFunction)NULL + ); + g_once_init_leave(&meta_buffer_fsl_vpu_info, meta); + } + + return meta_buffer_fsl_vpu_info; +} + + + + +static void gst_fsl_vpu_buffer_pool_finalize(GObject *object) +{ + GstFslVpuBufferPool *vpu_pool = GST_FSL_VPU_BUFFER_POOL(object); + + if (vpu_pool->framebuffers != NULL) + gst_object_unref(vpu_pool->framebuffers); + + GST_TRACE_OBJECT(vpu_pool, "shutting down buffer pool"); + + G_OBJECT_CLASS(gst_fsl_vpu_buffer_pool_parent_class)->finalize(object); +} + + +static const gchar ** gst_fsl_vpu_buffer_pool_get_options(G_GNUC_UNUSED GstBufferPool *pool) +{ + static const gchar *options[] = + { + GST_BUFFER_POOL_OPTION_VIDEO_META, + GST_BUFFER_POOL_OPTION_FSL_VPU_FRAMEBUFFER, + NULL + }; + + return options; +} + + +static gboolean gst_fsl_vpu_buffer_pool_set_config(GstBufferPool *pool, GstStructure *config) +{ + GstFslVpuBufferPool *vpu_pool; + GstVideoInfo info; + GstCaps *caps; + gsize size; + guint min, max; + + vpu_pool = GST_FSL_VPU_BUFFER_POOL(pool); + + if (!gst_buffer_pool_config_get_params(config, &caps, &size, &min, &max)) + { + GST_ERROR_OBJECT(pool, "pool configuration invalid"); + return FALSE; + } + + if (caps == NULL) + { + GST_ERROR_OBJECT(pool, "configuration contains no caps"); + return FALSE; + } + + if (!gst_video_info_from_caps(&info, caps)) + { + GST_ERROR_OBJECT(pool, "caps cannot be parsed for video info"); + return FALSE; + } + + vpu_pool->video_info = info; + + vpu_pool->video_info.stride[0] = vpu_pool->framebuffers->y_stride; + vpu_pool->video_info.stride[1] = vpu_pool->framebuffers->uv_stride; + vpu_pool->video_info.stride[2] = vpu_pool->framebuffers->uv_stride; + vpu_pool->video_info.offset[0] = 0; + vpu_pool->video_info.offset[1] = vpu_pool->framebuffers->y_size; + vpu_pool->video_info.offset[2] = vpu_pool->framebuffers->y_size + vpu_pool->framebuffers->u_size; + vpu_pool->video_info.size = vpu_pool->framebuffers->total_size; + + vpu_pool->add_videometa = gst_buffer_pool_config_has_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); + + return GST_BUFFER_POOL_CLASS(gst_fsl_vpu_buffer_pool_parent_class)->set_config(pool, config); +} + + +static GstFlowReturn gst_fsl_vpu_buffer_pool_alloc_buffer(GstBufferPool *pool, GstBuffer **buffer, G_GNUC_UNUSED GstBufferPoolAcquireParams *params) +{ + GstBuffer *buf; + GstFslVpuBufferPool *vpu_pool; + GstVideoInfo *info; + + vpu_pool = GST_FSL_VPU_BUFFER_POOL(pool); + + info = &(vpu_pool->video_info); + + buf = gst_buffer_new(); + if (buf == NULL) + { + GST_ERROR_OBJECT(pool, "could not create new buffer"); + return GST_FLOW_ERROR; + } + + GST_FSL_VPU_BUFFER_META_ADD(buf); + + if (vpu_pool->add_videometa) + { + gst_buffer_add_video_meta_full( + buf, + GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT(info), + GST_VIDEO_INFO_WIDTH(info), GST_VIDEO_INFO_HEIGHT(info), + GST_VIDEO_INFO_N_PLANES(info), + info->offset, + info->stride + ); + } + + *buffer = buf; + + return GST_FLOW_OK; +} + + +static void gst_fsl_vpu_buffer_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer) +{ + VpuDecRetCode dec_ret; + GstFslVpuBufferPool *vpu_pool; + GstFslVpuBufferMeta *vpu_meta; + + vpu_pool = GST_FSL_VPU_BUFFER_POOL(pool); + vpu_meta = GST_FSL_VPU_BUFFER_META_GET(buffer); + + if (vpu_meta->framebuffer == NULL) + { + GST_DEBUG_OBJECT(pool, "buffer %p does not have VPU metadata - nothing to clear", buffer); + return; + } + + g_mutex_lock(&(vpu_pool->framebuffers->available_fb_mutex)); + + if (vpu_meta->not_displayed_yet && vpu_pool->framebuffers->decoder_open) + { + dec_ret = VPU_DecOutFrameDisplayed(vpu_pool->framebuffers->handle, vpu_meta->framebuffer); + if (dec_ret != VPU_DEC_RET_SUCCESS) + GST_ERROR_OBJECT(pool, "clearing display framebuffer failed: %s", gst_fsl_vpu_strerror(dec_ret)); + else + { + vpu_meta->not_displayed_yet = FALSE; + vpu_pool->framebuffers->num_available_framebuffers++; + GST_DEBUG_OBJECT(pool, "cleared buffer %p", buffer); + } + } + else if (!vpu_pool->framebuffers->decoder_open) + GST_DEBUG_OBJECT(pool, "not clearing buffer %p, since VPU decodr is closed", buffer); + else + GST_DEBUG_OBJECT(pool, "buffer %p already cleared", buffer); + + g_mutex_unlock(&(vpu_pool->framebuffers->available_fb_mutex)); + + GST_BUFFER_POOL_CLASS(gst_fsl_vpu_buffer_pool_parent_class)->release_buffer(pool, buffer); +} + + +static void gst_fsl_vpu_buffer_pool_class_init(GstFslVpuBufferPoolClass *klass) +{ + GObjectClass *object_class; + GstBufferPoolClass *parent_class; + + object_class = G_OBJECT_CLASS(klass); + parent_class = GST_BUFFER_POOL_CLASS(klass); + + GST_DEBUG_CATEGORY_INIT(vpubufferpool_debug, "vpubufferpool", 0, "Freescale VPU DMA buffer pool"); + + object_class->finalize = GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_pool_finalize); + parent_class->get_options = GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_pool_get_options); + parent_class->set_config = GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_pool_set_config); + parent_class->alloc_buffer = GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_pool_alloc_buffer); + parent_class->release_buffer = GST_DEBUG_FUNCPTR(gst_fsl_vpu_buffer_pool_release_buffer); +} + + +static void gst_fsl_vpu_buffer_pool_init(GstFslVpuBufferPool *pool) +{ + pool->framebuffers = NULL; + pool->add_videometa = FALSE; + + GST_DEBUG_OBJECT(pool, "initializing VPU buffer pool"); +} + + +GstBufferPool *gst_fsl_vpu_buffer_pool_new(GstFslVpuFramebuffers *framebuffers) +{ + GstFslVpuBufferPool *vpu_pool; + + g_assert(framebuffers != NULL); + + vpu_pool = g_object_new(gst_fsl_vpu_buffer_pool_get_type(), NULL); + vpu_pool->framebuffers = gst_object_ref(framebuffers); + + return GST_BUFFER_POOL_CAST(vpu_pool); +} + + +void gst_fsl_vpu_buffer_pool_set_framebuffer(GstBufferPool *pool, GstFslVpuFramebuffers *framebuffers) +{ + GstFslVpuBufferPool *vpu_pool = GST_FSL_VPU_BUFFER_POOL(pool); + + g_assert(framebuffers != NULL); + + if (framebuffers == vpu_pool->framebuffers) + return; + + /* it is good practice to first ref the new, then unref the old object + * even though the case of identical pointers is caught above */ + gst_object_ref(framebuffers); + + if (vpu_pool->framebuffers != NULL) + gst_object_unref(vpu_pool->framebuffers); + + vpu_pool->framebuffers = framebuffers; +} + + +gboolean gst_fsl_vpu_set_buffer_contents(GstBuffer *buffer, GstFslVpuFramebuffers *framebuffers, VpuFrameBuffer *framebuffer, gboolean heap_mode) +{ + VpuDecRetCode dec_ret; + GstFslVpuBufferMeta *vpu_meta; + GstMemory *memory; + + vpu_meta = GST_FSL_VPU_BUFFER_META_GET(buffer); + if (vpu_meta == NULL) + { + GST_ERROR("buffer with pointer %p has no VPU metadata", buffer); + return FALSE; + } + + if (heap_mode) + { + GstMapInfo map_info; + + memory = gst_allocator_alloc(NULL, framebuffers->total_size, NULL); + gst_memory_map(memory, &map_info, GST_MAP_WRITE); + memcpy(map_info.data, framebuffer->pbufVirtY, framebuffers->y_size); + memcpy(map_info.data, framebuffer->pbufVirtCb, framebuffers->u_size); + memcpy(map_info.data, framebuffer->pbufVirtCr, framebuffers->v_size); + gst_memory_unmap(memory, &map_info); + + vpu_meta->framebuffer = NULL; + + dec_ret = VPU_DecOutFrameDisplayed(framebuffers->handle, framebuffer); + if (dec_ret != VPU_DEC_RET_SUCCESS) + GST_ERROR("clearing display framebuffer failed: %s", gst_fsl_vpu_strerror(dec_ret)); + } + else + { + vpu_meta->framebuffer = framebuffer; + + memory = gst_memory_new_wrapped( + GST_MEMORY_FLAG_NO_SHARE, + framebuffer->pbufVirtY, + framebuffers->total_size, + 0, + framebuffers->total_size, + NULL, + NULL + ); + } + + + gst_buffer_remove_all_memory(buffer); + gst_buffer_append_memory(buffer, memory); + + return TRUE; +} + + +void gst_fsl_vpu_mark_buf_as_not_displayed(GstBuffer *buffer) +{ + GstFslVpuBufferMeta *vpu_meta = GST_FSL_VPU_BUFFER_META_GET(buffer); + g_assert(vpu_meta != NULL); + vpu_meta->not_displayed_yet = TRUE; +} + diff --git a/src/common/vpu_bufferpool.h b/src/common/vpu_bufferpool.h new file mode 100644 index 0000000..8123884 --- /dev/null +++ b/src/common/vpu_bufferpool.h @@ -0,0 +1,92 @@ +/* GStreamer buffer pool for wrapped VPU framebuffers + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifndef VPU_BUFFERPOOL_H +#define VPU_BUFFERPOOL_H + +#include +#include +#include +#include +#include "vpu_framebuffers.h" + + +G_BEGIN_DECLS + + +typedef struct _GstFslVpuBufferMeta GstFslVpuBufferMeta; +typedef struct _GstFslVpuBufferPool GstFslVpuBufferPool; +typedef struct _GstFslVpuBufferPoolClass GstFslVpuBufferPoolClass; +typedef struct _GstFslVpuFrameBufferExt GstFslVpuFrameBufferExt; + + +#define GST_TYPE_FSL_VPU_BUFFER_POOL (gst_fsl_vpu_buffer_pool_get_type()) +#define GST_FSL_VPU_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FSL_VPU_BUFFER_POOL, GstFslVpuBufferPool)) +#define GST_FSL_VPU_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FSL_VPU_BUFFER_POOL, GstFslVpuBufferPoolClass)) + +#define GST_FSL_VPU_BUFFER_META_GET(buffer) ((GstFslVpuBufferMeta *)gst_buffer_get_meta((buffer), gst_fsl_vpu_buffer_meta_api_get_type())) +#define GST_FSL_VPU_BUFFER_META_ADD(buffer) (gst_buffer_add_meta((buffer), gst_fsl_vpu_buffer_meta_get_info(), NULL)) +#define GST_FSL_VPU_BUFFER_META_DEL(buffer) (gst_buffer_remove_meta((buffer), gst_buffer_get_meta((buffer), gst_fsl_vpu_buffer_meta_api_get_type()))) + + +#define GST_BUFFER_POOL_OPTION_FSL_VPU_FRAMEBUFFER "GstBufferPoolOptionFslVpuFramebuffer" + + +struct _GstFslVpuBufferMeta +{ + GstMeta meta; + + VpuFrameBuffer *framebuffer; + gboolean not_displayed_yet; +}; + + +struct _GstFslVpuBufferPool +{ + GstBufferPool bufferpool; + + GstFslVpuFramebuffers *framebuffers; + GstVideoInfo video_info; + gboolean add_videometa; +}; + + +struct _GstFslVpuBufferPoolClass +{ + GstBufferPoolClass parent_class; +}; + + +G_END_DECLS + + +GType gst_fsl_vpu_buffer_meta_api_get_type(void); +GstMetaInfo const * gst_fsl_vpu_buffer_meta_get_info(void); + +GType gst_fsl_vpu_buffer_pool_get_type(void); + +GstBufferPool *gst_fsl_vpu_buffer_pool_new(GstFslVpuFramebuffers *framebuffers); +void gst_fsl_vpu_buffer_pool_set_framebuffer(GstBufferPool *pool, GstFslVpuFramebuffers *framebuffers); + +gboolean gst_fsl_vpu_set_buffer_contents(GstBuffer *buffer, GstFslVpuFramebuffers *framebuffers, VpuFrameBuffer *framebuffer, gboolean heap_mode); +void gst_fsl_vpu_mark_buf_as_not_displayed(GstBuffer *buffer); + + +#endif + diff --git a/src/common/vpu_framebuffers.c b/src/common/vpu_framebuffers.c new file mode 100644 index 0000000..35eb47a --- /dev/null +++ b/src/common/vpu_framebuffers.c @@ -0,0 +1,189 @@ +/* VPU registered framebuffers structure + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include + +#include "vpu_framebuffers.h" +#include "vpu_utils.h" + + +GST_DEBUG_CATEGORY_STATIC(vpu_framebuffers_debug); +#define GST_CAT_DEFAULT vpu_framebuffers_debug + + +#define ALIGN_VAL_TO(LENGTH, ALIGN_SIZE) ( ((guintptr)((LENGTH) + (ALIGN_SIZE) - 1) / (ALIGN_SIZE)) * (ALIGN_SIZE) ) +#define FRAME_ALIGN 16 + + +G_DEFINE_TYPE(GstFslVpuFramebuffers, gst_fsl_vpu_framebuffers, GST_TYPE_OBJECT) + + +static gboolean gst_fsl_vpu_framebuffers_configure(GstFslVpuFramebuffers *framebuffers, VpuDecHandle handle, VpuDecInitInfo *init_info); +static void gst_fsl_vpu_framebuffers_finalize(GObject *object); + + + + +void gst_fsl_vpu_framebuffers_class_init(GstFslVpuFramebuffersClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS(klass); + object_class->finalize = GST_DEBUG_FUNCPTR(gst_fsl_vpu_framebuffers_finalize); + + GST_DEBUG_CATEGORY_INIT(vpu_framebuffers_debug, "vpuframebuffers", 0, "Freescale VPU framebuffer memory blocks"); +} + + +void gst_fsl_vpu_framebuffers_init(GstFslVpuFramebuffers *framebuffers) +{ + framebuffers->decoder_open = FALSE; + + framebuffers->framebuffers = NULL; + framebuffers->num_framebuffers = 0; + framebuffers->num_available_framebuffers = 0; + framebuffers->fb_mem_blocks = NULL; + + framebuffers->y_stride = framebuffers->uv_stride = 0; + framebuffers->y_size = framebuffers->u_size = framebuffers->v_size = framebuffers->mv_size = 0; + framebuffers->total_size = 0; + + g_mutex_init(&(framebuffers->available_fb_mutex)); +} + + +GstFslVpuFramebuffers * gst_fsl_vpu_framebuffers_new(VpuDecHandle handle, VpuDecInitInfo *init_info) +{ + GstFslVpuFramebuffers *framebuffers; + framebuffers = g_object_new(gst_fsl_vpu_framebuffers_get_type(), NULL); + if (gst_fsl_vpu_framebuffers_configure(framebuffers, handle, init_info)) + return framebuffers; + else + return NULL; +} + + +static gboolean gst_fsl_vpu_framebuffers_configure(GstFslVpuFramebuffers *framebuffers, VpuDecHandle handle, VpuDecInitInfo *init_info) +{ + int alignment; + unsigned char *phys_ptr, *virt_ptr; + guint i; + VpuDecRetCode vpu_ret; + + framebuffers->num_reserve_framebuffers = init_info->nMinFrameBufferCount; + framebuffers->num_framebuffers = MAX((guint)(init_info->nMinFrameBufferCount), (guint)10) + framebuffers->num_reserve_framebuffers; + framebuffers->num_available_framebuffers = framebuffers->num_framebuffers - framebuffers->num_reserve_framebuffers; + framebuffers->framebuffers = (VpuFrameBuffer *)g_slice_alloc(sizeof(VpuFrameBuffer) * framebuffers->num_framebuffers); + + framebuffers->handle = handle; + + framebuffers->y_stride = ALIGN_VAL_TO(init_info->nPicWidth, FRAME_ALIGN); + if (init_info->nInterlace) + framebuffers->y_size = framebuffers->y_stride * ALIGN_VAL_TO(init_info->nPicHeight, (2 * FRAME_ALIGN)); + else + framebuffers->y_size = framebuffers->y_stride * ALIGN_VAL_TO(init_info->nPicHeight, FRAME_ALIGN); + + framebuffers->uv_stride = framebuffers->y_stride / 2; + framebuffers->u_size = framebuffers->v_size = framebuffers->mv_size = framebuffers->y_size / 4; + + alignment = init_info->nAddressAlignment; + if (alignment > 1) + { + framebuffers->y_size = ALIGN_VAL_TO(framebuffers->y_size, alignment); + framebuffers->u_size = ALIGN_VAL_TO(framebuffers->u_size, alignment); + framebuffers->v_size = ALIGN_VAL_TO(framebuffers->v_size, alignment); + framebuffers->mv_size = ALIGN_VAL_TO(framebuffers->mv_size, alignment); + } + + framebuffers->total_size = framebuffers->y_size + framebuffers->u_size + framebuffers->v_size + framebuffers->mv_size + alignment; + GST_DEBUG_OBJECT(framebuffers, "num framebuffers: total: %u reserved: %u available: %d", framebuffers->num_framebuffers, framebuffers->num_reserve_framebuffers, framebuffers->num_available_framebuffers); + GST_DEBUG_OBJECT(framebuffers, "framebuffer memory block size: total: %d Y: %d U: %d V: %d Mv: %d alignment: %d", framebuffers->total_size, framebuffers->y_size, framebuffers->u_size, framebuffers->v_size, framebuffers->mv_size, alignment); + + for (i = 0; i < framebuffers->num_framebuffers; ++i) + { + VpuMemDesc *mem_block; + VpuFrameBuffer *framebuffer; + + framebuffer = &(framebuffers->framebuffers[i]); + + if (!gst_fsl_vpu_alloc_phys_mem_block(&mem_block, framebuffers->total_size)) + return FALSE; + gst_fsl_vpu_append_phys_mem_block(mem_block, &(framebuffers->fb_mem_blocks)); + + phys_ptr = (unsigned char*)(mem_block->nPhyAddr); + virt_ptr = (unsigned char*)(mem_block->nVirtAddr); + + if (alignment > 1) + { + phys_ptr = (unsigned char*)ALIGN_VAL_TO(phys_ptr, alignment); + virt_ptr = (unsigned char*)ALIGN_VAL_TO(virt_ptr, alignment); + } + + framebuffer->nStrideY = framebuffers->y_stride; + framebuffer->nStrideC = framebuffers->uv_stride; + + /* fill phy addr*/ + framebuffer->pbufY = phys_ptr; + framebuffer->pbufCb = phys_ptr + framebuffers->y_size; + framebuffer->pbufCr = phys_ptr + framebuffers->y_size + framebuffers->u_size; + framebuffer->pbufMvCol = phys_ptr + framebuffers->y_size + framebuffers->u_size + framebuffers->v_size; + + /* fill virt addr */ + framebuffer->pbufVirtY = virt_ptr; + framebuffer->pbufVirtCb = virt_ptr + framebuffers->y_size; + framebuffer->pbufVirtCr = virt_ptr + framebuffers->y_size + framebuffers->u_size; + framebuffer->pbufVirtMvCol = virt_ptr + framebuffers->y_size + framebuffers->u_size + framebuffers->v_size; + + framebuffer->pbufY_tilebot = 0; + framebuffer->pbufCb_tilebot = 0; + framebuffer->pbufVirtY_tilebot = 0; + framebuffer->pbufVirtCb_tilebot = 0; + } + + vpu_ret = VPU_DecRegisterFrameBuffer(framebuffers->handle, framebuffers->framebuffers, framebuffers->num_framebuffers); + if (vpu_ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(framebuffers, "registering framebuffers failed: %s", gst_fsl_vpu_strerror(vpu_ret)); + return FALSE; + } + + framebuffers->decoder_open = TRUE; + + return TRUE; +} + + +static void gst_fsl_vpu_framebuffers_finalize(GObject *object) +{ + GstFslVpuFramebuffers *framebuffers = GST_FSL_VPU_FRAMEBUFFERS(object); + + GST_DEBUG_OBJECT(framebuffers, "freeing framebuffer memory"); + + if (framebuffers->framebuffers != NULL) + { + g_slice_free1(sizeof(VpuFrameBuffer) * framebuffers->num_framebuffers, framebuffers->framebuffers); + framebuffers->framebuffers = NULL; + } + + gst_fsl_vpu_free_phys_mem_blocks(&(framebuffers->fb_mem_blocks)); + + G_OBJECT_CLASS(gst_fsl_vpu_framebuffers_parent_class)->finalize(object); +} + diff --git a/src/common/vpu_framebuffers.h b/src/common/vpu_framebuffers.h new file mode 100644 index 0000000..c89be17 --- /dev/null +++ b/src/common/vpu_framebuffers.h @@ -0,0 +1,77 @@ +/* VPU registered framebuffers structure + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifndef VPU_FRAMEBUFFERS_H +#define VPU_FRAMEBUFFERS_H + +#include +#include +#include + + +G_BEGIN_DECLS + + +typedef struct _GstFslVpuFramebuffers GstFslVpuFramebuffers; +typedef struct _GstFslVpuFramebuffersClass GstFslVpuFramebuffersClass; + + +#define GST_TYPE_FSL_VPU_FRAMEBUFFERS (gst_fsl_vpu_framebuffers_get_type()) +#define GST_FSL_VPU_FRAMEBUFFERS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FSL_VPU_FRAMEBUFFERS, GstFslVpuFramebuffers)) +#define GST_FSL_VPU_FRAMEBUFFERS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FSL_VPU_FRAMEBUFFERS, GstFslVpuFramebuffersClass)) +#define GST_FSL_VPU_FRAMEBUFFERS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_FSL_VPU_FRAMEBUFFERS, GstFslVpuFramebuffersClass)) +#define GST_IS_FSL_VPU_FRAMEBUFFERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_FSL_VPU_FRAMEBUFFERS)) +#define GST_IS_FSL_VPU_FRAMEBUFFERS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_FSL_VPU_FRAMEBUFFERS)) + + +struct _GstFslVpuFramebuffers +{ + GstObject parent; + + VpuDecHandle handle; + gboolean decoder_open; + + VpuFrameBuffer *framebuffers; + guint num_framebuffers; + guint num_reserve_framebuffers; + gint num_available_framebuffers; + GSList *fb_mem_blocks; + GMutex available_fb_mutex; + + int y_stride, uv_stride; + int y_size, u_size, v_size, mv_size; + int total_size; +}; + + +struct _GstFslVpuFramebuffersClass +{ + GstObjectClass parent_class; +}; + + +GType gst_fsl_vpu_framebuffers_get_type(void); +GstFslVpuFramebuffers * gst_fsl_vpu_framebuffers_new(VpuDecHandle handle, VpuDecInitInfo *init_info); + + +G_END_DECLS + + +#endif + diff --git a/src/common/vpu_utils.c b/src/common/vpu_utils.c new file mode 100644 index 0000000..c7d21a9 --- /dev/null +++ b/src/common/vpu_utils.c @@ -0,0 +1,145 @@ +/* Miscellanous utility functions for VPU operations + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include "vpu_utils.h" + +GST_DEBUG_CATEGORY_STATIC(vpualloc_debug); + + +gchar const *gst_fsl_vpu_strerror(VpuDecRetCode code) +{ + switch (code) + { + case VPU_DEC_RET_SUCCESS: return "success"; + case VPU_DEC_RET_FAILURE: return "failure"; + case VPU_DEC_RET_INVALID_PARAM: return "invalid param"; + case VPU_DEC_RET_INVALID_HANDLE: return "invalid handle"; + case VPU_DEC_RET_INVALID_FRAME_BUFFER: return "invalid frame buffer"; + case VPU_DEC_RET_INSUFFICIENT_FRAME_BUFFERS: return "insufficient frame buffers"; + case VPU_DEC_RET_INVALID_STRIDE: return "invalid stride"; + case VPU_DEC_RET_WRONG_CALL_SEQUENCE: return "wrong call sequence"; + case VPU_DEC_RET_FAILURE_TIMEOUT: return "failure timeout"; + default: + return NULL; + } +} + + +void gst_fsl_vpu_init_alloc_debug(void) +{ + GST_DEBUG_CATEGORY_INIT(vpualloc_debug, "vpualloc", 0, "VPU allocation functions"); +} + + +gboolean gst_fsl_vpu_alloc_virt_mem_block(unsigned char **mem_block, int size) +{ + *mem_block = (unsigned char *)g_try_malloc(size); + if ((*mem_block) == NULL) + { + GST_CAT_ERROR(vpualloc_debug, "could not request %d bytes of heap memory", size); + return FALSE; + } + else + GST_CAT_DEBUG(vpualloc_debug, "allocated %d bytes of heap memory at virt addr %p", size, *mem_block); + + return TRUE; +} + + +void gst_fsl_vpu_append_virt_mem_block(unsigned char *mem_block, GSList **virt_mem_blocks) +{ + *virt_mem_blocks = g_slist_append(*virt_mem_blocks, (gpointer)mem_block); +} + + +gboolean gst_fsl_vpu_free_virt_mem_blocks(GSList **virt_mem_blocks) +{ + GSList *mem_block_node; + g_assert(virt_mem_blocks != NULL); + mem_block_node = *virt_mem_blocks; + if (mem_block_node == NULL) + return TRUE; + + for (; mem_block_node != NULL; mem_block_node = mem_block_node->next) + { + g_free(mem_block_node->data); + GST_CAT_DEBUG(vpualloc_debug, "freed heap memory block at virt addr %p", mem_block_node->data); + } + + g_slist_free(*virt_mem_blocks); + *virt_mem_blocks = NULL; + + return TRUE; +} + + +gboolean gst_fsl_vpu_alloc_phys_mem_block(VpuMemDesc **mem_block, int size) +{ + VpuDecRetCode ret; + + *mem_block = g_slice_alloc(sizeof(VpuMemDesc)); + if (*mem_block == NULL) + { + GST_ERROR("could not allocate VPU memory descriptor: slice allocator failed"); + return FALSE; + } + + (*mem_block)->nSize = size; + ret = VPU_DecGetMem(*mem_block); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR("could not request %d bytes of VPU memory: %s", size, gst_fsl_vpu_strerror(ret)); + return FALSE; + } + else + GST_CAT_DEBUG(vpualloc_debug, "allocated %d bytes of VPU memory at virt addr %x phys addr %x", size, (*mem_block)->nVirtAddr, (*mem_block)->nPhyAddr); + + return TRUE; +} + + +void gst_fsl_vpu_append_phys_mem_block(VpuMemDesc *mem_block, GSList **phys_mem_blocks) +{ + *phys_mem_blocks = g_slist_append(*phys_mem_blocks, (gpointer)mem_block); +} + + +gboolean gst_fsl_vpu_free_phys_mem_blocks(GSList **phys_mem_blocks) +{ + GSList *mem_block_node; + g_assert(phys_mem_blocks != NULL); + mem_block_node = *phys_mem_blocks; + if (mem_block_node == NULL) + return TRUE; + + for (; mem_block_node != NULL; mem_block_node = mem_block_node->next) + { + VpuMemDesc *mem_block = (VpuMemDesc *)(mem_block_node->data); + GST_CAT_DEBUG(vpualloc_debug, "freed %d bytes of VPU memory at virt addr %x phys addr %x", mem_block->nSize, mem_block->nVirtAddr, mem_block->nPhyAddr); + VPU_DecFreeMem(mem_block); + g_slice_free1(sizeof(VpuMemDesc), mem_block); + } + + g_slist_free(*phys_mem_blocks); + *phys_mem_blocks = NULL; + + return TRUE; +} + diff --git a/src/common/vpu_utils.h b/src/common/vpu_utils.h new file mode 100644 index 0000000..03e03e9 --- /dev/null +++ b/src/common/vpu_utils.h @@ -0,0 +1,47 @@ +/* Miscellanous utility functions for VPU operations + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifndef VPU_UTILS_H +#define VPU_UTILS_H + +#include +#include + + +G_BEGIN_DECLS + + +gchar const *gst_fsl_vpu_strerror(VpuDecRetCode code); + +void gst_fsl_vpu_init_alloc_debug(void); + +gboolean gst_fsl_vpu_alloc_virt_mem_block(unsigned char **mem_block, int size); +void gst_fsl_vpu_append_virt_mem_block(unsigned char *mem_block, GSList **virt_mem_blocks); +gboolean gst_fsl_vpu_free_virt_mem_blocks(GSList **virt_mem_blocks); + +gboolean gst_fsl_vpu_alloc_phys_mem_block(VpuMemDesc **mem_block, int size); +void gst_fsl_vpu_append_phys_mem_block(VpuMemDesc *mem_block, GSList **phys_mem_blocks); +gboolean gst_fsl_vpu_free_phys_mem_blocks(GSList **phys_mem_blocks); + + +G_END_DECLS + + +#endif + diff --git a/src/decoder/vpu_decoder.c b/src/decoder/vpu_decoder.c new file mode 100644 index 0000000..d0d5866 --- /dev/null +++ b/src/decoder/vpu_decoder.c @@ -0,0 +1,997 @@ +/* GStreamer video decoder using the Freescale VPU hardware video engine + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "vpu_decoder.h" +#include "../common/vpu_utils.h" +#include "../common/vpu_bufferpool.h" + + + +/* Some notes about the design of this decoder: + * + * The VPU wrapper memory model and the GStreamer buffer pool design are fundamentally at odds with each other. + * The VPU wrapper expects the user to allocate and register a fixed set of framebuffers right after the first + * VPU_DecDecodeBuf() call returned with an VPU_DEC_INIT_OK code. This allocation happens once, and only once; + * reallocations or additional allocated buffers are not possible. + * GStreamer buffer pools, on the other hand, allocate on-demand, and allocate more buffers if necessary. + * To further complicate matters, the VPU wrapper has its own pooling logic; the user does not pick a framebuffer + * for the VPU to store decoded frames into, the VPU does that on its own. + * + * To bring these two together, an indirection was used: the allocated and registered framebuffers are contained + * and managed by the vpu_framebuffers structure. This structure derives from GstObject, and is therefore ref- + * counted. Once VPU_DecDecodeBuf() returns VPU_DEC_INIT_OK , an instance of the vpu_framebuffers structure is + * created. Internally, this allocates and registers framebuffers. There is also a custom buffer pool, which + * creates buffers with VPU-specific metadata, but no memory blocks. The buffer pools are always created after + * the framebuffers, since the buffer pools are created by the gst_video_decoder_allocate_output_buffer() call, + * which internally triggers the decide_allocation call, which in turn creates buffer pools. Created buffer pools + * retain a reference to the currently present vpu_framebuffers instance. + * The idea is to let the buffer pool deliver a buffer when the VPU_DecDecodeBuf() reports that a frame was + * decoded. If the buffer already has memory blocks, they are removed. Then, the framebuffer that was used by + * the VPU wrapper to store the decoded frame is retrieved using VPU_DecGetOutputFrame(). Finally, the + * framebuffer is wrapped inside a GstMemory block, this block is added to the empty buffer, and the buffer is + * sent downstream. Once the buffer is no longer used downstream, it is returned to the buffer pool. This triggers + * a release_buffer() call in the buffer pool, which is extended to clear the display flag from the framebuffer. + * This is necessary to notify the VPU wrapper that this frame is no longer used by anybody, and can be filled + * with decoded frames safely. + * + * In case the caps change for some reason, the set_format() function is invoked. Internally, it unrefs the + * framebuffers structure, closes the VPU decoder instance, and opens a new one, based on the new caps. + * Later, VPU_DecDecodeBuf() will return VPU_DEC_INIT_OK again, and a new framebuffers instance will be created + * etc. + * Since the framebuffers instance is refcounted, there will be no conflicts between old buffer pools and a + * new framebuffers structure. Old buffer pools that are kept alive for some reason (for example, because there + * are some of its buffers still floating around) in turn keep their associated old framebuffers instance alive. + * This prevents stale states. + * + * The fundamental problem with the VPU wrapper memory model is the case where all framebuffers are occupied. + * Then, the wrapper cannot pick a framebuffer to decode into, and decoding fails. This can easily happen if + * the GStreamer pipeline uses queues and downstream is not consuming the frames fast enough for some reason. + * To counter this effect, a trick was devised: the vpu_framebuffers structure allocates an extra set of + * framebuffers These are called the "reserved" framebuffers, while the others are the "available" ones. + * A counter is used (called "num_available_framebuffers"). This counts the number of available framebuffers, + * and initially equals the number of available framebuffers. Every time VPU_DecDecodeBuf() reports that a frame + * was consumed (NOTE: not to be confused with "a frame was decoded"), the counter is decremented. + * If the handle_frame() function is entered with a num_available_framebuffers value of zero, it means that all + * available framebuffers are occupied; only the reserved framebuffers are free. Decoding then switches to a + * secondary mode; inside handle_frame, do_memcpy is set to TRUE. Decoding continues as usual. But instead of + * wrapping the framebuffer containing the decoded frame, the decoded frame pixels are copied with memcpy() + * to a GstMemory block that was allocated on the heap. This block is then added to the buffer. The framebuffer + * is immediately marked as displayed (as opposed to when the buffer is returned to the buffer pool), and the + * buffer is sent downstream. + * In short: instead of sending the framebuffers downstream directly, their contents are copied to the heap, + * and the *copies* are sent downstream. + * + * This would not be necessary if it was possible to allocate new VPU framebuffers on the fly. Hopefully, + * Freescale will eventually adopt a different model, based on DMABUF. + */ + + + + +GST_DEBUG_CATEGORY_STATIC(vpudec_debug); +#define GST_CAT_DEFAULT vpudec_debug + + +#define ALIGN_VAL_TO(LENGTH, ALIGN_SIZE) ( ((guintptr)((LENGTH) + (ALIGN_SIZE) - 1) / (ALIGN_SIZE)) * (ALIGN_SIZE) ) + + + +static GMutex inst_counter_mutex; + + + +static GstStaticPadTemplate static_sink_template = GST_STATIC_PAD_TEMPLATE( + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS( + /* VPU_V_AVC */ + "video/x-h264, " + "parsed = (boolean) true, " + "stream-format = (string) byte-stream, " + "alignment = (string) au; " + + /* VPU_V_MPEG2 */ + "video/mpeg, " + "parsed = (boolean) true, " + "systemstream = (boolean) false, " + "mpegversion = (int) [ 1, 2 ]; " + + /* VPU_V_MPEG4 */ + "video/mpeg, " + "parsed = (boolean) true, " + "mpegversion = (int) 4; " + + /* VPU_V_DIVX3 */ + "video/x-divx, " + "divxversion = (int) 3; " + + /* VPU_V_DIVX56 */ + "video/x-divx, " + "divxversion = (int) [ 5, 6 ]; " + + /* VPU_V_XVID */ + "video/x-xvid; " + + /* VPU_V_H263 */ + "video/x-h263, " + "parsed = (boolean) true; " + +/* TODO: WMV disabled for now, since it does not work properly. + * It is necessary to find out how to prepare WMV3/VC1 data for the VPU first. */ + /* VPU_V_VC1_AP and VPU_V_VC1 */ + /* WVC1 = VC1-AP (VPU_V_VC1_AP) */ + /* WMV3 = VC1-SPMP (VPU_V_VC1) */ + /*"video/x-wmv, " + "wmvversion = (int) 3, " + "format = (string) { WVC1, WMV3 }; "*/ + + /* VPU_V_VP8 */ + "video/x-vp8; " + ) +); + +static GstStaticPadTemplate static_src_template = GST_STATIC_PAD_TEMPLATE( + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS( + "video/x-raw," + "format = (string) I420, " + "width = (int) [ 16, 2048 ], " + "height = (int) [ 16, 2048 ], " + "framerate = (fraction) [ 0, MAX ]" + ) +); + + +G_DEFINE_TYPE(GstFslVpuDec, gst_fsl_vpu_dec, GST_TYPE_VIDEO_DECODER) + + +/* miscellaneous functions */ +static gboolean gst_fsl_vpu_dec_alloc_dec_mem_blocks(GstFslVpuDec *vpu_dec); +static gboolean gst_fsl_vpu_dec_fill_param_set(GstFslVpuDec *vpu_dec, GstVideoCodecState *state, VpuDecOpenParam *open_param, GstBuffer **codec_data); +static void gst_fsl_vpu_dec_close_decoder(GstFslVpuDec *vpu_dec); + +/* functions for the base class */ +static gboolean gst_fsl_vpu_dec_start(GstVideoDecoder *decoder); +static gboolean gst_fsl_vpu_dec_stop(GstVideoDecoder *decoder); +static gboolean gst_fsl_vpu_dec_set_format(GstVideoDecoder *decoder, GstVideoCodecState *state); +static GstFlowReturn gst_fsl_vpu_dec_handle_frame(GstVideoDecoder *decoder, GstVideoCodecFrame *frame); +static gboolean gst_fsl_vpu_dec_reset(GstVideoDecoder *decoder, gboolean hard); +static gboolean gst_fsl_vpu_dec_decide_allocation(GstVideoDecoder *decoder, GstQuery *query); + + + + +/* required function declared by G_DEFINE_TYPE */ + +void gst_fsl_vpu_dec_class_init(GstFslVpuDecClass *klass) +{ + GstVideoDecoderClass *base_class; + GstElementClass *element_class; + + GST_DEBUG_CATEGORY_INIT(vpudec_debug, "vpudec", 0, "Freescale VPU video decoder"); + + base_class = GST_VIDEO_DECODER_CLASS(klass); + element_class = GST_ELEMENT_CLASS(klass); + + gst_element_class_set_static_metadata( + element_class, + "Freescale VPU video decoder", + "Codec/Decoder/Video", + "hardware-accelerated video decoding using the Freescale VPU engine", + "Carlos Rafael Giani " + ); + + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&static_sink_template)); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&static_src_template)); + + base_class->start = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_start); + base_class->stop = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_stop); + base_class->set_format = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_set_format); + base_class->handle_frame = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_handle_frame); + base_class->reset = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_reset); + base_class->decide_allocation = GST_DEBUG_FUNCPTR(gst_fsl_vpu_dec_decide_allocation); + + klass->inst_counter = 0; +} + + +void gst_fsl_vpu_dec_init(GstFslVpuDec *vpu_dec) +{ + vpu_dec->vpu_inst_opened = FALSE; + + vpu_dec->codec_data = NULL; + vpu_dec->current_framebuffers = NULL; + + vpu_dec->virt_dec_mem_blocks = NULL; + vpu_dec->phys_dec_mem_blocks = NULL; +} + + + + +/***************************/ +/* miscellaneous functions */ + +static gboolean gst_fsl_vpu_dec_alloc_dec_mem_blocks(GstFslVpuDec *vpu_dec) +{ + int i; + int size; + unsigned char *ptr; + + for (i = 0; i < vpu_dec->mem_info.nSubBlockNum; ++i) + { + size = vpu_dec->mem_info.MemSubBlock[i].nAlignment + vpu_dec->mem_info.MemSubBlock[i].nSize; + GST_DEBUG_OBJECT(vpu_dec, "sub block %d type: %s size: %d", i, (vpu_dec->mem_info.MemSubBlock[i].MemType == VPU_MEM_VIRT) ? "virtual" : "phys", size); + + if (vpu_dec->mem_info.MemSubBlock[i].MemType == VPU_MEM_VIRT) + { + if (!gst_fsl_vpu_alloc_virt_mem_block(&ptr, size)) + return FALSE; + + vpu_dec->mem_info.MemSubBlock[i].pVirtAddr = (unsigned char *)ALIGN_VAL_TO(ptr, vpu_dec->mem_info.MemSubBlock[i].nAlignment); + + gst_fsl_vpu_append_virt_mem_block(ptr, &(vpu_dec->virt_dec_mem_blocks)); + } + else if (vpu_dec->mem_info.MemSubBlock[i].MemType == VPU_MEM_PHY) + { + VpuMemDesc *mem_block; + if (!gst_fsl_vpu_alloc_phys_mem_block(&mem_block, size)) + return FALSE; + + vpu_dec->mem_info.MemSubBlock[i].pVirtAddr = (unsigned char *)ALIGN_VAL_TO(mem_block->nVirtAddr, vpu_dec->mem_info.MemSubBlock[i].nAlignment); + vpu_dec->mem_info.MemSubBlock[i].pPhyAddr = (unsigned char *)ALIGN_VAL_TO(mem_block->nPhyAddr, vpu_dec->mem_info.MemSubBlock[i].nAlignment); + + gst_fsl_vpu_append_phys_mem_block(mem_block, &(vpu_dec->phys_dec_mem_blocks)); + } + else + { + GST_WARNING_OBJECT(vpu_dec, "sub block %d type is unknown - skipping", i); + } + } + + return TRUE; +} + + +static gboolean gst_fsl_vpu_dec_free_dec_mem_blocks(GstFslVpuDec *vpu_dec) +{ + gboolean ret1, ret2; + /* NOT using the two calls with && directly, since otherwise an early exit could happen; in other words, + * if the first call failed, the second one wouldn't even be invoked + * doing the logical AND afterwards fixes this */ + ret1 = gst_fsl_vpu_free_virt_mem_blocks(&(vpu_dec->virt_dec_mem_blocks)); + ret2 = gst_fsl_vpu_free_phys_mem_blocks(&(vpu_dec->phys_dec_mem_blocks)); + return ret1 && ret2; +} + + +static gboolean gst_fsl_vpu_dec_fill_param_set(GstFslVpuDec *vpu_dec, GstVideoCodecState *state, VpuDecOpenParam *open_param, GstBuffer **codec_data) +{ + guint structure_nr; + gboolean format_set; + gboolean do_codec_data = FALSE; + + memset(open_param, 0, sizeof(VpuDecOpenParam)); + + for (structure_nr = 0; structure_nr < gst_caps_get_size(state->caps); ++structure_nr) + { + GstStructure *s; + gchar const *name; + + format_set = TRUE; + s = gst_caps_get_structure(state->caps, structure_nr); + name = gst_structure_get_name(s); + + open_param->nReorderEnable = 0; + + if (g_strcmp0(name, "video/x-h264") == 0) + { + open_param->CodecFormat = VPU_V_AVC; + open_param->nReorderEnable = 1; + GST_INFO_OBJECT(vpu_dec, "setting h.264 as stream format"); + } + else if (g_strcmp0(name, "video/mpeg") == 0) + { + gint mpegversion; + if (gst_structure_get_int(s, "mpegversion", &mpegversion)) + { + gboolean is_systemstream; + switch (mpegversion) + { + case 1: + case 2: + if (gst_structure_get_boolean(s, "systemstream", &is_systemstream) && !is_systemstream) + { + open_param->CodecFormat = VPU_V_MPEG2; + } + else + { + GST_WARNING_OBJECT(vpu_dec, "MPEG-%d system stream is not supported", mpegversion); + format_set = FALSE; + } + break; + case 4: + open_param->CodecFormat = VPU_V_MPEG4; + break; + default: + GST_WARNING_OBJECT(vpu_dec, "unsupported MPEG version: %d", mpegversion); + format_set = FALSE; + break; + } + + if (format_set) + GST_INFO_OBJECT(vpu_dec, "setting MPEG-%d as stream format", mpegversion); + } + + do_codec_data = TRUE; + } + else if (g_strcmp0(name, "video/x-divx") == 0) + { + gint divxversion; + if (gst_structure_get_int(s, "divxversion", &divxversion)) + { + switch (divxversion) + { + case 3: + open_param->CodecFormat = VPU_V_DIVX3; + break; + case 5: + case 6: + open_param->CodecFormat = VPU_V_DIVX56; + break; + default: + format_set = FALSE; + break; + } + + if (format_set) + GST_INFO_OBJECT(vpu_dec, "setting DivX %d as stream format", divxversion); + } + } + else if (g_strcmp0(name, "video/x-xvid") == 0) + { + open_param->CodecFormat = VPU_V_XVID; + GST_INFO_OBJECT(vpu_dec, "setting xvid as stream format"); + } + else if (g_strcmp0(name, "video/x-h263") == 0) + { + open_param->CodecFormat = VPU_V_H263; + GST_INFO_OBJECT(vpu_dec, "setting h.263 as stream format"); + } + else if (g_strcmp0(name, "video/x-wmv") == 0) + { + gint wmvversion; + gchar const *format_str; + + if (!gst_structure_get_int(s, "wmvversion", &wmvversion)) + { + GST_WARNING_OBJECT(vpu_dec, "wmvversion caps is missing"); + format_set = FALSE; + break; + } + if (wmvversion != 3) + { + GST_WARNING_OBJECT(vpu_dec, "unsupported WMV version %d (only version 3 is supported)", wmvversion); + format_set = FALSE; + break; + } + + format_str = gst_structure_get_string(s, "format"); + if ((format_str == NULL) || g_str_equal(format_str, "WMV3")) + { + GST_INFO_OBJECT(vpu_dec, "setting VC1M (= WMV3, VC1-SPMP) as stream format"); + open_param->CodecFormat = VPU_V_VC1; + } + else if (g_str_equal(format_str, "WVC1")) + { + GST_INFO_OBJECT(vpu_dec, "setting VC1 (= WVC1, VC1-AP) as stream format"); + open_param->CodecFormat = VPU_V_VC1_AP; + } + else + { + GST_WARNING_OBJECT(vpu_dec, "unsupported WMV format \"%s\"", format_str); + format_set = FALSE; + } + + do_codec_data = TRUE; + } + else if (g_strcmp0(name, "video/x-vp8") == 0) + { + open_param->CodecFormat = VPU_V_VP8; + GST_INFO_OBJECT(vpu_dec, "setting VP8 as stream format"); + } + + if (format_set) + { + if (do_codec_data) + { + GValue const *value = gst_structure_get_value(s, "codec_data"); + if (value != NULL) + { + GST_INFO_OBJECT(vpu_dec, "codec data expected and found in caps"); + *codec_data = gst_value_get_buffer(value); + } + else + { + GST_WARNING_OBJECT(vpu_dec, "codec data expected, but not found in caps"); + format_set = FALSE; + } + } + + break; + } + } + + if (!format_set) + return FALSE; + + open_param->nChromaInterleave = 0; + open_param->nMapType = 0; + open_param->nTiled2LinearEnable = 0; + open_param->nEnableFileMode = 0; + open_param->nPicWidth = state->info.width; + open_param->nPicHeight = state->info.height; + + return TRUE; +} + + +static void gst_fsl_vpu_dec_close_decoder(GstFslVpuDec *vpu_dec) +{ + VpuDecRetCode dec_ret; + + if (vpu_dec->vpu_inst_opened) + { + dec_ret = VPU_DecFlushAll(vpu_dec->handle); + if (dec_ret != VPU_DEC_RET_SUCCESS) + GST_ERROR_OBJECT(vpu_dec, "flushing decoder failed: %s", gst_fsl_vpu_strerror(dec_ret)); + + dec_ret = VPU_DecClose(vpu_dec->handle); + if (dec_ret != VPU_DEC_RET_SUCCESS) + GST_ERROR_OBJECT(vpu_dec, "closing decoder failed: %s", gst_fsl_vpu_strerror(dec_ret)); + + vpu_dec->vpu_inst_opened = FALSE; + } +} + + + + +/********************************/ +/* functions for the base class */ + +static gboolean gst_fsl_vpu_dec_start(GstVideoDecoder *decoder) +{ + VpuDecRetCode ret; + GstFslVpuDec *vpu_dec; + GstFslVpuDecClass *klass; + + vpu_dec = GST_FSL_VPU_DEC(decoder); + klass = GST_FSL_VPU_DEC_CLASS(G_OBJECT_GET_CLASS(vpu_dec)); + +#define VPUINIT_ERR(RET, DESC, UNLOAD) \ + if ((RET) != VPU_DEC_RET_SUCCESS) \ + { \ + g_mutex_unlock(&inst_counter_mutex); \ + GST_ELEMENT_ERROR(vpu_dec, LIBRARY, INIT, ("%s: %s", (DESC), gst_fsl_vpu_strerror(RET)), (NULL)); \ + if (UNLOAD) \ + VPU_DecUnLoad(); \ + return FALSE; \ + } + + g_mutex_lock(&inst_counter_mutex); + if (klass->inst_counter == 0) + { + ret = VPU_DecLoad(); + VPUINIT_ERR(ret, "loading VPU failed", FALSE); + + { + VpuVersionInfo version; + VpuWrapperVersionInfo wrapper_version; + + ret = VPU_DecGetVersionInfo(&version); + VPUINIT_ERR(ret, "getting version info failed", TRUE); + + ret = VPU_DecGetWrapperVersionInfo(&wrapper_version); + VPUINIT_ERR(ret, "getting wrapper version info failed", TRUE); + + GST_INFO_OBJECT(vpu_dec, "VPU loaded"); + GST_INFO_OBJECT(vpu_dec, "VPU firmware version %d.%d.%d_r%d", version.nFwMajor, version.nFwMinor, version.nFwRelease, version.nFwCode); + GST_INFO_OBJECT(vpu_dec, "VPU library version %d.%d.%d", version.nLibMajor, version.nLibMinor, version.nLibRelease); + GST_INFO_OBJECT(vpu_dec, "VPU wrapper version %d.%d.%d %s", wrapper_version.nMajor, wrapper_version.nMinor, wrapper_version.nRelease, wrapper_version.pBinary); + } + } + ++klass->inst_counter; + g_mutex_unlock(&inst_counter_mutex); + + /* mem_info contains information about how to set up memory blocks + * the VPU uses as temporary storage (they are "work buffers") */ + memset(&(vpu_dec->mem_info), 0, sizeof(VpuMemInfo)); + ret = VPU_DecQueryMem(&(vpu_dec->mem_info)); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not get VPU memory information: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + /* Allocate the work buffers + * Note that these are independent of decoder instances, so they + * are allocated before the VPU_DecOpen() call, and are not + * recreated in set_format */ + if (!gst_fsl_vpu_dec_alloc_dec_mem_blocks(vpu_dec)) + return FALSE; + +#undef VPUINIT_ERR + + /* The decoder is initialized in set_format, not here, since only then the input bitstream + * format is known (and this information is necessary for initialization). */ + + return TRUE; +} + + +static gboolean gst_fsl_vpu_dec_stop(GstVideoDecoder *decoder) +{ + gboolean ret; + VpuDecRetCode dec_ret; + GstFslVpuDec *vpu_dec; + GstFslVpuDecClass *klass; + + ret = TRUE; + + vpu_dec = GST_FSL_VPU_DEC(decoder); + klass = GST_FSL_VPU_DEC_CLASS(G_OBJECT_GET_CLASS(vpu_dec)); + + if (vpu_dec->current_framebuffers != NULL) + { + /* Using mutexes here to prevent race conditions when decoder_open is set to + * FALSE at the same time as it is checked in the buffer pool release() function */ + g_mutex_lock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + vpu_dec->current_framebuffers->decoder_open = FALSE; + g_mutex_unlock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + + gst_object_unref(vpu_dec->current_framebuffers); + vpu_dec->current_framebuffers = NULL; + } + + gst_fsl_vpu_dec_close_decoder(vpu_dec); + gst_fsl_vpu_dec_free_dec_mem_blocks(vpu_dec); + + if (vpu_dec->codec_data != NULL) + { + gst_buffer_unref(vpu_dec->codec_data); + vpu_dec->codec_data = NULL; + } + + g_mutex_lock(&inst_counter_mutex); + if (klass->inst_counter > 0) + { + --klass->inst_counter; + if (klass->inst_counter == 0) + { + dec_ret = VPU_DecUnLoad(); + if (dec_ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "unloading VPU failed: %s", gst_fsl_vpu_strerror(dec_ret)); + } + else + GST_INFO_OBJECT(vpu_dec, "VPU unloaded"); + } + } + g_mutex_unlock(&inst_counter_mutex); + + return ret; +} + + +static gboolean gst_fsl_vpu_dec_set_format(GstVideoDecoder *decoder, GstVideoCodecState *state) +{ + VpuDecRetCode ret; + VpuDecOpenParam open_param; + int config_param; + GstBuffer *codec_data = NULL; + GstFslVpuDec *vpu_dec = GST_FSL_VPU_DEC(decoder); + + /* Clean up existing framebuffers structure; + * if some previous and still existing buffer pools depend on this framebuffers + * structure, they will extend its lifetime, since they ref'd it + */ + if (vpu_dec->current_framebuffers != NULL) + { + /* Using mutexes here to prevent race conditions when decoder_open is set to + * FALSE at the same time as it is checked in the buffer pool release() function */ + g_mutex_lock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + vpu_dec->current_framebuffers->decoder_open = FALSE; + g_mutex_unlock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + + gst_object_unref(vpu_dec->current_framebuffers); + vpu_dec->current_framebuffers = NULL; + } + + /* Clean up old codec data copy */ + if (vpu_dec->codec_data != NULL) + { + gst_buffer_unref(vpu_dec->codec_data); + vpu_dec->codec_data = NULL; + } + + /* Close old decoder instance */ + gst_fsl_vpu_dec_close_decoder(vpu_dec); + + memset(&open_param, 0, sizeof(open_param)); + + /* codec_data does not need to be unref'd after use; it is owned by the caps structure */ + if (!gst_fsl_vpu_dec_fill_param_set(vpu_dec, state, &open_param, &codec_data)) + { + GST_ERROR_OBJECT(vpu_dec, "could not fill open params: state info incompatible"); + return FALSE; + } + + /* The actual initialization; requires bitstream information (such as the codec type), which + * is determined by the fill_param_set call before */ + ret = VPU_DecOpen(&(vpu_dec->handle), &open_param, &(vpu_dec->mem_info)); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "opening new VPU handle failed: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + vpu_dec->vpu_inst_opened = TRUE; + + /* configure AFTER setting vpu_inst_opened to TRUE, to make sure that in case of + config failure the VPU handle is closed in the finalizer */ + + config_param = VPU_DEC_SKIPNONE; + ret = VPU_DecConfig(vpu_dec->handle, VPU_DEC_CONF_SKIPMODE, &config_param); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not configure skip mode: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + config_param = 0; + ret = VPU_DecConfig(vpu_dec->handle, VPU_DEC_CONF_BUFDELAY, &config_param); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not configure buffer delay: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + config_param = VPU_DEC_IN_NORMAL; + ret = VPU_DecConfig(vpu_dec->handle, VPU_DEC_CONF_INPUTTYPE, &config_param); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not configure input type: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + gst_video_decoder_set_output_state(decoder, GST_VIDEO_FORMAT_I420, state->info.width, state->info.height, state); + + /* Copy the buffer, to make sure the codec_data lifetime does not depend on the caps */ + if (codec_data != NULL) + vpu_dec->codec_data = gst_buffer_copy(codec_data); + + return TRUE; + +} + + +static GstFlowReturn gst_fsl_vpu_dec_handle_frame(GstVideoDecoder *decoder, GstVideoCodecFrame *frame) +{ + int buffer_ret_code; + VpuDecRetCode dec_ret; + VpuBufferNode in_data; + GstMapInfo in_map_info; + gboolean do_memcpy; + guint8 *combined_input; + gsize combined_input_size; + GstFslVpuDec *vpu_dec; + + combined_input = NULL; + vpu_dec = GST_FSL_VPU_DEC(decoder); + + memset(&in_data, 0, sizeof(in_data)); + + gst_buffer_map(frame->input_buffer, &in_map_info, GST_MAP_READ); + + /* If there is codec data, prepend it to the input frame data + * To do that, allocate a temporary memory block big enough for both + * codec and input frame data, copy the codec data first, then the frame data + */ + if (vpu_dec->codec_data != NULL) + { + GstMapInfo cd_in_map_info; + gst_buffer_map(vpu_dec->codec_data, &cd_in_map_info, GST_MAP_READ); + + combined_input_size = cd_in_map_info.size + in_map_info.size; + combined_input = g_slice_alloc(combined_input_size); + memcpy(combined_input, cd_in_map_info.data, cd_in_map_info.size); + memcpy(combined_input + cd_in_map_info.size, in_map_info.data, in_map_info.size); + + GST_DEBUG_OBJECT(vpu_dec, "preparing combined input: codec data size: %u input frame size: %u", cd_in_map_info.size, in_map_info.size); + + gst_buffer_unmap(vpu_dec->codec_data, &cd_in_map_info); + + gst_buffer_unref(vpu_dec->codec_data); + vpu_dec->codec_data = NULL; + + in_data.pPhyAddr = NULL; + in_data.pVirAddr = (unsigned char *)(combined_input); + in_data.nSize = combined_input_size; + } + else + { + in_data.pPhyAddr = NULL; + in_data.pVirAddr = (unsigned char *)(in_map_info.data); + in_data.nSize = in_map_info.size; + } + + GST_DEBUG_OBJECT(vpu_dec, "total input frame size: %u", in_data.nSize); + + /* Using a mutex here, since the VPU_DecDecodeBuf() call internally picks an + * available framebuffer, and at the same time, the bufferpool release() function + * might be returning a framebuffer to the list of available ones */ + if (vpu_dec->current_framebuffers != NULL) + g_mutex_lock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + dec_ret = VPU_DecDecodeBuf(vpu_dec->handle, &in_data, &buffer_ret_code); + if (vpu_dec->current_framebuffers != NULL) + g_mutex_unlock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + + if (dec_ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "failed to decode frame: %s", gst_fsl_vpu_strerror(dec_ret)); + return GST_FLOW_ERROR; + } + + GST_LOG_OBJECT(vpu_dec, "VPU_DecDecodeBuf returns: %x", buffer_ret_code); + + /* Cleanup temporary combined block and input frame mapping */ + if (combined_input != NULL) + g_slice_free1(combined_input_size, combined_input); + gst_buffer_unmap(frame->input_buffer, &in_map_info); + + if (buffer_ret_code & VPU_DEC_INIT_OK) + { + dec_ret = VPU_DecGetInitialInfo(vpu_dec->handle, &(vpu_dec->init_info)); + if (dec_ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not get init info: %s", gst_fsl_vpu_strerror(dec_ret)); + return GST_FLOW_ERROR; + } + + /* Allocate and register a new set of framebuffers for decoding + * This point is always reached after set_format() was called, + * and always before a frame is output */ + vpu_dec->current_framebuffers = gst_fsl_vpu_framebuffers_new(vpu_dec->handle, &(vpu_dec->init_info)); + } + + if (buffer_ret_code & VPU_DEC_NO_ENOUGH_INBUF) + { + /* Not dropping frame here on purpose; the next input frame may + * complete the input */ + GST_DEBUG_OBJECT(vpu_dec, "need more input"); + return GST_FLOW_OK; + } + + if (vpu_dec->current_framebuffers == NULL) + { + GST_ERROR_OBJECT(vpu_dec, "no framebuffers allocated"); + return GST_FLOW_ERROR; + } + + do_memcpy = FALSE; + + /* The following code block may cause a race condition if not synchronized; + * the buffer pool release() function must not run at the same time */ + g_mutex_lock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + + if (buffer_ret_code & VPU_DEC_ONE_FRM_CONSUMED) + { + VpuDecFrameLengthInfo dec_framelen_info; + + dec_ret = VPU_DecGetConsumedFrameInfo(vpu_dec->handle, &dec_framelen_info); + if (dec_ret != VPU_DEC_RET_SUCCESS) + GST_ERROR_OBJECT(vpu_dec, "could not get information about consumed frame: %s", gst_fsl_vpu_strerror(dec_ret)); + + GST_DEBUG_OBJECT(vpu_dec, "one frame got consumed: framebuffer: %p stuff length: %d frame length: %d", dec_framelen_info.pFrame, dec_framelen_info.nStuffLength, dec_framelen_info.nFrameLength); + + if (vpu_dec->current_framebuffers->num_available_framebuffers > 0) + vpu_dec->current_framebuffers->num_available_framebuffers--; + GST_DEBUG_OBJECT(vpu_dec, "number of available buffers is %d", vpu_dec->current_framebuffers->num_available_framebuffers); + } + + /* There are no available framebuffers left; only the reserved ones + * -> instead of sending them downstream, mark their contents to be + * copied to memory allocated on the heap */ + if (vpu_dec->current_framebuffers->num_available_framebuffers <= 0) + { + GST_WARNING_OBJECT(vpu_dec, "no framebuffers available - copying decoded contents to a heap buffer"); + do_memcpy = TRUE; + } + + /* Unlock the mutex; the subsequent steps are safe */ + g_mutex_unlock(&(vpu_dec->current_framebuffers->available_fb_mutex)); + + if ((buffer_ret_code & VPU_DEC_OUTPUT_DIS) || (buffer_ret_code & VPU_DEC_OUTPUT_MOSAIC_DIS)) + { + GstBuffer *buffer; + VpuDecOutFrameInfo out_frame_info; + + /* Retrieve the decoded frame */ + dec_ret = VPU_DecGetOutputFrame(vpu_dec->handle, &out_frame_info); + if (dec_ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "could not get decoded output frame: %s", gst_fsl_vpu_strerror(dec_ret)); + return GST_FLOW_ERROR; + } + + /* Create empty buffer */ + buffer = gst_video_decoder_allocate_output_buffer(decoder); + /* ... and set its contents; either pass on the framebuffer directly, + * or have set_contents() copy its pixels to a memory block on the heap, + * depending on do_memcpy */ + if (!gst_fsl_vpu_set_buffer_contents(buffer, vpu_dec->current_framebuffers, out_frame_info.pDisplayFrameBuf, do_memcpy)) + { + gst_buffer_unref(buffer); + return GST_FLOW_ERROR; + } + + GST_DEBUG_OBJECT(vpu_dec, "output frame: framebuffer addr: %p gstbuffer addr: %p pic type: %d Y stride: %d CbCr stride: %d", out_frame_info.pDisplayFrameBuf, buffer, out_frame_info.ePicType, out_frame_info.pDisplayFrameBuf->nStrideY, out_frame_info.pDisplayFrameBuf->nStrideC); + + if (!do_memcpy) + { + /* If a framebuffer is sent downstream directly, it will + * have to be marked later as displayed after it was used, + * to allow the VPU wrapper to reuse it for new decoded + * frames. Since this is a fresh frame, and it wasn't + * used yet, mark it now as undisplayed. */ + gst_fsl_vpu_mark_buf_as_not_displayed(buffer); + } + + frame->output_buffer = buffer; + + gst_video_decoder_finish_frame(decoder, frame); + } + else if (buffer_ret_code & VPU_DEC_OUTPUT_DROPPED) + { + GST_DEBUG_OBJECT(vpu_dec, "dropping frame"); + gst_video_decoder_drop_frame(decoder, frame); + } + else if (buffer_ret_code & VPU_DEC_NO_ENOUGH_BUF) + GST_WARNING_OBJECT(vpu_dec, "no free output frame available (ret code: 0x%X)", buffer_ret_code); + else + GST_DEBUG_OBJECT(vpu_dec, "nothing to output (ret code: 0x%X)", buffer_ret_code); + + return GST_FLOW_OK; +} + + +static gboolean gst_fsl_vpu_dec_reset(GstVideoDecoder *decoder, gboolean hard) +{ + VpuDecRetCode ret; + GstFslVpuDec *vpu_dec = GST_FSL_VPU_DEC(decoder); + + if (!vpu_dec->vpu_inst_opened) + return TRUE; + + /* TODO: does the num_available_framebuffers counter have to be + * reset here? */ + + ret = VPU_DecFlushAll(vpu_dec->handle); + if (ret != VPU_DEC_RET_SUCCESS) + { + GST_ERROR_OBJECT(vpu_dec, "flushing VPU failed: %s", gst_fsl_vpu_strerror(ret)); + return FALSE; + } + + return TRUE; +} + + +static gboolean gst_fsl_vpu_dec_decide_allocation(GstVideoDecoder *decoder, GstQuery *query) +{ + GstFslVpuDec *vpu_dec = GST_FSL_VPU_DEC(decoder); + GstCaps *outcaps; + GstBufferPool *pool = NULL; + guint size, min = 0, max = 0; + GstStructure *config; + GstVideoInfo vinfo; + gboolean update_pool; + + g_assert(vpu_dec->current_framebuffers != NULL); + + gst_query_parse_allocation(query, &outcaps, NULL); + gst_video_info_init(&vinfo); + gst_video_info_from_caps(&vinfo, outcaps); + + GST_DEBUG_OBJECT(decoder, "num allocation pools: %d", gst_query_get_n_allocation_pools(query)); + + /* Look for an allocator which can allocate VPU DMA buffers */ + if (gst_query_get_n_allocation_pools(query) > 0) + { + for (guint i = 0; i < gst_query_get_n_allocation_pools(query); ++i) + { + gst_query_parse_nth_allocation_pool(query, i, &pool, &size, &min, &max); + if (gst_buffer_pool_has_option(pool, GST_BUFFER_POOL_OPTION_FSL_VPU_FRAMEBUFFER)) + break; + } + + size = MAX(size, (guint)(vpu_dec->current_framebuffers->total_size)); + size = MAX(size, vinfo.size); + update_pool = TRUE; + } + else + { + pool = NULL; + size = MAX(vinfo.size, (guint)(vpu_dec->current_framebuffers->total_size)); + min = max = 0; + update_pool = FALSE; + } + + /* Either no pool or no pool with the ability to allocate VPU DMA buffers + * has been found -> create a new pool */ + if ((pool == NULL) || !gst_buffer_pool_has_option(pool, GST_BUFFER_POOL_OPTION_FSL_VPU_FRAMEBUFFER)) + { + if (pool == NULL) + GST_DEBUG_OBJECT(decoder, "no pool present; creating new pool"); + else + GST_DEBUG_OBJECT(decoder, "no pool supports VPU buffers; creating new pool"); + pool = gst_fsl_vpu_buffer_pool_new(vpu_dec->current_framebuffers); + } + + GST_DEBUG_OBJECT( + pool, + "pool config: outcaps: %" GST_PTR_FORMAT " size: %u min buffers: %u max buffers: %u", + outcaps, + size, + min, + max + ); + + /* Inform the pool about the framebuffers */ + gst_fsl_vpu_buffer_pool_set_framebuffer(pool, vpu_dec->current_framebuffers); + + /* Now configure the pool. */ + config = gst_buffer_pool_get_config(pool); + gst_buffer_pool_config_set_params(config, outcaps, size, min, max); + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_FSL_VPU_FRAMEBUFFER); + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_set_config(pool, config); + + if (update_pool) + gst_query_set_nth_allocation_pool(query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool(query, pool, size, min, max); + + if (pool != NULL) + gst_object_unref(pool); + + return TRUE; +} + diff --git a/src/decoder/vpu_decoder.h b/src/decoder/vpu_decoder.h new file mode 100644 index 0000000..8a1c6ae --- /dev/null +++ b/src/decoder/vpu_decoder.h @@ -0,0 +1,80 @@ +/* GStreamer video decoder using the Freescale VPU hardware video engine + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifndef VPU_DECODER_H +#define VPU_DECODER_H + +#include +#include +#include +#include + +#include + +#include "../common/vpu_framebuffers.h" + + +G_BEGIN_DECLS + + +typedef struct _GstFslVpuDec GstFslVpuDec; +typedef struct _GstFslVpuDecClass GstFslVpuDecClass; + + +#define GST_TYPE_FSL_VPU_DEC (gst_fsl_vpu_dec_get_type()) +#define GST_FSL_VPU_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_FSL_VPU_DEC, GstFslVpuDec)) +#define GST_FSL_VPU_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_FSL_VPU_DEC, GstFslVpuDecClass)) +#define GST_IS_FSL_VPU_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_FSL_VPU_DEC)) +#define GST_IS_FSL_VPU_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_FSL_VPU_DEC)) + + +struct _GstFslVpuDec +{ + GstVideoDecoder parent; + + VpuDecHandle handle; + + VpuDecInitInfo init_info; + VpuMemInfo mem_info; + + gboolean vpu_inst_opened; + + GstBuffer *codec_data; + + GstFslVpuFramebuffers *current_framebuffers; + + GSList *virt_dec_mem_blocks, *phys_dec_mem_blocks; +}; + + +struct _GstFslVpuDecClass +{ + GstVideoDecoderClass parent_class; + gint inst_counter; +}; + + +GType gst_fsl_vpu_dec_get_type(void); + + +G_END_DECLS + + +#endif + diff --git a/src/decoder/vpu_decplugin.c b/src/decoder/vpu_decplugin.c new file mode 100644 index 0000000..09a641a --- /dev/null +++ b/src/decoder/vpu_decplugin.c @@ -0,0 +1,49 @@ +/* GStreamer video decoder using the Freescale VPU hardware video engine + * Copyright (C) 2013 Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include "config.h" +#include "vpu_decoder.h" +#include "../common/vpu_utils.h" + + + +static gboolean plugin_init(GstPlugin *plugin) +{ + gst_fsl_vpu_init_alloc_debug(); + + gboolean ret = TRUE; + ret = ret && gst_element_register(plugin, "fslvpudec", GST_RANK_PRIMARY + 1, gst_fsl_vpu_dec_get_type()); + return ret; +} + + + +GST_PLUGIN_DEFINE( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + fslvpudec, + "hardware-accelerated video decoding using the Freescale VPU engine", + plugin_init, + VERSION, + "LGPL", + GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN +) + diff --git a/waf b/waf new file mode 100755 index 0000000000000000000000000000000000000000..d26b50054262724bb6aa59f1e9b2723eeca8227f GIT binary patch literal 91066 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

AybqWQ`-D+3AlQbS3}20mmNHfe1oK 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 to trigger a +# compiler warning; sometimes, gcc does not tell about an unsupported parameter *unless* the +# code being compiled causes a warning +c_cflag_check_code = """ +int main() +{ + float f = 4.0; + char c = f; + return c - 4; +} +""" +def check_compiler_flag(conf, flag, lang): + return conf.check(fragment = c_cflag_check_code, mandatory = 0, execute = 0, define_ret = 0, msg = 'Checking for compiler switch %s' % flag, cxxflags = conf.env[lang + 'FLAGS'] + [flag], okmsg = 'yes', errmsg = 'no') +def check_compiler_flags_2(conf, cflags, ldflags, msg): + return conf.check(fragment = c_cflag_check_code, mandatory = 0, execute = 0, define_ret = 0, msg = msg, cxxflags = cflags, ldflags = ldflags, okmsg = 'yes', errmsg = 'no') + + +def add_compiler_flags(conf, env, flags, lang, compiler, uselib = ''): + for flag in reversed(flags): + if type(flag) == type(()): + flag_candidate = flag[0] + flag_alternative = flag[1] + else: + flag_candidate = flag + flag_alternative = None + + if uselib: + flags_pattern = lang + 'FLAGS_' + uselib + else: + flags_pattern = lang + 'FLAGS' + + if check_compiler_flag(conf, flag_candidate, compiler): + env.prepend_value(flags_pattern, [flag_candidate]) + elif flag_alternative: + if check_compiler_flag(conf, flag_alternative, compiler): + env.prepend_value(flags_pattern, [flag_alternative]) + + +def options(opt): + opt.add_option('--enable-debug', action = 'store_true', default = False, help = 'enable debug build [default: %default]') + opt.add_option('--with-package-name', action = 'store', default = "Unknown package release", help = 'specify package name to use in plugin [default: %default]') + opt.add_option('--with-package-origin', action = 'store', default = "Unknown package origin", help = 'specify package origin URL to use in plugin [default: %default]') + opt.add_option('--plugin-install-path', action = 'store', default = "${PREFIX}/lib/gstreamer-1.0", help = 'where to install the plugin for GStreamer 1.0 [default: %default]') + opt.load('compiler_c') + + +def configure(conf): + import os + + conf.load('compiler_c') + + # check and add compiler flags + + if conf.env['CFLAGS'] and conf.env['LINKFLAGS']: + check_compiler_flags_2(conf, conf.env['CFLAGS'], conf.env['LINKFLAGS'], "Testing compiler flags %s and linker flags %s" % (' '.join(conf.env['CFLAGS']), ' '.join(conf.env['LINKFLAGS']))) + elif conf.env['CFLAGS']: + check_compiler_flags_2(conf, conf.env['CFLAGS'], '', "Testing compiler flags %s" % ' '.join(conf.env['CFLAGS'])) + elif conf.env['LINKFLAGS']: + check_compiler_flags_2(conf, '', conf.env['LINKFLAGS'], "Testing linker flags %s" % ' '.join(conf.env['LINKFLAGS'])) + + compiler_flags = ['-Wextra', '-Wall', '-std=c99', '-pedantic', '-fPIC', '-DPIC'] + if conf.options.enable_debug: + compiler_flags += ['-O0', '-g3', '-ggdb'] + else: + compiler_flags += ['-O2'] + + add_compiler_flags(conf, conf.env, compiler_flags, 'C', 'C') + + + # test for pthreads and the math library + + conf.check_cc(lib = 'm', uselib_store = 'M', mandatory = 1) + + if conf.check_cc(lib = 'pthread', uselib_store = 'PTHREAD', mandatory = 1): + conf.env['CFLAGS_PTHREAD'] += ['-pthread'] + + + # test for GStreamer libraries + + conf.check_cfg(package = 'gstreamer-1.0 >= 1.0.0', uselib_store = 'GSTREAMER', args = '--cflags --libs', mandatory = 1) + conf.check_cfg(package = 'gstreamer-base-1.0 >= 1.0.0', uselib_store = 'GSTREAMER_BASE', args = '--cflags --libs', mandatory = 1) + conf.check_cfg(package = 'gstreamer-video-1.0 >= 1.0.0', uselib_store = 'GSTREAMER_VIDEO', args = '--cflags --libs', mandatory = 1) + + # test for Freescale libraries + + conf.check_cfg(package = 'libfslvpuwrap', uselib_store = 'FSLVPUWRAPPER', args = '--cflags --libs', mandatory = 1) + + + conf.env['PLUGIN_INSTALL_PATH'] = os.path.expanduser(conf.options.plugin_install_path) + + conf.define('GST_PACKAGE_NAME', conf.options.with_package_name) + conf.define('GST_PACKAGE_ORIGIN', conf.options.with_package_origin) + conf.define('PACKAGE', "gst-fsl") + conf.define('VERSION', "1.0") + + + conf.write_config_header('config.h') + + + +def build(bld): + common_source = bld.path.ant_glob('src/common/*.c') + common_uselib = ['GSTREAMER', 'GSTREAMER_BASE', 'GSTREAMER_VIDEO', 'FSLVPUWRAPPER', 'PTHREAD', 'M'] + + bld( + features = ['c', 'cshlib'], + includes = ['.'], + uselib = common_uselib, + target = 'gstfsl', + source = common_source + bld.path.ant_glob('src/decoder/*.c'), + install_path = bld.env['PLUGIN_INSTALL_PATH'] + ) + + +