summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2014-01-07 16:20:51 +0100
committerThomas Reufer <thomas@reufer.ch>2014-01-07 16:20:51 +0100
commitd2ed6de22829288c5cfd3a52b1da090e96d3329d (patch)
treeef84cba3610e96cc4c58359da3c8a69882aa03c9
downloadvdr-plugin-rpihddevice-0.0.1.tar.gz
vdr-plugin-rpihddevice-0.0.1.tar.bz2
2013-09-27: Version 0.0.10.0.1
Initial prototype - limitations: - video codec hard coded to MPEG2, output on HDMI - audio codec hard coded to MP3, output on phone jack - tested: - OSD - SDTV live view and replay - missing: - dynamic switching between MPEG2 and H264 video codec - trick speeds - much more...
-rw-r--r--COPYING340
-rw-r--r--HISTORY17
-rw-r--r--Makefile138
-rw-r--r--README18
-rw-r--r--ilclient/Makefile23
-rw-r--r--ilclient/ilclient.c1836
-rw-r--r--ilclient/ilclient.h1039
-rw-r--r--ilclient/ilcore.c308
-rw-r--r--omxdevice.c782
-rw-r--r--omxdevice.h90
-rw-r--r--ovgosd.c297
-rw-r--r--ovgosd.h52
-rw-r--r--rpihddevice.c98
13 files changed, 5038 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, 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 software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, 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 redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..e2632e0
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,17 @@
+VDR Plugin 'rpihddevice' Revision History
+-----------------------------------------
+
+2013-09-27: Version 0.0.1
+
+Initial prototype
+- limitations:
+ - video codec hard coded to MPEG2, output on HDMI
+ - audio codec hard coded to MP3, output on phone jack
+- tested:
+ - OSD
+ - SDTV live view and replay
+- missing:
+ - dynamic switching between MPEG2 and H264 video codec
+ - trick speeds
+ - much more...
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..090d5ce
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,138 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile 2.18 2013/01/12 13:45:01 kls Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+
+PLUGIN = rpihddevice
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The directory environment:
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+### The version number of VDR's plugin API:
+
+APIVERSION = $(call PKGCFG,apiversion)
+
+### Allow user defined options to overwrite defaults:
+
+-include $(PLGCFG)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+DEFINES += -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM
+DEFINES += -Wno-write-strings -fpermissive
+
+INCLUDES += -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads
+
+ILCDIR = ilclient
+VCLIBDIR = /opt/vc/lib
+
+LDFLAGS += -L$(VCLIBDIR) -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt -lmpg123
+LDFLAGS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive
+
+# -I/opt/vc/src/hello_pi/libs/vgfont
+
+### The object files (add further files here):
+
+ILCLIENT = $(ILCDIR)/libilclient.a
+OBJS = $(PLUGIN).o omxdevice.o ovgosd.o
+
+### The main target:
+
+all: $(SOFILE) i18n
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+
+$(SOFILE): $(ILCLIENT) $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@
+
+$(ILCLIENT):
+ $(MAKE) --no-print-directory -C $(ILCDIR) all
+
+install-lib: $(SOFILE)
+ install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib install-i18n
+
+dist: $(I18Npo) clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
+ $(MAKE) --no-print-directory -C $(ILCDIR) clean
+
diff --git a/README b/README
new file mode 100644
index 0000000..f90aa02
--- /dev/null
+++ b/README
@@ -0,0 +1,18 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Thomas Reufer <thomas@reufer.ch>
+
+Project's homepage: t.b.d.
+
+Latest version available at: t.b.d.
+
+See the file COPYING for license information.
+
+Description:
+
+ Output device for Raspberry Pi.
+
+Requirements:
+
+ - valid MPEG2 licence
+ - libmpg123
diff --git a/ilclient/Makefile b/ilclient/Makefile
new file mode 100644
index 0000000..7a967f4
--- /dev/null
+++ b/ilclient/Makefile
@@ -0,0 +1,23 @@
+OBJS=ilclient.o ilcore.o
+LIB=libilclient.a
+
+CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi
+
+INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux
+
+all: $(LIB)
+
+%.o: %.c
+ @rm -f $@
+ $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations
+
+%.a: $(OBJS)
+ $(AR) r $@ $^
+
+clean:
+ for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done
+ @rm -f $(BIN) $(LIB)
+
+
+
+
diff --git a/ilclient/ilclient.c b/ilclient/ilclient.c
new file mode 100644
index 0000000..da08ad0
--- /dev/null
+++ b/ilclient/ilclient.c
@@ -0,0 +1,1836 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * \file
+ *
+ * \brief This API defines helper functions for writing IL clients.
+ *
+ * This file defines an IL client side library. This is useful when
+ * writing IL clients, since there tends to be much repeated and
+ * common code across both single and multiple clients. This library
+ * seeks to remove that common code and abstract some of the
+ * interactions with components. There is a wrapper around a
+ * component and tunnel, and some operations can be done on lists of
+ * these. The callbacks from components are handled, and specific
+ * events can be checked or waited for.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "interface/vcos/vcos.h"
+#include "interface/vcos/vcos_logging.h"
+#include "interface/vmcs_host/vchost.h"
+
+#include "IL/OMX_Broadcom.h"
+#include "ilclient.h"
+
+#define VCOS_LOG_CATEGORY (&ilclient_log_category)
+
+#ifndef ILCLIENT_THREAD_DEFAULT_STACK_SIZE
+#define ILCLIENT_THREAD_DEFAULT_STACK_SIZE (6<<10)
+#endif
+
+static VCOS_LOG_CAT_T ilclient_log_category;
+
+/******************************************************************************
+Static data and types used only in this file.
+******************************************************************************/
+
+struct _ILEVENT_T {
+ OMX_EVENTTYPE eEvent;
+ OMX_U32 nData1;
+ OMX_U32 nData2;
+ OMX_PTR pEventData;
+ struct _ILEVENT_T *next;
+};
+
+#define NUM_EVENTS 100
+struct _ILCLIENT_T {
+ ILEVENT_T *event_list;
+ VCOS_SEMAPHORE_T event_sema;
+ ILEVENT_T event_rep[NUM_EVENTS];
+
+ ILCLIENT_CALLBACK_T port_settings_callback;
+ void *port_settings_callback_data;
+ ILCLIENT_CALLBACK_T eos_callback;
+ void *eos_callback_data;
+ ILCLIENT_CALLBACK_T error_callback;
+ void *error_callback_data;
+ ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback;
+ void *fill_buffer_done_callback_data;
+ ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback;
+ void *empty_buffer_done_callback_data;
+ ILCLIENT_CALLBACK_T configchanged_callback;
+ void *configchanged_callback_data;
+};
+
+struct _COMPONENT_T {
+ OMX_HANDLETYPE comp;
+ ILCLIENT_CREATE_FLAGS_T flags;
+ VCOS_SEMAPHORE_T sema;
+ VCOS_EVENT_FLAGS_T event;
+ struct _COMPONENT_T *related;
+ OMX_BUFFERHEADERTYPE *out_list;
+ OMX_BUFFERHEADERTYPE *in_list;
+ char name[32];
+ char bufname[32];
+ unsigned int error_mask;
+ unsigned int private;
+ ILEVENT_T *list;
+ ILCLIENT_T *client;
+};
+
+#define random_wait()
+static char *states[] = {"Invalid", "Loaded", "Idle", "Executing", "Pause", "WaitingForResources"};
+
+typedef enum {
+ ILCLIENT_ERROR_UNPOPULATED = 0x1,
+ ILCLIENT_ERROR_SAMESTATE = 0x2,
+ ILCLIENT_ERROR_BADPARAMETER = 0x4
+} ILERROR_MASK_T;
+
+/******************************************************************************
+Static functions.
+******************************************************************************/
+
+static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent,
+ OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
+static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent,
+ OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
+static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+static void ilclient_lock_events(ILCLIENT_T *st);
+static void ilclient_unlock_events(ILCLIENT_T *st);
+
+/******************************************************************************
+Global functions
+******************************************************************************/
+
+/***********************************************************
+ * Name: ilclient_init
+ *
+ * Description: Creates ilclient pointer
+ *
+ * Returns: pointer to client structure
+ ***********************************************************/
+ILCLIENT_T *ilclient_init()
+{
+ ILCLIENT_T *st = vcos_malloc(sizeof(ILCLIENT_T), "ilclient");
+ int i;
+
+ if (!st)
+ return NULL;
+
+ vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_WARN);
+ vcos_log_register("ilclient", VCOS_LOG_CATEGORY);
+
+ memset(st, 0, sizeof(ILCLIENT_T));
+
+ i = vcos_semaphore_create(&st->event_sema, "il:event", 1);
+ vc_assert(i == VCOS_SUCCESS);
+
+ ilclient_lock_events(st);
+ st->event_list = NULL;
+ for (i=0; i<NUM_EVENTS; i++)
+ {
+ st->event_rep[i].eEvent = -1; // mark as unused
+ st->event_rep[i].next = st->event_list;
+ st->event_list = st->event_rep+i;
+ }
+ ilclient_unlock_events(st);
+ return st;
+}
+
+/***********************************************************
+ * Name: ilclient_destroy
+ *
+ * Description: frees client state
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_destroy(ILCLIENT_T *st)
+{
+ vcos_semaphore_delete(&st->event_sema);
+ vcos_free(st);
+ vcos_log_unregister(VCOS_LOG_CATEGORY);
+}
+
+/***********************************************************
+ * Name: ilclient_set_port_settings_callback
+ *
+ * Description: sets the callback used when receiving port settings
+ * changed messages. The data field in the callback function will be
+ * the port index reporting the message.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_port_settings_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata)
+{
+ st->port_settings_callback = func;
+ st->port_settings_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_set_eos_callback
+ *
+ * Description: sets the callback used when receiving eos flags. The
+ * data parameter in the callback function will be the port index
+ * reporting an eos flag.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_eos_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata)
+{
+ st->eos_callback = func;
+ st->eos_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_set_error_callback
+ *
+ * Description: sets the callback used when receiving error events.
+ * The data parameter in the callback function will be the error code
+ * being reported.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_error_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata)
+{
+ st->error_callback = func;
+ st->error_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_set_fill_buffer_done_callback
+ *
+ * Description: sets the callback used when receiving
+ * fill_buffer_done event
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_fill_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata)
+{
+ st->fill_buffer_done_callback = func;
+ st->fill_buffer_done_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_set_empty_buffer_done_callback
+ *
+ * Description: sets the callback used when receiving
+ * empty_buffer_done event
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_empty_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata)
+{
+ st->empty_buffer_done_callback = func;
+ st->empty_buffer_done_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_set_configchanged_callback
+ *
+ * Description: sets the callback used when a config changed
+ * event is received
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_set_configchanged_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata)
+{
+ st->configchanged_callback = func;
+ st->configchanged_callback_data = userdata;
+}
+
+/***********************************************************
+ * Name: ilclient_create_component
+ *
+ * Description: initialises a component state structure and creates
+ * the IL component.
+ *
+ * Returns: 0 on success, -1 on failure
+ ***********************************************************/
+int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name,
+ ILCLIENT_CREATE_FLAGS_T flags)
+{
+ OMX_CALLBACKTYPE callbacks;
+ OMX_ERRORTYPE error;
+ char component_name[128];
+ int32_t status;
+
+ *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp");
+ if(!*comp)
+ return -1;
+
+ memset(*comp, 0, sizeof(COMPONENT_T));
+
+#define COMP_PREFIX "OMX.broadcom."
+
+ status = vcos_event_flags_create(&(*comp)->event,"il:comp");
+ vc_assert(status == VCOS_SUCCESS);
+ status = vcos_semaphore_create(&(*comp)->sema, "il:comp", 1);
+ vc_assert(status == VCOS_SUCCESS);
+ (*comp)->client = client;
+
+ vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", name);
+ vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", name);
+ vcos_snprintf(component_name, sizeof(component_name), "%s%s", COMP_PREFIX, name);
+
+ (*comp)->flags = flags;
+
+ callbacks.EventHandler = ilclient_event_handler;
+ callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error;
+ callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error;
+
+ error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks);
+
+ if (error == OMX_ErrorNone)
+ {
+ OMX_UUIDTYPE uid;
+ char name[128];
+ OMX_VERSIONTYPE compVersion, specVersion;
+
+ if(OMX_GetComponentVersion((*comp)->comp, name, &compVersion, &specVersion, &uid) == OMX_ErrorNone)
+ {
+ char *p = (char *) uid + strlen(COMP_PREFIX);
+
+ vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", p);
+ (*comp)->name[sizeof((*comp)->name)-1] = 0;
+ vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", p);
+ (*comp)->bufname[sizeof((*comp)->bufname)-1] = 0;
+ }
+
+ if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS))
+ {
+ OMX_PORT_PARAM_TYPE ports;
+ OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit};
+ int i;
+
+ ports.nSize = sizeof(OMX_PORT_PARAM_TYPE);
+ ports.nVersion.nVersion = OMX_VERSION;
+
+ for(i=0; i<4; i++)
+ {
+ OMX_ERRORTYPE error = OMX_GetParameter((*comp)->comp, types[i], &ports);
+ if(error == OMX_ErrorNone)
+ {
+ uint32_t j;
+ for(j=0; j<ports.nPorts; j++)
+ {
+ if(flags & ILCLIENT_DISABLE_ALL_PORTS)
+ ilclient_disable_port(*comp, ports.nStartPortNumber+j);
+
+ if(flags & ILCLIENT_OUTPUT_ZERO_BUFFERS)
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE portdef;
+ portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+ portdef.nVersion.nVersion = OMX_VERSION;
+ portdef.nPortIndex = ports.nStartPortNumber+j;
+ if(OMX_GetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef) == OMX_ErrorNone)
+ {
+ if(portdef.eDir == OMX_DirOutput && portdef.nBufferCountActual > 0)
+ {
+ portdef.nBufferCountActual = 0;
+ OMX_SetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ vcos_event_flags_delete(&(*comp)->event);
+ vcos_semaphore_delete(&(*comp)->sema);
+ vcos_free(*comp);
+ *comp = NULL;
+ return -1;
+ }
+}
+
+/***********************************************************
+ * Name: ilclient_remove_event
+ *
+ * Description: Removes an event from a component event list. ignore1
+ * and ignore2 are flags indicating whether to not match on nData1 and
+ * nData2 respectively.
+ *
+ * Returns: 0 if the event was removed. -1 if no matching event was
+ * found.
+ ***********************************************************/
+int ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent,
+ OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2)
+{
+ ILEVENT_T *cur, *prev;
+ uint32_t set;
+ ilclient_lock_events(st->client);
+
+ cur = st->list;
+ prev = NULL;
+
+ while (cur && !(cur->eEvent == eEvent && (ignore1 || cur->nData1 == nData1) && (ignore2 || cur->nData2 == nData2)))
+ {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur == NULL)
+ {
+ ilclient_unlock_events(st->client);
+ return -1;
+ }
+
+ if (prev == NULL)
+ st->list = cur->next;
+ else
+ prev->next = cur->next;
+
+ // add back into spare list
+ cur->next = st->client->event_list;
+ st->client->event_list = cur;
+ cur->eEvent = -1; // mark as unused
+
+ // if we're removing an OMX_EventError or OMX_EventParamOrConfigChanged event, then clear the error bit from the eventgroup,
+ // since the user might have been notified through the error callback, and then
+ // can't clear the event bit - this will then cause problems the next time they
+ // wait for an error.
+ if(eEvent == OMX_EventError)
+ vcos_event_flags_get(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set);
+ else if(eEvent == OMX_EventParamOrConfigChanged)
+ vcos_event_flags_get(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR_CONSUME, 0, &set);
+
+ ilclient_unlock_events(st->client);
+ return 0;
+}
+
+/***********************************************************
+ * Name: ilclient_state_transition
+ *
+ * Description: Transitions a null terminated list of IL components to
+ * a given state. All components are told to transition in a random
+ * order before any are checked for transition completion.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_state_transition(COMPONENT_T *list[], OMX_STATETYPE state)
+{
+ OMX_ERRORTYPE error;
+ int i, num;
+ uint32_t set;
+
+ num=0;
+ while (list[num])
+ num++;
+
+ // if we transition the supplier port first, it will call freebuffer on the non
+ // supplier, which will correctly signal a port unpopulated error. We want to
+ // ignore these errors.
+ if (state == OMX_StateLoaded)
+ for (i=0; i<num; i++)
+ list[i]->error_mask |= ILCLIENT_ERROR_UNPOPULATED;
+ for (i=0; i<num; i++)
+ list[i]->private = ((rand() >> 13) & 0xff)+1;
+
+ for (i=0; i<num; i++)
+ {
+ // transition the components in a random order
+ int j, min = -1;
+ for (j=0; j<num; j++)
+ if (list[j]->private && (min == -1 || list[min]->private > list[j]->private))
+ min = j;
+
+ list[min]->private = 0;
+
+ random_wait();
+ //Clear error event for this component
+ vcos_event_flags_get(&list[min]->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set);
+
+ error = OMX_SendCommand(list[min]->comp, OMX_CommandStateSet, state, NULL);
+ vc_assert(error == OMX_ErrorNone);
+ }
+
+ random_wait();
+
+ for (i=0; i<num; i++)
+ if(ilclient_wait_for_command_complete(list[i], OMX_CommandStateSet, state) < 0)
+ vc_assert(0);
+
+ if (state == OMX_StateLoaded)
+ for (i=0; i<num; i++)
+ list[i]->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED;
+}
+
+/***********************************************************
+ * Name: ilclient_teardown_tunnels
+ *
+ * Description: tears down a null terminated list of tunnels.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_teardown_tunnels(TUNNEL_T *tunnel)
+{
+ int i;
+ OMX_ERRORTYPE error;
+
+ i=0;;
+ while (tunnel[i].source)
+ {
+ error = OMX_SetupTunnel(tunnel[i].source->comp, tunnel[i].source_port, NULL, 0);
+ vc_assert(error == OMX_ErrorNone);
+
+ error = OMX_SetupTunnel(tunnel[i].sink->comp, tunnel[i].sink_port, NULL, 0);
+ vc_assert(error == OMX_ErrorNone);
+ i++;
+ }
+}
+
+/***********************************************************
+ * Name: ilclient_disable_tunnel
+ *
+ * Description: disables a tunnel by disabling the ports. Allows
+ * ports to signal same state error if they were already disabled.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_disable_tunnel(TUNNEL_T *tunnel)
+{
+ OMX_ERRORTYPE error;
+
+ if(tunnel->source == 0 || tunnel->sink == 0)
+ return;
+
+ tunnel->source->error_mask |= ILCLIENT_ERROR_UNPOPULATED;
+ tunnel->sink->error_mask |= ILCLIENT_ERROR_UNPOPULATED;
+
+ error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortDisable, tunnel->source_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortDisable, tunnel->sink_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortDisable, tunnel->source_port) < 0)
+ vc_assert(0);
+
+ if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortDisable, tunnel->sink_port) < 0)
+ vc_assert(0);
+
+ tunnel->source->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED;
+ tunnel->sink->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED;
+}
+
+/***********************************************************
+ * Name: ilclient_enable_tunnel
+ *
+ * Description: enables a tunnel by enabling the ports
+ *
+ * Returns: 0 on success, -1 on failure
+ ***********************************************************/
+int ilclient_enable_tunnel(TUNNEL_T *tunnel)
+{
+ OMX_STATETYPE state;
+ OMX_ERRORTYPE error;
+
+ ilclient_debug_output("ilclient: enable tunnel from %x/%d to %x/%d",
+ tunnel->source, tunnel->source_port,
+ tunnel->sink, tunnel->sink_port);
+
+ error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortEnable, tunnel->source_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortEnable, tunnel->sink_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ // to complete, the sink component can't be in loaded state
+ error = OMX_GetState(tunnel->sink->comp, &state);
+ vc_assert(error == OMX_ErrorNone);
+ if (state == OMX_StateLoaded)
+ {
+ int ret = 0;
+
+ if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0 ||
+ OMX_SendCommand(tunnel->sink->comp, OMX_CommandStateSet, OMX_StateIdle, NULL) != OMX_ErrorNone ||
+ (ret = ilclient_wait_for_command_complete_dual(tunnel->sink, OMX_CommandStateSet, OMX_StateIdle, tunnel->source)) < 0)
+ {
+ if(ret == -2)
+ {
+ // the error was reported fom the source component: clear this error and disable the sink component
+ ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port);
+ ilclient_disable_port(tunnel->sink, tunnel->sink_port);
+ }
+
+ ilclient_debug_output("ilclient: could not change component state to IDLE");
+ ilclient_disable_port(tunnel->source, tunnel->source_port);
+ return -1;
+ }
+ }
+ else
+ {
+ if (ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0)
+ {
+ ilclient_debug_output("ilclient: could not change sink port %d to enabled", tunnel->sink_port);
+
+ //Oops failed to enable the sink port
+ ilclient_disable_port(tunnel->source, tunnel->source_port);
+ //Clean up the port enable event from the source port.
+ ilclient_wait_for_event(tunnel->source, OMX_EventCmdComplete,
+ OMX_CommandPortEnable, 0, tunnel->source_port, 0,
+ ILCLIENT_PORT_ENABLED | ILCLIENT_EVENT_ERROR, VCOS_EVENT_FLAGS_SUSPEND);
+ return -1;
+ }
+ }
+
+ if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port) != 0)
+ {
+ ilclient_debug_output("ilclient: could not change source port %d to enabled", tunnel->source_port);
+
+ //Failed to enable the source port
+ ilclient_disable_port(tunnel->sink, tunnel->sink_port);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/***********************************************************
+ * Name: ilclient_flush_tunnels
+ *
+ * Description: flushes all ports used in a null terminated list of
+ * tunnels. max specifies the maximum number of tunnels to flush from
+ * the list, where max=0 means all tunnels.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_flush_tunnels(TUNNEL_T *tunnel, int max)
+{
+ OMX_ERRORTYPE error;
+ int i;
+
+ i=0;
+ while (tunnel[i].source && (max == 0 || i < max))
+ {
+ error = OMX_SendCommand(tunnel[i].source->comp, OMX_CommandFlush, tunnel[i].source_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ error = OMX_SendCommand(tunnel[i].sink->comp, OMX_CommandFlush, tunnel[i].sink_port, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ ilclient_wait_for_event(tunnel[i].source, OMX_EventCmdComplete,
+ OMX_CommandFlush, 0, tunnel[i].source_port, 0,
+ ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND);
+ ilclient_wait_for_event(tunnel[i].sink, OMX_EventCmdComplete,
+ OMX_CommandFlush, 0, tunnel[i].sink_port, 0,
+ ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND);
+ i++;
+ }
+}
+
+
+/***********************************************************
+ * Name: ilclient_return_events
+ *
+ * Description: Returns all events from a component event list to the
+ * list of unused event structures.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_return_events(COMPONENT_T *comp)
+{
+ ilclient_lock_events(comp->client);
+ while (comp->list)
+ {
+ ILEVENT_T *next = comp->list->next;
+ comp->list->next = comp->client->event_list;
+ comp->client->event_list = comp->list;
+ comp->list = next;
+ }
+ ilclient_unlock_events(comp->client);
+}
+
+/***********************************************************
+ * Name: ilclient_cleanup_components
+ *
+ * Description: frees all components from a null terminated list and
+ * deletes resources used in component state structure.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_cleanup_components(COMPONENT_T *list[])
+{
+ int i;
+ OMX_ERRORTYPE error;
+
+ i=0;
+ while (list[i])
+ {
+ ilclient_return_events(list[i]);
+ if (list[i]->comp)
+ {
+ error = OMX_FreeHandle(list[i]->comp);
+
+ vc_assert(error == OMX_ErrorNone);
+ }
+ i++;
+ }
+
+ i=0;
+ while (list[i])
+ {
+ vcos_event_flags_delete(&list[i]->event);
+ vcos_semaphore_delete(&list[i]->sema);
+ vcos_free(list[i]);
+ list[i] = NULL;
+ i++;
+ }
+}
+
+/***********************************************************
+ * Name: ilclient_change_component_state
+ *
+ * Description: changes the state of a single component. Note: this
+ * may not be suitable if the component is tunnelled and requires
+ * connected components to also change state.
+ *
+ * Returns: 0 on success, -1 on failure (note - trying to change to
+ * the same state which causes a OMX_ErrorSameState is treated as
+ * success)
+ ***********************************************************/
+int ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state)
+{
+ OMX_ERRORTYPE error;
+ error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL);
+ vc_assert(error == OMX_ErrorNone);
+ if(ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state) < 0)
+ {
+ ilclient_debug_output("ilclient: could not change component state to %d", state);
+ ilclient_remove_event(comp, OMX_EventError, 0, 1, 0, 1);
+ return -1;
+ }
+ return 0;
+}
+
+/***********************************************************
+ * Name: ilclient_disable_port
+ *
+ * Description: disables a port on a given component.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_disable_port(COMPONENT_T *comp, int portIndex)
+{
+ OMX_ERRORTYPE error;
+ error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL);
+ vc_assert(error == OMX_ErrorNone);
+ if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0)
+ vc_assert(0);
+}
+
+/***********************************************************
+ * Name: ilclient_enabled_port
+ *
+ * Description: enables a port on a given component.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_enable_port(COMPONENT_T *comp, int portIndex)
+{
+ OMX_ERRORTYPE error;
+ error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL);
+ vc_assert(error == OMX_ErrorNone);
+ if(ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0)
+ vc_assert(0);
+}
+
+
+/***********************************************************
+ * Name: ilclient_enable_port_buffers
+ *
+ * Description: enables a port on a given component which requires
+ * buffers to be supplied by the client.
+ *
+ * Returns: void
+ ***********************************************************/
+int ilclient_enable_port_buffers(COMPONENT_T *comp, int portIndex,
+ ILCLIENT_MALLOC_T ilclient_malloc,
+ ILCLIENT_FREE_T ilclient_free,
+ void *private)
+{
+ OMX_ERRORTYPE error;
+ OMX_PARAM_PORTDEFINITIONTYPE portdef;
+ OMX_BUFFERHEADERTYPE *list = NULL, **end = &list;
+ OMX_STATETYPE state;
+ int i;
+
+ memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+ portdef.nVersion.nVersion = OMX_VERSION;
+ portdef.nPortIndex = portIndex;
+
+ // work out buffer requirements, check port is in the right state
+ error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef);
+ if(error != OMX_ErrorNone || portdef.bEnabled != OMX_FALSE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0)
+ return -1;
+
+ // check component is in the right state to accept buffers
+ error = OMX_GetState(comp->comp, &state);
+ if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause))
+ return -1;
+
+ // send the command
+ error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ for (i=0; i != portdef.nBufferCountActual; i++)
+ {
+ unsigned char *buf;
+ if(ilclient_malloc)
+ buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname);
+ else
+ buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname);
+
+ if(!buf)
+ break;
+
+ error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf);
+ if(error != OMX_ErrorNone)
+ {
+ if(ilclient_free)
+ ilclient_free(private, buf);
+ else
+ vcos_free(buf);
+
+ break;
+ }
+ end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate);
+ }
+
+ // queue these buffers
+ vcos_semaphore_wait(&comp->sema);
+
+ if(portdef.eDir == OMX_DirInput)
+ {
+ *end = comp->in_list;
+ comp->in_list = list;
+ }
+ else
+ {
+ *end = comp->out_list;
+ comp->out_list = list;
+ }
+
+ vcos_semaphore_post(&comp->sema);
+
+ if(i != portdef.nBufferCountActual ||
+ ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0)
+ {
+ ilclient_disable_port_buffers(comp, portIndex, NULL, ilclient_free, private);
+
+ // at this point the first command might have terminated with an error, which means that
+ // the port is disabled before the disable_port_buffers function is called, so we're left
+ // with the error bit set and an error event in the queue. Clear these now if they exist.
+ ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0);
+
+ return -1;
+ }
+
+ // success
+ return 0;
+}
+
+
+/***********************************************************
+ * Name: ilclient_disable_port_buffers
+ *
+ * Description: disables a port on a given component which has
+ * buffers supplied by the client.
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_disable_port_buffers(COMPONENT_T *comp, int portIndex,
+ OMX_BUFFERHEADERTYPE *bufferList,
+ ILCLIENT_FREE_T ilclient_free,
+ void *private)
+{
+ OMX_ERRORTYPE error;
+ OMX_BUFFERHEADERTYPE *list = bufferList;
+ OMX_BUFFERHEADERTYPE **head, *clist, *prev;
+ OMX_PARAM_PORTDEFINITIONTYPE portdef;
+ int num;
+
+ // get the buffers off the relevant queue
+ memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+ portdef.nVersion.nVersion = OMX_VERSION;
+ portdef.nPortIndex = portIndex;
+
+ // work out buffer requirements, check port is in the right state
+ error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef);
+ if(error != OMX_ErrorNone || portdef.bEnabled != OMX_TRUE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0)
+ return;
+
+ num = portdef.nBufferCountActual;
+
+ error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL);
+ vc_assert(error == OMX_ErrorNone);
+
+ while(num > 0)
+ {
+ VCOS_UNSIGNED set;
+
+ if(list == NULL)
+ {
+ vcos_semaphore_wait(&comp->sema);
+
+ // take buffers for this port off the relevant queue
+ head = portdef.eDir == OMX_DirInput ? &comp->in_list : &comp->out_list;
+ clist = *head;
+ prev = NULL;
+
+ while(clist)
+ {
+ if((portdef.eDir == OMX_DirInput ? clist->nInputPortIndex : clist->nOutputPortIndex) == portIndex)
+ {
+ OMX_BUFFERHEADERTYPE *pBuffer = clist;
+
+ if(!prev)
+ clist = *head = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate;
+ else
+ clist = prev->pAppPrivate = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate;
+
+ pBuffer->pAppPrivate = list;
+ list = pBuffer;
+ }
+ else
+ {
+ prev = clist;
+ clist = (OMX_BUFFERHEADERTYPE *) &(clist->pAppPrivate);
+ }
+ }
+
+ vcos_semaphore_post(&comp->sema);
+ }
+
+ while(list)
+ {
+ void *buf = list->pBuffer;
+ OMX_BUFFERHEADERTYPE *next = list->pAppPrivate;
+
+ error = OMX_FreeBuffer(comp->comp, portIndex, list);
+ vc_assert(error == OMX_ErrorNone);
+
+ if(ilclient_free)
+ ilclient_free(private, buf);
+ else
+ vcos_free(buf);
+
+ num--;
+ list = next;
+ }
+
+ if(num)
+ {
+ OMX_U32 mask = ILCLIENT_PORT_DISABLED | ILCLIENT_EVENT_ERROR;
+ mask |= (portdef.eDir == OMX_DirInput ? ILCLIENT_EMPTY_BUFFER_DONE : ILCLIENT_FILL_BUFFER_DONE);
+
+ // also wait for command complete/error in case we didn't have all the buffers allocated
+ vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, -1, &set);
+
+ if((set & ILCLIENT_EVENT_ERROR) && ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0) >= 0)
+ return;
+
+ if((set & ILCLIENT_PORT_DISABLED) && ilclient_remove_event(comp, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, portIndex, 0) >= 0)
+ return;
+ }
+ }
+
+ if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0)
+ vc_assert(0);
+}
+
+
+/***********************************************************
+ * Name: ilclient_setup_tunnel
+ *
+ * Description: creates a tunnel between components that require that
+ * ports be inititially disabled, then enabled after tunnel setup. If
+ * timeout is non-zero, it will initially wait until a port settings
+ * changes message has been received by the output port. If port
+ * streams are supported by the output port, the requested port stream
+ * will be selected.
+ *
+ * Returns: 0 indicates success, negative indicates failure.
+ * -1: a timeout waiting for the parameter changed
+ * -2: an error was returned instead of parameter changed
+ * -3: no streams are available from this port
+ * -4: requested stream is not available from this port
+ * -5: the data format was not acceptable to the sink
+ ***********************************************************/
+int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout)
+{
+ OMX_ERRORTYPE error;
+ OMX_PARAM_U32TYPE param;
+ OMX_STATETYPE state;
+ int32_t status;
+ int enable_error;
+
+ // source component must at least be idle, not loaded
+ error = OMX_GetState(tunnel->source->comp, &state);
+ vc_assert(error == OMX_ErrorNone);
+ if (state == OMX_StateLoaded && ilclient_change_component_state(tunnel->source, OMX_StateIdle) < 0)
+ return -2;
+
+ // wait for the port parameter changed from the source port
+ if(timeout)
+ {
+ status = ilclient_wait_for_event(tunnel->source, OMX_EventPortSettingsChanged,
+ tunnel->source_port, 0, -1, 1,
+ ILCLIENT_PARAMETER_CHANGED | ILCLIENT_EVENT_ERROR, timeout);
+
+ if (status < 0)
+ {
+ ilclient_debug_output(
+ "ilclient: timed out waiting for port settings changed on port %d", tunnel->source_port);
+ return status;
+ }
+ }
+
+ // disable ports
+ ilclient_disable_tunnel(tunnel);
+
+ // if this source port uses port streams, we need to select one of them before proceeding
+ // if getparameter causes an error that's fine, nothing needs selecting
+ param.nSize = sizeof(OMX_PARAM_U32TYPE);
+ param.nVersion.nVersion = OMX_VERSION;
+ param.nPortIndex = tunnel->source_port;
+ if (OMX_GetParameter(tunnel->source->comp, OMX_IndexParamNumAvailableStreams, &param) == OMX_ErrorNone)
+ {
+ if (param.nU32 == 0)
+ {
+ // no streams available
+ // leave the source port disabled, and return a failure
+ return -3;
+ }
+ if (param.nU32 <= portStream)
+ {
+ // requested stream not available
+ // no streams available
+ // leave the source port disabled, and return a failure
+ return -4;
+ }
+
+ param.nU32 = portStream;
+ error = OMX_SetParameter(tunnel->source->comp, OMX_IndexParamActiveStream, &param);
+ vc_assert(error == OMX_ErrorNone);
+ }
+
+ // now create the tunnel
+ error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, tunnel->sink->comp, tunnel->sink_port);
+
+ enable_error = 0;
+
+ if (error != OMX_ErrorNone || (enable_error=ilclient_enable_tunnel(tunnel)) < 0)
+ {
+ // probably format not compatible
+ error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, NULL, 0);
+ vc_assert(error == OMX_ErrorNone);
+ error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0);
+ vc_assert(error == OMX_ErrorNone);
+
+ if(enable_error)
+ {
+ //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/
+ ilclient_remove_event(tunnel->sink, OMX_EventError, 0, 1, 0, 1);
+ ilclient_remove_event(tunnel->source, OMX_EventError, 0, 1, 0, 1);
+ }
+
+ ilclient_debug_output("ilclient: could not setup/enable tunnel (setup=0x%x,enable=%d)",
+ error, enable_error);
+ return -5;
+ }
+
+ return 0;
+}
+
+/***********************************************************
+ * Name: ilclient_wait_for_event
+ *
+ * Description: waits for a given event to appear on a component event
+ * list. If not immediately present, will wait on that components
+ * event group for the given event flag.
+ *
+ * Returns: 0 indicates success, negative indicates failure.
+ * -1: a timeout was received.
+ * -2: an error event was received.
+ * -3: a config change event was received.
+ ***********************************************************/
+int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event,
+ OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2,
+ int event_flag, int suspend)
+{
+ int32_t status;
+ uint32_t set;
+
+ while (ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) < 0)
+ {
+ // if we want to be notified of errors, check the list for an error now
+ // before blocking, the event flag may have been cleared already.
+ if(event_flag & ILCLIENT_EVENT_ERROR)
+ {
+ ILEVENT_T *cur;
+ ilclient_lock_events(comp->client);
+ cur = comp->list;
+ while(cur && cur->eEvent != OMX_EventError)
+ cur = cur->next;
+
+ if(cur)
+ {
+ // clear error flag
+ vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set);
+ ilclient_unlock_events(comp->client);
+ return -2;
+ }
+
+ ilclient_unlock_events(comp->client);
+ }
+ // check for config change event if we are asked to be notified of that
+ if(event_flag & ILCLIENT_CONFIG_CHANGED)
+ {
+ ILEVENT_T *cur;
+ ilclient_lock_events(comp->client);
+ cur = comp->list;
+ while(cur && cur->eEvent != OMX_EventParamOrConfigChanged)
+ cur = cur->next;
+
+ ilclient_unlock_events(comp->client);
+
+ if(cur)
+ return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3;
+ }
+
+ status = vcos_event_flags_get(&comp->event, event_flag, VCOS_OR_CONSUME,
+ suspend, &set);
+ if (status != 0)
+ return -1;
+ if (set & ILCLIENT_EVENT_ERROR)
+ return -2;
+ if (set & ILCLIENT_CONFIG_CHANGED)
+ return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3;
+ }
+
+ return 0;
+}
+
+
+
+/***********************************************************
+ * Name: ilclient_wait_for_command_complete_dual
+ *
+ * Description: Waits for an event signalling command completion. In
+ * this version we may also return failure if there is an error event
+ * that has terminated a command on a second component.
+ *
+ * Returns: 0 on success, -1 on failure of comp, -2 on failure of other
+ ***********************************************************/
+int ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2, COMPONENT_T *other)
+{
+ OMX_U32 mask = ILCLIENT_EVENT_ERROR;
+ int ret = 0;
+
+ switch(command) {
+ case OMX_CommandStateSet: mask |= ILCLIENT_STATE_CHANGED; break;
+ case OMX_CommandPortDisable: mask |= ILCLIENT_PORT_DISABLED; break;
+ case OMX_CommandPortEnable: mask |= ILCLIENT_PORT_ENABLED; break;
+ default: return -1;
+ }
+
+ if(other)
+ other->related = comp;
+
+ while(1)
+ {
+ ILEVENT_T *cur, *prev = NULL;
+ VCOS_UNSIGNED set;
+
+ ilclient_lock_events(comp->client);
+
+ cur = comp->list;
+ while(cur &&
+ !(cur->eEvent == OMX_EventCmdComplete && cur->nData1 == command && cur->nData2 == nData2) &&
+ !(cur->eEvent == OMX_EventError && cur->nData2 == 1))
+ {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if(cur)
+ {
+ if(prev == NULL)
+ comp->list = cur->next;
+ else
+ prev->next = cur->next;
+
+ // work out whether this was a success or a fail event
+ ret = cur->eEvent == OMX_EventCmdComplete || cur->nData1 == OMX_ErrorSameState ? 0 : -1;
+
+ if(cur->eEvent == OMX_EventError)
+ vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set);
+
+ // add back into spare list
+ cur->next = comp->client->event_list;
+ comp->client->event_list = cur;
+ cur->eEvent = -1; // mark as unused
+
+ ilclient_unlock_events(comp->client);
+ break;
+ }
+ else if(other != NULL)
+ {
+ // check the other component for an error event that terminates a command
+ cur = other->list;
+ while(cur && !(cur->eEvent == OMX_EventError && cur->nData2 == 1))
+ cur = cur->next;
+
+ if(cur)
+ {
+ // we don't remove the event in this case, since the user
+ // can confirm that this event errored by calling wait_for_command on the
+ // other component
+
+ ret = -2;
+ ilclient_unlock_events(comp->client);
+ break;
+ }
+ }
+
+ ilclient_unlock_events(comp->client);
+
+ vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, VCOS_SUSPEND, &set);
+ }
+
+ if(other)
+ other->related = NULL;
+
+ return ret;
+}
+
+
+/***********************************************************
+ * Name: ilclient_wait_for_command_complete
+ *
+ * Description: Waits for an event signalling command completion.
+ *
+ * Returns: 0 on success, -1 on failure.
+ ***********************************************************/
+int ilclient_wait_for_command_complete(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2)
+{
+ return ilclient_wait_for_command_complete_dual(comp, command, nData2, NULL);
+}
+
+/***********************************************************
+ * Name: ilclient_get_output_buffer
+ *
+ * Description: Returns an output buffer returned from a component
+ * using the OMX_FillBufferDone callback from the output list for the
+ * given component and port index.
+ *
+ * Returns: pointer to buffer if available, otherwise NULL
+ ***********************************************************/
+OMX_BUFFERHEADERTYPE *ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block)
+{
+ OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL;
+ VCOS_UNSIGNED set;
+
+ do {
+ vcos_semaphore_wait(&comp->sema);
+ ret = comp->out_list;
+ while(ret != NULL && ret->nOutputPortIndex != portIndex)
+ {
+ prev = ret;
+ ret = ret->pAppPrivate;
+ }
+
+ if(ret)
+ {
+ if(prev == NULL)
+ comp->out_list = ret->pAppPrivate;
+ else
+ prev->pAppPrivate = ret->pAppPrivate;
+
+ ret->pAppPrivate = NULL;
+ }
+ vcos_semaphore_post(&comp->sema);
+
+ if(block && !ret)
+ vcos_event_flags_get(&comp->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set);
+
+ } while(block && !ret);
+
+ return ret;
+}
+
+/***********************************************************
+ * Name: ilclient_get_input_buffer
+ *
+ * Description: Returns an input buffer return from a component using
+ * the OMX_EmptyBufferDone callback from the output list for the given
+ * component and port index.
+ *
+ * Returns: pointer to buffer if available, otherwise NULL
+ ***********************************************************/
+OMX_BUFFERHEADERTYPE *ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block)
+{
+ OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL;
+
+ do {
+ VCOS_UNSIGNED set;
+
+ vcos_semaphore_wait(&comp->sema);
+ ret = comp->in_list;
+ while(ret != NULL && ret->nInputPortIndex != portIndex)
+ {
+ prev = ret;
+ ret = ret->pAppPrivate;
+ }
+
+ if(ret)
+ {
+ if(prev == NULL)
+ comp->in_list = ret->pAppPrivate;
+ else
+ prev->pAppPrivate = ret->pAppPrivate;
+
+ ret->pAppPrivate = NULL;
+ }
+ vcos_semaphore_post(&comp->sema);
+
+ if(block && !ret)
+ vcos_event_flags_get(&comp->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set);
+
+ } while(block && !ret);
+
+ return ret;
+}
+
+/***********************************************************
+ * Name: ilclient_debug_output
+ *
+ * Description: prints a varg message to the log or the debug screen
+ * under win32
+ *
+ * Returns: void
+ ***********************************************************/
+void ilclient_debug_output(char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vcos_vlog_info(format, args);
+ va_end(args);
+}
+
+/******************************************************************************
+Static functions
+******************************************************************************/
+
+/***********************************************************
+ * Name: ilclient_lock_events
+ *
+ * Description: locks the client event structure
+ *
+ * Returns: void
+ ***********************************************************/
+static void ilclient_lock_events(ILCLIENT_T *st)
+{
+ vcos_semaphore_wait(&st->event_sema);
+}
+
+/***********************************************************
+ * Name: ilclient_unlock_events
+ *
+ * Description: unlocks the client event structure
+ *
+ * Returns: void
+ ***********************************************************/
+static void ilclient_unlock_events(ILCLIENT_T *st)
+{
+ vcos_semaphore_post(&st->event_sema);
+}
+
+/***********************************************************
+ * Name: ilclient_event_handler
+ *
+ * Description: event handler passed to core to use as component
+ * callback
+ *
+ * Returns: success
+ ***********************************************************/
+static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData)
+{
+ COMPONENT_T *st = (COMPONENT_T *) pAppData;
+ ILEVENT_T *event;
+ OMX_ERRORTYPE error = OMX_ErrorNone;
+
+ ilclient_lock_events(st->client);
+
+ // go through the events on this component and remove any duplicates in the
+ // existing list, since the client probably doesn't need them. it's better
+ // than asserting when we run out.
+ event = st->list;
+ while(event != NULL)
+ {
+ ILEVENT_T **list = &(event->next);
+ while(*list != NULL)
+ {
+ if((*list)->eEvent == event->eEvent &&
+ (*list)->nData1 == event->nData1 &&
+ (*list)->nData2 == event->nData2)
+ {
+ // remove this duplicate
+ ILEVENT_T *rem = *list;
+ ilclient_debug_output("%s: removing %d/%d/%d", st->name, event->eEvent, event->nData1, event->nData2);
+ *list = rem->next;
+ rem->eEvent = -1;
+ rem->next = st->client->event_list;
+ st->client->event_list = rem;
+ }
+ else
+ list = &((*list)->next);
+ }
+
+ event = event->next;
+ }
+
+ vc_assert(st->client->event_list);
+ event = st->client->event_list;
+
+ switch (eEvent) {
+ case OMX_EventCmdComplete:
+ switch (nData1) {
+ case OMX_CommandStateSet:
+ ilclient_debug_output("%s: callback state changed (%s)", st->name, states[nData2]);
+ vcos_event_flags_set(&st->event, ILCLIENT_STATE_CHANGED, VCOS_OR);
+ break;
+ case OMX_CommandPortDisable:
+ ilclient_debug_output("%s: callback port disable %d", st->name, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_PORT_DISABLED, VCOS_OR);
+ break;
+ case OMX_CommandPortEnable:
+ ilclient_debug_output("%s: callback port enable %d", st->name, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_PORT_ENABLED, VCOS_OR);
+ break;
+ case OMX_CommandFlush:
+ ilclient_debug_output("%s: callback port flush %d", st->name, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_PORT_FLUSH, VCOS_OR);
+ break;
+ case OMX_CommandMarkBuffer:
+ ilclient_debug_output("%s: callback mark buffer %d", st->name, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_MARKED_BUFFER, VCOS_OR);
+ break;
+ default:
+ vc_assert(0);
+ }
+ break;
+ case OMX_EventError:
+ {
+ // check if this component failed a command, and we have to notify another command
+ // of this failure
+ if(nData2 == 1 && st->related != NULL)
+ vcos_event_flags_set(&st->related->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+
+ error = nData1;
+ switch (error) {
+ case OMX_ErrorPortUnpopulated:
+ if (st->error_mask & ILCLIENT_ERROR_UNPOPULATED)
+ {
+ ilclient_debug_output("%s: ignore error: port unpopulated (%d)", st->name, nData2);
+ event = NULL;
+ break;
+ }
+ ilclient_debug_output("%s: port unpopulated %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorSameState:
+ if (st->error_mask & ILCLIENT_ERROR_SAMESTATE)
+ {
+ ilclient_debug_output("%s: ignore error: same state (%d)", st->name, nData2);
+ event = NULL;
+ break;
+ }
+ ilclient_debug_output("%s: same state %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorBadParameter:
+ if (st->error_mask & ILCLIENT_ERROR_BADPARAMETER)
+ {
+ ilclient_debug_output("%s: ignore error: bad parameter (%d)", st->name, nData2);
+ event = NULL;
+ break;
+ }
+ ilclient_debug_output("%s: bad parameter %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorIncorrectStateTransition:
+ ilclient_debug_output("%s: incorrect state transition %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorBadPortIndex:
+ ilclient_debug_output("%s: bad port index %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorStreamCorrupt:
+ ilclient_debug_output("%s: stream corrupt %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorInsufficientResources:
+ ilclient_debug_output("%s: insufficient resources %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorUnsupportedSetting:
+ ilclient_debug_output("%s: unsupported setting %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorOverflow:
+ ilclient_debug_output("%s: overflow %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorDiskFull:
+ ilclient_debug_output("%s: disk full %x (%d)", st->name, error, nData2);
+ //we do not set the error
+ break;
+ case OMX_ErrorMaxFileSize:
+ ilclient_debug_output("%s: max file size %x (%d)", st->name, error, nData2);
+ //we do not set the error
+ break;
+ case OMX_ErrorDrmUnauthorised:
+ ilclient_debug_output("%s: drm file is unauthorised %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorDrmExpired:
+ ilclient_debug_output("%s: drm file has expired %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ case OMX_ErrorDrmGeneral:
+ ilclient_debug_output("%s: drm library error %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ default:
+ vc_assert(0);
+ ilclient_debug_output("%s: unexpected error %x (%d)", st->name, error, nData2);
+ vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR);
+ break;
+ }
+ }
+ break;
+ case OMX_EventBufferFlag:
+ ilclient_debug_output("%s: buffer flag %d/%x", st->name, nData1, nData2);
+ if (nData2 & OMX_BUFFERFLAG_EOS)
+ {
+ vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_FLAG_EOS, VCOS_OR);
+ nData2 = OMX_BUFFERFLAG_EOS;
+ }
+ else
+ vc_assert(0);
+ break;
+ case OMX_EventPortSettingsChanged:
+ ilclient_debug_output("%s: port settings changed %d", st->name, nData1);
+ vcos_event_flags_set(&st->event, ILCLIENT_PARAMETER_CHANGED, VCOS_OR);
+ break;
+ case OMX_EventMark:
+ ilclient_debug_output("%s: buffer mark %p", st->name, pEventData);
+ vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_MARK, VCOS_OR);
+ break;
+ case OMX_EventParamOrConfigChanged:
+ ilclient_debug_output("%s: param/config 0x%X on port %d changed", st->name, nData2, nData1);
+ vcos_event_flags_set(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR);
+ break;
+ default:
+ vc_assert(0);
+ break;
+ }
+
+ if (event)
+ {
+ // fill in details
+ event->eEvent = eEvent;
+ event->nData1 = nData1;
+ event->nData2 = nData2;
+ event->pEventData = pEventData;
+
+ // remove from top of spare list
+ st->client->event_list = st->client->event_list->next;
+
+ // put at head of component event queue
+ event->next = st->list;
+ st->list = event;
+ }
+ ilclient_unlock_events(st->client);
+
+ // now call any callbacks without the event lock so the client can
+ // remove the event in context
+ switch(eEvent) {
+ case OMX_EventError:
+ if(event && st->client->error_callback)
+ st->client->error_callback(st->client->error_callback_data, st, error);
+ break;
+ case OMX_EventBufferFlag:
+ if ((nData2 & OMX_BUFFERFLAG_EOS) && st->client->eos_callback)
+ st->client->eos_callback(st->client->eos_callback_data, st, nData1);
+ break;
+ case OMX_EventPortSettingsChanged:
+ if (st->client->port_settings_callback)
+ st->client->port_settings_callback(st->client->port_settings_callback_data, st, nData1);
+ break;
+ case OMX_EventParamOrConfigChanged:
+ if (st->client->configchanged_callback)
+ st->client->configchanged_callback(st->client->configchanged_callback_data, st, nData2);
+ break;
+ default:
+ // ignore
+ break;
+ }
+
+ return OMX_ErrorNone;
+}
+
+/***********************************************************
+ * Name: ilclient_empty_buffer_done
+ *
+ * Description: passed to core to use as component callback, puts
+ * buffer on list
+ *
+ * Returns:
+ ***********************************************************/
+static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
+{
+ COMPONENT_T *st = (COMPONENT_T *) pAppData;
+ OMX_BUFFERHEADERTYPE *list;
+
+ ilclient_debug_output("%s: empty buffer done %p", st->name, pBuffer);
+
+ vcos_semaphore_wait(&st->sema);
+ // insert at end of the list, so we process buffers in
+ // the same order
+ list = st->in_list;
+ while(list && list->pAppPrivate)
+ list = list->pAppPrivate;
+
+ if(!list)
+ st->in_list = pBuffer;
+ else
+ list->pAppPrivate = pBuffer;
+
+ pBuffer->pAppPrivate = NULL;
+ vcos_semaphore_post(&st->sema);
+
+ vcos_event_flags_set(&st->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR);
+
+ if (st->client->empty_buffer_done_callback)
+ st->client->empty_buffer_done_callback(st->client->empty_buffer_done_callback_data, st);
+
+ return OMX_ErrorNone;
+}
+
+/***********************************************************
+ * Name: ilclient_empty_buffer_done_error
+ *
+ * Description: passed to core to use as component callback, asserts
+ * on use as client not expecting component to use this callback.
+ *
+ * Returns:
+ ***********************************************************/
+static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
+{
+ vc_assert(0);
+ return OMX_ErrorNone;
+}
+
+/***********************************************************
+ * Name: ilclient_fill_buffer_done
+ *
+ * Description: passed to core to use as component callback, puts
+ * buffer on list
+ *
+ * Returns:
+ ***********************************************************/
+static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent,
+ OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
+{
+ COMPONENT_T *st = (COMPONENT_T *) pAppData;
+ OMX_BUFFERHEADERTYPE *list;
+
+ ilclient_debug_output("%s: fill buffer done %p", st->name, pBuffer);
+
+ vcos_semaphore_wait(&st->sema);
+ // insert at end of the list, so we process buffers in
+ // the correct order
+ list = st->out_list;
+ while(list && list->pAppPrivate)
+ list = list->pAppPrivate;
+
+ if(!list)
+ st->out_list = pBuffer;
+ else
+ list->pAppPrivate = pBuffer;
+
+ pBuffer->pAppPrivate = NULL;
+ vcos_semaphore_post(&st->sema);
+
+ vcos_event_flags_set(&st->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR);
+
+ if (st->client->fill_buffer_done_callback)
+ st->client->fill_buffer_done_callback(st->client->fill_buffer_done_callback_data, st);
+
+ return OMX_ErrorNone;
+}
+
+/***********************************************************
+ * Name: ilclient_fill_buffer_done_error
+ *
+ * Description: passed to core to use as component callback, asserts
+ * on use as client not expecting component to use this callback.
+ *
+ * Returns:
+ ***********************************************************/
+static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent,
+ OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
+{
+ vc_assert(0);
+ return OMX_ErrorNone;
+}
+
+
+
+OMX_HANDLETYPE ilclient_get_handle(COMPONENT_T *comp)
+{
+ vcos_assert(comp);
+ return comp->comp;
+}
+
+
+static struct {
+ OMX_PORTDOMAINTYPE dom;
+ int param;
+} port_types[] = {
+ { OMX_PortDomainVideo, OMX_IndexParamVideoInit },
+ { OMX_PortDomainAudio, OMX_IndexParamAudioInit },
+ { OMX_PortDomainImage, OMX_IndexParamImageInit },
+ { OMX_PortDomainOther, OMX_IndexParamOtherInit },
+};
+
+int ilclient_get_port_index(COMPONENT_T *comp, OMX_DIRTYPE dir, OMX_PORTDOMAINTYPE type, int index)
+{
+ uint32_t i;
+ // for each possible port type...
+ for (i=0; i<sizeof(port_types)/sizeof(port_types[0]); i++)
+ {
+ if ((port_types[i].dom == type) || (type == (OMX_PORTDOMAINTYPE) -1))
+ {
+ OMX_PORT_PARAM_TYPE param;
+ OMX_ERRORTYPE error;
+ uint32_t j;
+
+ param.nSize = sizeof(param);
+ param.nVersion.nVersion = OMX_VERSION;
+ error = OMX_GetParameter(ILC_GET_HANDLE(comp), port_types[i].param, &param);
+ assert(error == OMX_ErrorNone);
+
+ // for each port of this type...
+ for (j=0; j<param.nPorts; j++)
+ {
+ int port = param.nStartPortNumber+j;
+
+ OMX_PARAM_PORTDEFINITIONTYPE portdef;
+ portdef.nSize = sizeof(portdef);
+ portdef.nVersion.nVersion = OMX_VERSION;
+ portdef.nPortIndex = port;
+
+ error = OMX_GetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamPortDefinition, &portdef);
+ assert(error == OMX_ErrorNone);
+
+ if (portdef.eDir == dir)
+ {
+ if (index-- == 0)
+ return port;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+int ilclient_suggest_bufsize(COMPONENT_T *comp, OMX_U32 nBufSizeHint)
+{
+ OMX_PARAM_BRCMOUTPUTBUFFERSIZETYPE param;
+ OMX_ERRORTYPE error;
+
+ param.nSize = sizeof(param);
+ param.nVersion.nVersion = OMX_VERSION;
+ param.nBufferSize = nBufSizeHint;
+ error = OMX_SetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamBrcmOutputBufferSize,
+ &param);
+ assert(error == OMX_ErrorNone);
+
+ return (error == OMX_ErrorNone) ? 0 : -1;
+}
+
+unsigned int ilclient_stack_size(void)
+{
+ return ILCLIENT_THREAD_DEFAULT_STACK_SIZE;
+}
+
diff --git a/ilclient/ilclient.h b/ilclient/ilclient.h
new file mode 100644
index 0000000..8478f15
--- /dev/null
+++ b/ilclient/ilclient.h
@@ -0,0 +1,1039 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * \file
+ *
+ * \brief This API defines helper functions for writing IL clients.
+ *
+ * This file defines an IL client side library. This is useful when
+ * writing IL clients, since there tends to be much repeated and
+ * common code across both single and multiple clients. This library
+ * seeks to remove that common code and abstract some of the
+ * interactions with components. There is a wrapper around a
+ * component and tunnel, and some operations can be done on lists of
+ * these. The callbacks from components are handled, and specific
+ * events can be checked or waited for.
+ */
+
+#ifndef _IL_CLIENT_H
+#define _IL_CLIENT_H
+
+#include "IL/OMX_Broadcom.h"
+#include "interface/vcos/vcos.h"
+
+/**
+ * The <DFN>ILCLIENT_T</DFN> structure encapsulates the state needed for the IL
+ * Client API. It contains a set of callback functions used to
+ * communicate with the user. It also includes a linked list of free
+ * event structures.
+ ***********************************************************/
+typedef struct _ILCLIENT_T ILCLIENT_T;
+
+
+/**
+ * Each <DFN>ILEVENT_T</DFN> structure stores the result of an <DFN>EventHandler</DFN>
+ * callback from a component, storing the event message type and any
+ * parameters returned.
+ ***********************************************************/
+typedef struct _ILEVENT_T ILEVENT_T;
+
+
+
+struct _COMPONENT_T;
+
+/**
+ * The <DFN>COMPONENT_T</DFN> structure represents an IL component,
+ * together with the necessary extra information required by the IL
+ * Client API. This structure stores the handle to the OMX component,
+ * as well as the event list containing all events sent by this
+ * component. The component state structure also holds a pair of
+ * buffer queues, for input and output buffers returned to the client
+ * by the <DFN>FillBufferDone</DFN> and <DFN>EmptyBufferDone</DFN>
+ * callbacks. As some operations result in error callbacks that can
+ * be ignored, an error mask is maintained to allow some errors to be
+ * ignored. A pointer to the client state structure is also added.
+ ***********************************************************/
+typedef struct _COMPONENT_T COMPONENT_T;
+
+
+/**
+ * The generic callback function is used for communicating events from
+ * a particular component to the user.
+ *
+ * @param userdata The data returned from when the callback was registered.
+ *
+ * @param comp The component structure representing the component that
+ * originated this event.
+ *
+ * @param data The relevant data field from the event.
+ *
+ * @return Void.
+ ***********************************************************/
+typedef void (*ILCLIENT_CALLBACK_T)(void *userdata, COMPONENT_T *comp, OMX_U32 data);
+
+
+/**
+ * The buffer callback function is used for indicating that a
+ * component has returned a buffer on a port using client buffer
+ * communication.
+ *
+ * @param data The data returned from when the callback was registered.
+ *
+ * @param comp The component from which the buffer originated.
+ *
+ * @return Void.
+ ***********************************************************/
+typedef void (*ILCLIENT_BUFFER_CALLBACK_T)(void *data, COMPONENT_T *comp);
+
+
+/**
+ * The malloc function is passed into
+ * <DFN>ilclient_enable_port_buffers()</DFN> and used for allocating the
+ * buffer payload.
+ *
+ * @param userdata Private pointer passed into
+ * <DFN>ilclient_enable_port_buffers()</DFN> call.
+ *
+ * @param size Size in bytes of the requested memory region.
+ *
+ * @param align Alignment requirement in bytes for the base memory address.
+ *
+ * @param description Text description of the memory being allocated.
+ *
+ * @return The memory address on success, <DFN>NULL</DFN> on failure.
+ ***********************************************************/
+typedef void *(*ILCLIENT_MALLOC_T)(void *userdata, VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description);
+
+
+/**
+ * The free function is passed into
+ * <DFN>ilclient_enable_port_buffers()</DFN> and
+ * <DFN>ilclient_disable_port_buffers()</DFN> and used for freeing the
+ * buffer payload.
+ *
+ * @param userdata Private pointer passed into
+ * <DFN>ilclient_enable_port_buffers()</DFN> and
+ * <DFN>ilclient_disable_port_buffers()</DFN>.
+ *
+ * @param pointer Memory address to free, that was previously returned
+ * from <DFN>ILCLIENT_MALLOC_T</DFN> function.
+ *
+ * @return Void.
+ ***********************************************************/
+typedef void (*ILCLIENT_FREE_T)(void *userdata, void *pointer);
+
+
+/**
+ * The event mask enumeration describes the possible events that the
+ * user can ask to wait for when waiting for a particular event.
+ ***********************************************************/
+typedef enum {
+ ILCLIENT_EMPTY_BUFFER_DONE = 0x1, /**< Set when a buffer is
+ returned from an input
+ port */
+
+ ILCLIENT_FILL_BUFFER_DONE = 0x2, /**< Set when a buffer is
+ returned from an output
+ port */
+
+ ILCLIENT_PORT_DISABLED = 0x4, /**< Set when a port indicates
+ it has completed a disable
+ command. */
+
+ ILCLIENT_PORT_ENABLED = 0x8, /**< Set when a port indicates
+ is has completed an enable
+ command. */
+
+ ILCLIENT_STATE_CHANGED = 0x10, /**< Set when a component
+ indicates it has completed
+ a state change command. */
+
+ ILCLIENT_BUFFER_FLAG_EOS = 0x20, /**< Set when a port signals
+ an EOS event. */
+
+ ILCLIENT_PARAMETER_CHANGED = 0x40, /**< Set when a port signals a
+ port settings changed
+ event. */
+
+ ILCLIENT_EVENT_ERROR = 0x80, /**< Set when a component
+ indicates an error. */
+
+ ILCLIENT_PORT_FLUSH = 0x100, /**< Set when a port indicates
+ is has completed a flush
+ command. */
+
+ ILCLIENT_MARKED_BUFFER = 0x200, /**< Set when a port indicates
+ it has marked a buffer. */
+
+ ILCLIENT_BUFFER_MARK = 0x400, /**< Set when a port indicates
+ it has received a buffer
+ mark. */
+
+ ILCLIENT_CONFIG_CHANGED = 0x800 /**< Set when a config parameter
+ changed. */
+} ILEVENT_MASK_T;
+
+
+/**
+ * On component creation the user can set flags to control the
+ * creation of that component.
+ ***********************************************************/
+typedef enum {
+ ILCLIENT_FLAGS_NONE = 0x0, /**< Used if no flags are
+ set. */
+
+ ILCLIENT_ENABLE_INPUT_BUFFERS = 0x1, /**< If set we allow the
+ client to communicate with
+ input ports via buffer
+ communication, rather than
+ tunneling with another
+ component. */
+
+ ILCLIENT_ENABLE_OUTPUT_BUFFERS = 0x2, /**< If set we allow the
+ client to communicate with
+ output ports via buffer
+ communication, rather than
+ tunneling with another
+ component. */
+
+ ILCLIENT_DISABLE_ALL_PORTS = 0x4, /**< If set we disable all
+ ports on creation. */
+
+ ILCLIENT_HOST_COMPONENT = 0x8, /**< Create a host component.
+ The default host ilcore
+ only can create host components
+ by being locally hosted
+ so should only be used for testing
+ purposes. */
+
+ ILCLIENT_OUTPUT_ZERO_BUFFERS = 0x10 /**< All output ports will have
+ nBufferCountActual set to zero,
+ if supported by the component. */
+} ILCLIENT_CREATE_FLAGS_T;
+
+
+/**
+ * \brief This structure represents a tunnel in the OpenMAX IL API.
+ *
+ * Some operations in this API act on a tunnel, so the tunnel state
+ * structure (<DFN>TUNNEL_T</DFN>) is a convenient store of the source and sink
+ * of the tunnel. For each, a pointer to the relevant component state
+ * structure and the port index is stored.
+ ***********************************************************/
+typedef struct {
+ COMPONENT_T *source; /**< The source component */
+ int source_port; /**< The output port index on the source component */
+ COMPONENT_T *sink; /**< The sink component */
+ int sink_port; /**< The input port index on the sink component */
+} TUNNEL_T;
+
+
+/**
+ * The <DFN>set_tunnel</DFN> macro is a useful function that initialises a
+ * <DFN>TUNNEL_T</DFN> structure.
+ ***********************************************************/
+#define set_tunnel(t,a,b,c,d) do {TUNNEL_T *_ilct = (t); \
+ _ilct->source = (a); _ilct->source_port = (b); \
+ _ilct->sink = (c); _ilct->sink_port = (d);} while(0)
+
+/**
+ * For calling OpenMAX IL methods directory, we need to access the
+ * <DFN>OMX_HANDLETYPE</DFN> corresponding to the <DFN>COMPONENT_T</DFN> structure. This
+ * macro enables this while keeping the <DFN>COMPONENT_T</DFN> structure opaque.
+ * The parameter <DFN>x</DFN> should be of the type <DFN>*COMPONENT_T</DFN>.
+ ***********************************************************/
+#define ILC_GET_HANDLE(x) ilclient_get_handle(x)
+
+/**
+ * An IL Client structure is created by the <DFN>ilclient_init()</DFN>
+ * method. This structure is used when creating components, but
+ * otherwise is not needed in other API functions as a pointer to this
+ * structure is maintained in the <DFN>COMPONENT_T</DFN> structure.
+ *
+ * @return pointer to client structure
+ ***********************************************************/
+VCHPRE_ ILCLIENT_T VCHPOST_ *ilclient_init(void);
+
+/**
+ * When all components have been deleted, the IL Client structure can
+ * be destroyed by calling the <DFN>ilclient_destroy()</DFN> function.
+ *
+ * @param handle The client handle. After calling this function, this
+ * handle should not be used.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_destroy(ILCLIENT_T *handle);
+
+/**
+ * The <DFN>ilclient_set_port_settings_callback()</DFN> function registers a
+ * callback to be used when the <DFN>OMX_EventPortSettingsChanged</DFN> event is
+ * received. When the event is received, a pointer to the component
+ * structure and port index is returned by the callback.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_port_settings_callback(ILCLIENT_T *handle,
+ ILCLIENT_CALLBACK_T func,
+ void *userdata);
+
+/**
+ * The <DFN>ilclient_set_eos_callback()</DFN> function registers a callback to be
+ * used when the <DFN>OMX_EventBufferFlag</DFN> is received with the
+ * <DFN>OMX_BUFFERFLAG_EOS</DFN> flag set. When the event is received, a pointer
+ * to the component structure and port index is returned by the
+ * callback.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_eos_callback(ILCLIENT_T *handle,
+ ILCLIENT_CALLBACK_T func,
+ void *userdata);
+
+/**
+ * The <DFN>ilclient_set_error_callback()</DFN> function registers a callback to be
+ * used when the <DFN>OMX_EventError</DFN> is received from a component. When
+ * the event is received, a pointer to the component structure and the
+ * error code are reported by the callback.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_error_callback(ILCLIENT_T *handle,
+ ILCLIENT_CALLBACK_T func,
+ void *userdata);
+
+/**
+ * The <DFN>ilclient_set_configchanged_callback()</DFN> function
+ * registers a callback to be used when an
+ * <DFN>OMX_EventParamOrConfigChanged</DFN> event occurs. When the
+ * event is received, a pointer to the component structure and the
+ * index value that has changed are reported by the callback. The
+ * user may then use an <DFN>OMX_GetConfig</DFN> call with the index
+ * as specified to retrieve the updated information.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_configchanged_callback(ILCLIENT_T *handle,
+ ILCLIENT_CALLBACK_T func,
+ void *userdata);
+
+
+/**
+ * The <DFN>ilclient_set_fill_buffer_done_callback()</DFN> function registers a
+ * callback to be used when a buffer passed to an output port using the
+ * <DFN>OMX_FillBuffer</DFN> call is returned with the <DFN>OMX_FillBufferDone</DFN>
+ * callback. When the event is received, a pointer to the component
+ * structure is returned by the callback. The user may then use the
+ * <DFN>ilclient_get_output_buffer()</DFN> function to retrieve the buffer.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_fill_buffer_done_callback(ILCLIENT_T *handle,
+ ILCLIENT_BUFFER_CALLBACK_T func,
+ void *userdata);
+
+/**
+ * The <DFN>ilclient_set_empty_buffer_done_callback()</DFN> function registers a
+ * callback to be used when a buffer passed to an input port using the
+ * <DFN>OMX_EmptyBuffer</DFN> call is returned with the <DFN>OMX_EmptyBufferDone</DFN>
+ * callback. When the event is received, a pointer to the component
+ * structure is returned by the callback. The user may then use the
+ * <DFN>ilclient_get_input_buffer()</DFN> function to retrieve the buffer.
+ *
+ * @param handle The client handle
+ *
+ * @param func The callback function to use. Calling this function
+ * with a <DFN>NULL</DFN> function pointer will deregister any existing
+ * registered callback.
+ *
+ * @param userdata Data to be passed back when calling the callback
+ * function.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_set_empty_buffer_done_callback(ILCLIENT_T *handle,
+ ILCLIENT_BUFFER_CALLBACK_T func,
+ void *userdata);
+
+
+/**
+ * Components are created using the <DFN>ilclient_create_component()</DFN>
+ * function.
+ *
+ * @param handle The client handle
+ *
+ * @param comp On successful creation, the component structure pointer
+ * will be written back into <DFN>comp</DFN>.
+ *
+ * @param name The name of the component to be created. Component
+ * names as provided are automatically prefixed with
+ * <DFN>"OMX.broadcom."</DFN> before passing to the IL core. The name
+ * provided will also be used in debugging messages added about this
+ * component.
+ *
+ * @param flags The client can specify some creation behaviour by using
+ * the <DFN>flags</DFN> field. The meaning of each flag is defined
+ * by the <DFN>ILCLIENT_CREATE_FLAGS_T</DFN> type.
+ *
+ * @return 0 on success, -1 on failure
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_create_component(ILCLIENT_T *handle,
+ COMPONENT_T **comp,
+ char *name,
+ ILCLIENT_CREATE_FLAGS_T flags);
+
+/**
+ * The <DFN>ilclient_cleanup_components()</DFN> function deallocates all
+ * state associated with components and frees the OpenMAX component
+ * handles. All tunnels connecting components should have been torn
+ * down explicitly, and all components must be in loaded state.
+ *
+ * @param list A null-terminated list of component pointers to be
+ * deallocated.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_cleanup_components(COMPONENT_T *list[]);
+
+
+/**
+ * The <DFN>ilclient_change_component_state()</DFN> function changes the
+ * state of an individual component. This will trigger the state
+ * change, and also wait for that state change to be completed. It
+ * should not be called if this state change has dependencies on other
+ * components also changing states. Trying to change a component to
+ * its current state is treated as success.
+ *
+ * @param comp The component to change.
+ *
+ * @param state The new state to transition to.
+ *
+ * @return 0 on success, -1 on failure.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_change_component_state(COMPONENT_T *comp,
+ OMX_STATETYPE state);
+
+
+/**
+ * The <DFN>ilclient_state_transition()</DFN> function transitions a set of
+ * components that need to perform a simultaneous state transition;
+ * for example, when two components are tunnelled and the buffer
+ * supplier port needs to allocate and pass buffers to a non-supplier
+ * port. All components are sent a command to change state, then the
+ * function will wait for all components to signal that they have
+ * changed state.
+ *
+ * @param list A null-terminated list of component pointers.
+ *
+ * @param state The new state to which to transition all components.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_state_transition(COMPONENT_T *list[],
+ OMX_STATETYPE state);
+
+
+/**
+ * The <DFN>ilclient_disable_port()</DFN> function disables a port on a
+ * given component. This function sends the disable port message to
+ * the component and waits for the component to signal that this has
+ * taken place. If the port is already disabled, this is treated as a
+ * sucess.
+ *
+ * @param comp The component containing the port to disable.
+ *
+ * @param portIndex The port index of the port to disable. This must
+ * be a named port index, rather than a <DFN>OMX_ALL</DFN> value.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_disable_port(COMPONENT_T *comp,
+ int portIndex);
+
+
+/**
+ * The <DFN>ilclient_enable_port()</DFN> function enables a port on a
+ * given component. This function sends the enable port message to
+ * the component and waits for the component to signal that this has
+ * taken place. If the port is already disabled, this is treated as a
+ * sucess.
+ *
+ * @param comp The component containing the port to enable.
+ *
+ * @param portIndex The port index of the port to enable. This must
+ * be a named port index, rather than a <DFN>OMX_ALL</DFN> value.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_enable_port(COMPONENT_T *comp,
+ int portIndex);
+
+
+
+/**
+ * The <DFN>ilclient_enable_port_buffers()</DFN> function enables a port
+ * in base profile mode on a given component. The port is not
+ * tunneled, so requires buffers to be allocated.
+ *
+ * @param comp The component containing the port to enable.
+ *
+ * @param portIndex The port index of the port to enable. This must
+ * be a named port index, rather than a <DFN>OMX_ALL</DFN> value.
+ *
+ * @param ilclient_malloc This function will be used to allocate
+ * buffer payloads. If <DFN>NULL</DFN> then
+ * <DFN>vcos_malloc_aligned</DFN> will be used.
+ *
+ * @param ilclient_free If an error occurs, this function is used to
+ * free previously allocated payloads. If <DFN>NULL</DFN> then
+ * <DFN>vcos_free</DFN> will be used.
+ *
+ * @param userdata The first argument to calls to
+ * <DFN>ilclient_malloc</DFN> and <DFN>ilclient_free</DFN>, if these
+ * arguments are not <DFN>NULL</DFN>.
+ *
+ * @return 0 indicates success. -1 indicates failure.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_enable_port_buffers(COMPONENT_T *comp,
+ int portIndex,
+ ILCLIENT_MALLOC_T ilclient_malloc,
+ ILCLIENT_FREE_T ilclient_free,
+ void *userdata);
+
+
+/**
+ * The <DFN>ilclient_disable_port_buffers()</DFN> function disables a
+ * port in base profile mode on a given component. The port is not
+ * tunneled, and has been supplied with buffers by the client.
+ *
+ * @param comp The component containing the port to disable.
+ *
+ * @param portIndex The port index of the port to disable. This must
+ * be a named port index, rather than a <DFN>OMX_ALL</DFN> value.
+ *
+ * @param bufferList A list of buffers, using <DFN>pAppPrivate</DFN>
+ * as the next pointer that were allocated on this port, and currently
+ * held by the application. If buffers on this port are held by IL
+ * client or the component then these are automatically freed.
+ *
+ * @param ilclient_free This function is used to free the buffer payloads.
+ * If <DFN>NULL</DFN> then <DFN>vcos_free</DFN> will be used.
+ *
+ * @param userdata The first argument to calls to
+ * <DFN>ilclient_free</DFN>.
+ *
+ * @return void
+ */
+VCHPRE_ void VCHPOST_ ilclient_disable_port_buffers(COMPONENT_T *comp,
+ int portIndex,
+ OMX_BUFFERHEADERTYPE *bufferList,
+ ILCLIENT_FREE_T ilclient_free,
+ void *userdata);
+
+
+/**
+ * With a populated tunnel structure, the
+ * <DFN>ilclient_setup_tunnel()</DFN> function connects the tunnel. It
+ * first transitions the source component to idle if currently in
+ * loaded state, and then optionally checks the source event list for
+ * a port settings changed event from the source port. If this event
+ * is not in the event queue then this function optionally waits for
+ * it to arrive. To disable this check for the port settings changed
+ * event, set <DFN>timeout</DFN> to zero.
+ *
+ * Both ports are then disabled, and the source port is inspected for
+ * a port streams parameter. If this is supported, then the
+ * <DFN>portStream</DFN> argument is used to select which port stream
+ * to use. The two ports are then tunnelled using the
+ * <DFN>OMX_SetupTunnel</DFN> function. If this is successful, then
+ * both ports are enabled. Note that for disabling and enabling the
+ * tunnelled ports, the functions <DFN>ilclient_disable_tunnel()</DFN>
+ * and <DFN>ilclient_enable_tunnel()</DFN> are used, so the relevant
+ * documentation for those functions applies here.
+ *
+ * @param tunnel The tunnel structure representing the tunnel to
+ * set up.
+ *
+ * @param portStream If port streams are supported on the output port
+ * of the tunnel, then this parameter indicates the port stream to
+ * select on this port.
+ *
+ * @param timeout The time duration in milliseconds to wait for the
+ * output port to signal a port settings changed event before
+ * returning a timeout failure. If this is 0, then we do not check
+ * for a port settings changed before setting up the tunnel.
+ *
+ * @return 0 indicates success, negative indicates failure:
+ * - -1: a timeout waiting for the parameter changed
+ * - -2: an error was returned instead of parameter changed
+ * - -3: no streams are available from this port
+ * - -4: requested stream is not available from this port
+ * - -5: the data format was not acceptable to the sink
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_setup_tunnel(TUNNEL_T *tunnel,
+ unsigned int portStream,
+ int timeout);
+
+
+/**
+ * The <DFN>ilclient_disable_tunnel()</DFN> function disables both ports listed in
+ * the tunnel structure. It will send a port disable command to each
+ * port, then waits for both to indicate they have completed the
+ * transition. The errors <DFN>OMX_ErrorPortUnpopulated</DFN> and
+ * <DFN>OMX_ErrorSameState</DFN> are both ignored by this function; the former
+ * since the first port to disable may deallocate buffers before the
+ * second port has been disabled, leading to the second port reporting
+ * the unpopulated error.
+ *
+ * @param tunnel The tunnel to disable.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_disable_tunnel(TUNNEL_T *tunnel);
+
+
+/**
+ * The <DFN>ilclient_enable_tunnel()</DFN> function enables both ports listed in
+ * the tunnel structure. It will first send a port enable command to
+ * each port. It then checks whether the sink component is not in
+ * loaded state - if so, the function waits for both ports to complete
+ * the requested port enable. If the sink component was in loaded
+ * state, then this component is transitioned to idle to allow the
+ * ports to exchange buffers and enable the ports. This is since
+ * typically this function is used when creating a tunnel between two
+ * components, where the source component is processing data to enable
+ * it to report the port settings changed event, and the sink port has
+ * yet to be used. Before transitioning the sink component to idle,
+ * this function waits for the sink port to be enabled - since the
+ * component is in loaded state, this will happen quickly. If the
+ * transition to idle fails, the sink component is transitioned back
+ * to loaded and the source port disabled. If the transition
+ * succeeds, the function then waits for the source port to complete
+ * the requested port enable.
+ *
+ * @param tunnel The tunnel to enable.
+ *
+ * @return 0 on success, -1 on failure.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_enable_tunnel(TUNNEL_T *tunnel);
+
+
+/**
+ * The <DFN>ilclient_flush_tunnels()</DFN> function will flush a number of tunnels
+ * from the list of tunnels presented. For each tunnel that is to be
+ * flushed, both source and sink ports are sent a flush command. The
+ * function then waits for both ports to report they have completed
+ * the flush operation.
+ *
+ * @param tunnel List of tunnels. The list must be terminated with a
+ * tunnel structure with <DFN>NULL</DFN> component entries.
+ *
+ * @param max The maximum number of tunnels to flush from the list.
+ * A value of 0 indicates that all tunnels in the list are flushed.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_flush_tunnels(TUNNEL_T *tunnel,
+ int max);
+
+
+/**
+ * The <DFN>ilclient_teardown_tunnels()</DFN> function tears down all tunnels in
+ * the list of tunnels presented. For each tunnel in the list, the
+ * <DFN>OMX_SetupTunnel</DFN> is called on the source port and on the sink port,
+ * where for both calls the destination component is <DFN>NULL</DFN> and the
+ * destination port is zero. The VMCSX IL implementation requires
+ * that all tunnels are torn down in this manner before components are
+ * freed.
+ *
+ * @param tunnels List of tunnels to teardown. The list must be
+ * terminated with a tunnel structure with <DFN>NULL</DFN> component entries.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_teardown_tunnels(TUNNEL_T *tunnels);
+
+
+/**
+ * The <DFN>ilclient_get_output_buffer()</DFN> function returns a buffer
+ * that was sent to an output port and that has been returned from a
+ * component using the <DFN>OMX_FillBufferDone</DFN> callback.
+ *
+ * @param comp The component that returned the buffer.
+ *
+ * @param portIndex The port index on the component that the buffer
+ * was returned from.
+ *
+ * @param block If non-zero, the function will block until a buffer is available.
+ *
+ * @return Pointer to buffer if available, otherwise <DFN>NULL</DFN>.
+ ***********************************************************/
+VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_output_buffer(COMPONENT_T *comp,
+ int portIndex,
+ int block);
+
+
+/**
+ * The <DFN>ilclient_get_input_buffer()</DFN> function returns a buffer
+ * that was sent to an input port and that has been returned from a
+ * component using the <DFN>OMX_EmptyBufferDone</DFN> callback.
+ *
+ * @param comp The component that returned the buffer.
+ *
+ * @param portIndex The port index on the component from which the buffer
+ * was returned.
+ *
+ * @param block If non-zero, the function will block until a buffer is available.
+ *
+ * @return pointer to buffer if available, otherwise <DFN>NULL</DFN>
+ ***********************************************************/
+VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_input_buffer(COMPONENT_T *comp,
+ int portIndex,
+ int block);
+
+
+/**
+ * The <DFN>ilclient_remove_event()</DFN> function queries the event list for the
+ * given component, matching against the given criteria. If a matching
+ * event is found, it is removed and added to the free event list.
+ *
+ * @param comp The component that returned the matching event.
+ *
+ * @param event The event type of the matching event.
+ *
+ * @param nData1 The <DFN>nData1</DFN> field of the matching event.
+ *
+ * @param ignore1 Whether to ignore the <DFN>nData1</DFN> field when finding a
+ * matching event. A value of 0 indicates that <DFN>nData1</DFN> must match, a
+ * value of 1 indicates that <DFN>nData1</DFN> does not have to match.
+ *
+ * @param nData2 The <DFN>nData2</DFN> field of the matching event.
+ *
+ * @param ignore2 Whether to ignore the <DFN>nData2</DFN> field when finding a
+ * matching event. A value of 0 indicates that <DFN>nData2</DFN> must match, a
+ * value of 1 indicates that <DFN>nData2</DFN> does not have to match.
+ *
+ * @return 0 if the event was removed. -1 if no matching event was
+ * found.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_remove_event(COMPONENT_T *comp,
+ OMX_EVENTTYPE event,
+ OMX_U32 nData1,
+ int ignore1,
+ OMX_U32 nData2,
+ int ignore2);
+
+
+/**
+ * The <DFN>ilclient_return_events()</DFN> function removes all events
+ * from a component event list and adds them to the IL client free
+ * event list. This function is automatically called when components
+ * are freed.
+ *
+ * @param comp The component from which all events should be moved to
+ * the free list.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_return_events(COMPONENT_T *comp);
+
+
+/**
+ * The <DFN>ilclient_wait_for_event()</DFN> function is similar to
+ * <DFN>ilclient_remove_event()</DFN>, but allows the caller to block until that
+ * event arrives.
+ *
+ * @param comp The component that returned the matching event.
+ *
+ * @param event The event type of the matching event.
+ *
+ * @param nData1 The <DFN>nData1</DFN> field of the matching event.
+ *
+ * @param ignore1 Whether to ignore the <DFN>nData1</DFN> field when finding a
+ * matching event. A value of 0 indicates that <DFN>nData1</DFN> must match, a
+ * value of 1 indicates that <DFN>nData1</DFN> does not have to match.
+ *
+ * @param nData2 The <DFN>nData2</DFN> field of the matching event.
+ *
+ * @param ignore2 Whether to ignore the <DFN>nData2</DFN> field when finding a
+ * matching event. A value of 0 indicates that <DFN>nData2</DFN> must match, a
+ * value of 1 indicates that <DFN>nData2</DFN> does not have to match.
+ *
+ * @param event_flag Specifies a bitfield of IL client events to wait
+ * for, given in <DFN>ILEVENT_MASK_T</DFN>. If any of these events
+ * are signalled by the component, the event list is then re-checked
+ * for a matching event. If the <DFN>ILCLIENT_EVENT_ERROR</DFN> bit
+ * is included, and an error is signalled by the component, then the
+ * function will return an error code. If the
+ * <DFN>ILCLIENT_CONFIG_CHANGED</DFN> bit is included, and this bit is
+ * signalled by the component, then the function will return an error
+ * code.
+ *
+ * @param timeout Specifies how long to block for in milliseconds
+ * before returning a failure.
+ *
+ * @return 0 indicates success, a matching event has been removed from
+ * the component's event queue. A negative return indicates failure,
+ * in this case no events have been removed from the component's event
+ * queue.
+ * - -1: a timeout was received.
+ * - -2: an error event was received.
+ * - -3: a config changed event was received.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_wait_for_event(COMPONENT_T *comp,
+ OMX_EVENTTYPE event,
+ OMX_U32 nData1,
+ int ignore1,
+ OMX_U32 nData2,
+ int ignore2,
+ int event_flag,
+ int timeout);
+
+
+/**
+ * The <DFN>ilclient_wait_for_command_complete()</DFN> function waits
+ * for a message from a component that indicates that the command
+ * has completed. This is either a command success message, or an
+ * error message that signals the completion of an event.
+ *
+ * @param comp The component currently processing a command.
+ *
+ * @param command The command being processed. This must be either
+ * <DFN>OMX_CommandStateSet</DFN>, <DFN>OMX_CommandPortDisable</DFN>
+ * or <DFN>OMX_CommandPortEnable</DFN>.
+ *
+ * @param nData2 The expected value of <DFN>nData2</DFN> in the
+ * command complete message.
+ *
+ * @return 0 indicates success, either the command successfully completed
+ * or the <DFN>OMX_ErrorSameState</DFN> was returned. -1 indicates
+ * that the command terminated with a different error message.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete(COMPONENT_T *comp,
+ OMX_COMMANDTYPE command,
+ OMX_U32 nData2);
+
+
+/**
+ * The <DFN>ilclient_wait_for_command_complete_dual()</DFN> function
+ * is similar to <DFN>ilclient_wait_for_command_complete()</DFN>. The
+ * difference is that while waiting for the component to complete the
+ * event or raise an error, we can also report if another reports an
+ * error that terminates a command. This is useful if the two
+ * components are tunneled, and we need to wait for one component to
+ * enable a port, or change state to <DFN>OMX_StateIdle</DFN>. If the
+ * other component is the buffer supplier and reports an error, then
+ * it will not allocate buffers, so our first component may stall.
+ *
+ * @param comp The component currently processing a command.
+ *
+ * @param command The command being processed. This must be either
+ * <DFN>OMX_CommandStateSet</DFN>, <DFN>OMX_CommandPortDisable</DFN>
+ * or <DFN>OMX_CommandPortEnable</DFN>.
+ *
+ * @param nData2 The expected value of <DFN>nData2</DFN> in the
+ * command complete message.
+ *
+ * @param related Another component, where we will return if this
+ * component raises an error that terminates a command.
+ *
+ * @return 0 indicates success, either the command successfully
+ * completed or the <DFN>OMX_ErrorSameState</DFN> was returned. -1
+ * indicates that the command terminated with a different error
+ * message. -2 indicates that the related component raised an error.
+ * In this case, the error is not cleared from the related
+ * component's event list.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete_dual(COMPONENT_T *comp,
+ OMX_COMMANDTYPE command,
+ OMX_U32 nData2,
+ COMPONENT_T *related);
+
+
+/**
+ * The <DFN>ilclient_debug_output()</DFN> function adds a message to a
+ * host-specific debug display. For a local VideoCore host the message is
+ * added to the internal message log. For a Win32 host the message is
+ * printed to the debug display. This function should be customised
+ * when IL client is ported to another platform.
+ *
+ * @param format A message to add, together with the variable
+ * argument list similar to <DFN>printf</DFN> and other standard C functions.
+ *
+ * @return void
+ ***********************************************************/
+VCHPRE_ void VCHPOST_ ilclient_debug_output(char *format, ...);
+
+/**
+ * The <DFN>ilclient_get_handle()</DFN> function returns the
+ * underlying OMX component held by an IL component handle. This is
+ * needed when calling methods such as <DFN>OMX_SetParameter</DFN> on
+ * a component created using the IL client library.
+ *
+ * @param comp IL component handle
+ *
+ * @return The <DFN>OMX_HANDLETYPE</DFN> value for the component.
+ ***********************************************************/
+VCHPRE_ OMX_HANDLETYPE VCHPOST_ ilclient_get_handle(COMPONENT_T *comp);
+
+
+#ifndef OMX_SKIP64BIT
+
+/**
+ * Macro to return <DFN>OMX_TICKS</DFN> from a signed 64 bit value.
+ * This is the version where <DFN>OMX_TICKS</DFN> is a signed 64 bit
+ * value, an alternative definition is used when <DFN>OMX_TICKS</DFN>
+ * is a structure.
+ */
+#define ilclient_ticks_from_s64(s) (s)
+
+/**
+ * Macro to return signed 64 bit value from <DFN>OMX_TICKS</DFN>.
+ * This is the version where <DFN>OMX_TICKS</DFN> is a signed 64 bit
+ * value, an alternative definition is used when <DFN>OMX_TICKS</DFN>
+ * is a structure.
+ */
+#define ilclient_ticks_to_s64(t) (t)
+
+#else
+
+/**
+ * Inline function to return <DFN>OMX_TICKS</DFN> from a signed 64 bit
+ * value. This is the version where <DFN>OMX_TICKS</DFN> is a
+ * structure, an alternative definition is used when
+ * <DFN>OMX_TICKS</DFN> is a signed 64 bit value.
+ */
+static inline OMX_TICKS ilclient_ticks_from_s64(int64_t s) {
+ OMX_TICKS ret;
+ ret.nLowPart = s;
+ ret.nHighPart = s>>32;
+ return ret;
+}
+
+/**
+ * Inline function to return signed 64 bit value from
+ * <DFN>OMX_TICKS</DFN>. This is the version where
+ * <DFN>OMX_TICKS</DFN> is a structure, an alternative definition is
+ * used when <DFN>OMX_TICKS</DFN> is a signed 64 bit value.
+ */
+static inline int64_t ilclient_ticks_to_s64(OMX_TICKS t) {
+ uint64_t u = t.nLowPart | ((uint64_t)t.nHighPart << 32);
+ return u;
+}
+
+
+#endif /* OMX_SKIP64BIT */
+
+/**
+ * The <DFN>ilclient_get_port_index()</DFN> function returns the n'th
+ * port index of the specified type and direction for the given
+ * component.
+ *
+ * @param comp The component of interest
+ * @param dir The direction
+ * @param type The type, or -1 for any type.
+ * @param index Which port (counting from 0).
+ *
+ * @return The port index, or -1 if not found.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_get_port_index(COMPONENT_T *comp,
+ OMX_DIRTYPE dir,
+ OMX_PORTDOMAINTYPE type,
+ int index);
+
+
+/**
+ * The <DFN>ilclient_suggest_bufsize()</DFN> function gives a
+ * component a hint about the size of buffer it should use. This size
+ * is set on the component by setting the
+ * <DFN>OMX_IndexParamBrcmOutputBufferSize</DFN> index on the given
+ * component.
+ *
+ * @param comp IL component handle
+ * @param nBufSizeHint Size of buffer in bytes
+ *
+ * @return 0 indicates success, -1 indicates failure.
+ ***********************************************************/
+VCHPRE_ int VCHPOST_ ilclient_suggest_bufsize(COMPONENT_T *comp,
+ OMX_U32 nBufSizeHint);
+
+
+/**
+ * The <DFN>ilclient_stack_size()</DFN> function suggests a minimum
+ * stack size that tasks calling into with API will require.
+ *
+ * @return Suggested stack size in bytes.
+ ***********************************************************/
+VCHPRE_ unsigned int VCHPOST_ ilclient_stack_size(void);
+
+#endif /* ILCLIENT_H */
diff --git a/ilclient/ilcore.c b/ilclient/ilcore.c
new file mode 100644
index 0000000..356733d
--- /dev/null
+++ b/ilclient/ilcore.c
@@ -0,0 +1,308 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * \file
+ *
+ * \brief Host core implementation.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+//includes
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "IL/OMX_Component.h"
+#include "interface/vcos/vcos.h"
+
+#include "interface/vmcs_host/vcilcs.h"
+#include "interface/vmcs_host/vchost.h"
+#include "interface/vmcs_host/vcilcs_common.h"
+
+static int coreInit = 0;
+static int nActiveHandles = 0;
+static ILCS_SERVICE_T *ilcs_service = NULL;
+static VCOS_MUTEX_T lock;
+static VCOS_ONCE_T once = VCOS_ONCE_INIT;
+
+/* Atomic creation of lock protecting shared state */
+static void initOnce(void)
+{
+ VCOS_STATUS_T status;
+ status = vcos_mutex_create(&lock, VCOS_FUNCTION);
+ vcos_demand(status == VCOS_SUCCESS);
+}
+
+/* OMX_Init */
+OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void)
+{
+ VCOS_STATUS_T status;
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+
+ status = vcos_once(&once, initOnce);
+ vcos_demand(status == VCOS_SUCCESS);
+
+ vcos_mutex_lock(&lock);
+
+ if(coreInit == 0)
+ {
+ // we need to connect via an ILCS connection to VideoCore
+ VCHI_INSTANCE_T initialise_instance;
+ VCHI_CONNECTION_T *connection;
+ ILCS_CONFIG_T config;
+
+ vc_host_get_vchi_state(&initialise_instance, &connection);
+
+ vcilcs_config(&config);
+
+ ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0);
+
+ if(ilcs_service == NULL)
+ {
+ err = OMX_ErrorHardware;
+ goto end;
+ }
+
+ coreInit = 1;
+ }
+ else
+ coreInit++;
+
+end:
+ vcos_mutex_unlock(&lock);
+ return err;
+}
+
+/* OMX_Deinit */
+OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void)
+{
+ if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0))
+ return OMX_ErrorNotReady;
+
+ vcos_mutex_lock(&lock);
+
+ coreInit--;
+
+ if(coreInit == 0)
+ {
+ // we need to teardown the ILCS connection to VideoCore
+ ilcs_deinit(ilcs_service);
+ ilcs_service = NULL;
+ }
+
+ vcos_mutex_unlock(&lock);
+
+ return OMX_ErrorNone;
+}
+
+
+/* OMX_ComponentNameEnum */
+OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum(
+ OMX_OUT OMX_STRING cComponentName,
+ OMX_IN OMX_U32 nNameLength,
+ OMX_IN OMX_U32 nIndex)
+{
+ if(ilcs_service == NULL)
+ return OMX_ErrorBadParameter;
+
+ return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex);
+}
+
+
+/* OMX_GetHandle */
+OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle(
+ OMX_OUT OMX_HANDLETYPE* pHandle,
+ OMX_IN OMX_STRING cComponentName,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_CALLBACKTYPE* pCallBacks)
+{
+ OMX_ERRORTYPE eError;
+ OMX_COMPONENTTYPE *pComp;
+ OMX_HANDLETYPE hHandle = 0;
+
+ if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL)
+ {
+ if(pHandle)
+ *pHandle = NULL;
+ return OMX_ErrorBadParameter;
+ }
+
+ {
+ pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE));
+ if (!pComp)
+ {
+ vcos_assert(0);
+ return OMX_ErrorInsufficientResources;
+ }
+ memset(pComp, 0, sizeof(OMX_COMPONENTTYPE));
+ hHandle = (OMX_HANDLETYPE)pComp;
+ pComp->nSize = sizeof(OMX_COMPONENTTYPE);
+ pComp->nVersion.nVersion = OMX_VERSION;
+ eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName);
+
+ if (eError == OMX_ErrorNone) {
+ // Check that all function pointers have been filled in.
+ // All fields should be non-zero.
+ int i;
+ uint32_t *p = (uint32_t *) pComp;
+ for(i=0; i<sizeof(OMX_COMPONENTTYPE)>>2; i++)
+ if(*p++ == 0)
+ eError = OMX_ErrorInvalidComponent;
+
+ if(eError != OMX_ErrorNone && pComp->ComponentDeInit)
+ pComp->ComponentDeInit(hHandle);
+ }
+
+ if (eError == OMX_ErrorNone) {
+ eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData);
+ if (eError != OMX_ErrorNone)
+ pComp->ComponentDeInit(hHandle);
+ }
+ if (eError == OMX_ErrorNone) {
+ *pHandle = hHandle;
+ }
+ else {
+ *pHandle = NULL;
+ free(pComp);
+ }
+ }
+
+ if (eError == OMX_ErrorNone) {
+ vcos_mutex_lock(&lock);
+ nActiveHandles++;
+ vcos_mutex_unlock(&lock);
+ }
+
+ return eError;
+}
+
+/* OMX_FreeHandle */
+OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle(
+ OMX_IN OMX_HANDLETYPE hComponent)
+{
+ OMX_ERRORTYPE eError = OMX_ErrorNone;
+ OMX_COMPONENTTYPE *pComp;
+
+ if (hComponent == NULL || ilcs_service == NULL)
+ return OMX_ErrorBadParameter;
+
+ pComp = (OMX_COMPONENTTYPE*)hComponent;
+
+ if (ilcs_service == NULL)
+ return OMX_ErrorBadParameter;
+
+ eError = (pComp->ComponentDeInit)(hComponent);
+ if (eError == OMX_ErrorNone) {
+ vcos_mutex_lock(&lock);
+ --nActiveHandles;
+ vcos_mutex_unlock(&lock);
+ free(pComp);
+ }
+
+ vcos_assert(nActiveHandles >= 0);
+
+ return eError;
+}
+
+/* OMX_SetupTunnel */
+OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel(
+ OMX_IN OMX_HANDLETYPE hOutput,
+ OMX_IN OMX_U32 nPortOutput,
+ OMX_IN OMX_HANDLETYPE hInput,
+ OMX_IN OMX_U32 nPortInput)
+{
+ OMX_ERRORTYPE eError = OMX_ErrorNone;
+ OMX_COMPONENTTYPE *pCompIn, *pCompOut;
+ OMX_TUNNELSETUPTYPE oTunnelSetup;
+
+ if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL)
+ return OMX_ErrorBadParameter;
+
+ oTunnelSetup.nTunnelFlags = 0;
+ oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified;
+
+ pCompOut = (OMX_COMPONENTTYPE*)hOutput;
+
+ if (hOutput){
+ eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup);
+ }
+
+ if (eError == OMX_ErrorNone && hInput) {
+ pCompIn = (OMX_COMPONENTTYPE*)hInput;
+ eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup);
+
+ if (eError != OMX_ErrorNone && hOutput) {
+ /* cancel tunnel request on output port since input port failed */
+ pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL);
+ }
+ }
+ return eError;
+}
+
+/* OMX_GetComponentsOfRole */
+OMX_ERRORTYPE OMX_GetComponentsOfRole (
+ OMX_IN OMX_STRING role,
+ OMX_INOUT OMX_U32 *pNumComps,
+ OMX_INOUT OMX_U8 **compNames)
+{
+ OMX_ERRORTYPE eError = OMX_ErrorNone;
+
+ *pNumComps = 0;
+ return eError;
+}
+
+/* OMX_GetRolesOfComponent */
+OMX_ERRORTYPE OMX_GetRolesOfComponent (
+ OMX_IN OMX_STRING compName,
+ OMX_INOUT OMX_U32 *pNumRoles,
+ OMX_OUT OMX_U8 **roles)
+{
+ OMX_ERRORTYPE eError = OMX_ErrorNone;
+
+ *pNumRoles = 0;
+ return eError;
+}
+
+/* OMX_GetDebugInformation */
+OMX_ERRORTYPE OMX_GetDebugInformation (
+ OMX_OUT OMX_STRING debugInfo,
+ OMX_INOUT OMX_S32 *pLen)
+{
+ if(ilcs_service == NULL)
+ return OMX_ErrorBadParameter;
+
+ return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen);
+}
+
+
+
+/* File EOF */
+
diff --git a/omxdevice.c b/omxdevice.c
new file mode 100644
index 0000000..2034e51
--- /dev/null
+++ b/omxdevice.c
@@ -0,0 +1,782 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "omxdevice.h"
+
+#include <vdr/remux.h>
+#include <vdr/tools.h>
+
+#include <mpg123.h>
+
+#include <string.h>
+
+extern "C"
+{
+#include "ilclient.h"
+}
+
+#include "bcm_host.h"
+
+class cOmx
+{
+
+public:
+
+ static const char* errStr(int err)
+ {
+ return err == OMX_ErrorNone ? "None" :
+ err == OMX_ErrorInsufficientResources ? "InsufficientResources" :
+ err == OMX_ErrorUndefined ? "Undefined" :
+ err == OMX_ErrorInvalidComponentName ? "InvalidComponentName" :
+ err == OMX_ErrorComponentNotFound ? "ComponentNotFound" :
+ err == OMX_ErrorInvalidComponent ? "InvalidComponent" :
+ err == OMX_ErrorBadParameter ? "BadParameter" :
+ err == OMX_ErrorNotImplemented ? "NotImplemented" :
+ err == OMX_ErrorUnderflow ? "Underflow" :
+ err == OMX_ErrorOverflow ? "Overflow" :
+ err == OMX_ErrorHardware ? "Hardware" :
+ err == OMX_ErrorInvalidState ? "InvalidState" :
+ err == OMX_ErrorStreamCorrupt ? "StreamCorrupt" :
+ err == OMX_ErrorPortsNotCompatible ? "PortsNotCompatible" :
+ err == OMX_ErrorResourcesLost ? "ResourcesLost" :
+ err == OMX_ErrorNoMore ? "NoMore" :
+ err == OMX_ErrorVersionMismatch ? "VersionMismatch" :
+ err == OMX_ErrorNotReady ? "NotReady" :
+ err == OMX_ErrorTimeout ? "Timeout" :
+ err == OMX_ErrorSameState ? "SameState" :
+ err == OMX_ErrorResourcesPreempted ? "ResourcesPreempted" :
+ err == OMX_ErrorPortUnresponsiveDuringAllocation ? "PortUnresponsiveDuringAllocation" :
+ err == OMX_ErrorPortUnresponsiveDuringDeallocation ? "PortUnresponsiveDuringDeallocation" :
+ err == OMX_ErrorPortUnresponsiveDuringStop ? "PortUnresponsiveDuringStop" :
+ err == OMX_ErrorIncorrectStateTransition ? "IncorrectStateTransition" :
+ err == OMX_ErrorIncorrectStateOperation ? "IncorrectStateOperation" :
+ err == OMX_ErrorUnsupportedSetting ? "UnsupportedSetting" :
+ err == OMX_ErrorUnsupportedIndex ? "UnsupportedIndex" :
+ err == OMX_ErrorBadPortIndex ? "BadPortIndex" :
+ err == OMX_ErrorPortUnpopulated ? "PortUnpopulated" :
+ err == OMX_ErrorComponentSuspended ? "ComponentSuspended" :
+ err == OMX_ErrorDynamicResourcesUnavailable ? "DynamicResourcesUnavailable" :
+ err == OMX_ErrorMbErrorsInFrame ? "MbErrorsInFrame" :
+ err == OMX_ErrorFormatNotDetected ? "FormatNotDetected" :
+ err == OMX_ErrorContentPipeOpenFailed ? "ContentPipeOpenFailed" :
+ err == OMX_ErrorContentPipeCreationFailed ? "ContentPipeCreationFailed" :
+ err == OMX_ErrorSeperateTablesUsed ? "SeperateTablesUsed" :
+ err == OMX_ErrorTunnelingUnsupported ? "TunnelingUnsupported" :
+ err == OMX_ErrorKhronosExtensions ? "KhronosExtensions" :
+ err == OMX_ErrorVendorStartUnused ? "VendorStartUnused" :
+ err == OMX_ErrorDiskFull ? "DiskFull" :
+ err == OMX_ErrorMaxFileSize ? "MaxFileSize" :
+ err == OMX_ErrorDrmUnauthorised ? "DrmUnauthorised" :
+ err == OMX_ErrorDrmExpired ? "DrmExpired" :
+ err == OMX_ErrorDrmGeneral ? "DrmGeneral" :
+ "unknown";
+ };
+
+ enum eOmxComponent {
+ eClock = 0,
+ eVideoDecoder,
+ eVideoScheduler,
+ eVideoRender,
+ eAudioRender,
+ eNumComponents
+ };
+
+ enum eOmxTunnel {
+ eVideoDecoderToVideoScheduler = 0,
+ eVideoSchedulerToVideoRender,
+ eClockToVideoScheduler,
+ eClockToAudioRender,
+ eNumTunnels
+ };
+
+ ILCLIENT_T *client;
+ COMPONENT_T *comp[cOmx::eNumComponents + 1];
+ TUNNEL_T tun[cOmx::eNumTunnels + 1];
+
+ static void OmxError(void *omxDevice, COMPONENT_T *comp, unsigned int data)
+ {
+ if (data != OMX_ErrorSameState)
+ esyslog("rpihddevice: OmxError(%s)", errStr((int)data));
+ }
+
+ static void OmxBufferEmpty(void *omxDevice, COMPONENT_T *comp)
+ {
+// dsyslog("rpihddevice: OmxBufferEmpty()");
+ }
+
+ static void OmxPortSettingsChanged(void *omxDevice, COMPONENT_T *comp, unsigned int data)
+ {
+ cOmxDevice* dev = static_cast <cOmxDevice*> (omxDevice);
+ dev->HandlePortSettingsChanged(data);
+ }
+
+ static void OmxEndOfStream(void *omxDevice, COMPONENT_T *comp, unsigned int data)
+ {
+ cOmxDevice* dev = static_cast <cOmxDevice*> (omxDevice);
+ dev->HandleEndOfStream(data);
+ }
+
+ static void PtsToTicks(uint64_t pts, OMX_TICKS &ticks)
+ {
+ // ticks = pts * OMX_TICKS_PER_SECOND / PTSTICKS
+ pts = pts * 100 / 9;
+ ticks.nLowPart = (OMX_U32)pts;
+ ticks.nHighPart = (OMX_U32)(pts >> 32);
+ }
+
+ static uint64_t TicksToPts(OMX_TICKS &ticks)
+ {
+ // pts = ticks * PTSTICKS / OMX_TICKS_PER_SECOND
+ uint64_t pts = ticks.nHighPart << 32 + ticks.nLowPart;
+ pts = pts * 9 / 100;
+ return pts;
+ }
+
+};
+
+class cAudio
+{
+
+public:
+
+ cAudio() :
+ sampleRate(0),
+ bitDepth(0),
+ nChannels(0),
+ m_handle(0)
+ {
+ int ret;
+ mpg123_init();
+ m_handle = mpg123_new(NULL, &ret);
+ if (m_handle == NULL)
+ esyslog("rpihddevice: failed to create mpg123 handle!");
+
+ if (mpg123_open_feed(m_handle) == MPG123_ERR)
+ esyslog("rpihddevice: failed to open mpg123 feed!");
+
+ dsyslog("rpihddevice: new cAudio()");
+ }
+
+ ~cAudio()
+ {
+ mpg123_delete(m_handle);
+ dsyslog("rpihddevice: delete cAudio()");
+ }
+
+ unsigned int decode(const unsigned char *inbuf, unsigned int length, unsigned char *outbuf, unsigned int bufsize)
+ {
+ unsigned int done = 0;
+ if (mpg123_decode(m_handle, inbuf, length, outbuf, bufsize, &done) == MPG123_ERR)
+ esyslog("rpihddevice: failed to decode audio data!");
+ return done;
+ }
+
+ int sampleRate;
+ int bitDepth;
+ int nChannels;
+
+ mpg123_handle *m_handle;
+};
+
+cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) :
+ cDevice(),
+ m_onPrimaryDevice(onPrimaryDevice),
+ m_omx(new cOmx()),
+ m_audio(new cAudio()),
+ m_eosEvent(0),
+ m_state(eStop),
+ m_firstVideoPacket(false),
+ m_firstAudioPacket(false)
+{
+ m_eosEvent = new cCondWait();
+ m_mutex = new cMutex();
+}
+
+cOmxDevice::~cOmxDevice()
+{
+ OmxDeInit();
+ delete m_omx;
+ delete m_audio;
+ delete m_mutex;
+ delete m_eosEvent;
+}
+
+bool cOmxDevice::CanReplay(void) const
+{
+ // video codec de-initialization done
+ return (m_state == eStop);
+}
+
+bool cOmxDevice::SetPlayMode(ePlayMode PlayMode)
+{
+ dsyslog("rpihddevice: SetPlayMode(%s)",
+ PlayMode == pmNone ? "none" :
+ PlayMode == pmAudioVideo ? "Audio/Video" :
+ PlayMode == pmAudioOnly ? "Audio only" :
+ PlayMode == pmAudioOnlyBlack ? "Audio only, black" :
+ PlayMode == pmVideoOnly ? "Video only" :
+ "unsupported");
+ switch (PlayMode)
+ {
+ case pmNone:
+ Stop();
+ break;
+
+ case pmAudioVideo:
+ Start();
+ break;
+
+ case pmAudioOnly:
+ case pmAudioOnlyBlack:
+ case pmVideoOnly:
+
+ break;
+ }
+
+ return true;
+}
+
+int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
+{
+ m_mutex->Lock();
+
+ if (m_state != ePlay)
+ {
+ m_mutex->Unlock();
+ dsyslog("rpihddevice: PlayAudio() not replaying!");
+ return 0;
+ }
+
+ if (!PesHasLength(Data))
+ {
+ esyslog("rpihddevice: audio packet dropped!");
+ m_mutex->Unlock();
+ return Length;
+ }
+
+ int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
+
+ const uchar *payload = Data + PesPayloadOffset(Data);
+ int length = PesLength(Data) - PesPayloadOffset(Data);
+
+ // first packet of a new stream needs valid PTS
+ if (m_firstAudioPacket && pts == 0)
+ {
+ m_mutex->Unlock();
+ return Length;
+ }
+
+ if (m_firstAudioPacket)
+ OmxSetAudioCodec(payload);
+
+ OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_omx->comp[cOmx::eAudioRender], 100, 1);
+ if (buf == NULL)
+ {
+ esyslog("rpihddevice: failed to get audio buffer!");
+ m_mutex->Unlock();
+ return 0;
+ }
+
+ // decode audio packet
+ buf->nFilledLen = m_audio->decode(payload, length, buf->pBuffer, buf->nAllocLen);
+
+ buf->nFlags = m_firstAudioPacket ? OMX_BUFFERFLAG_STARTTIME : 0;
+ cOmx::PtsToTicks(pts, buf->nTimeStamp);
+
+// dsyslog("A: %u.%u - %lld", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart, pts);
+// dsyslog("rpihddevice: PlayAudio(%u.%u, %02x %02x %02x %02x, %d)", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart,
+// buf->pBuffer[0], buf->pBuffer[1], buf->pBuffer[2], buf->pBuffer[3], buf->nFilledLen);
+
+ if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_omx->comp[cOmx::eAudioRender]), buf) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to pass buffer to audio render!");
+
+ m_firstAudioPacket = false;
+
+ m_mutex->Unlock();
+ return Length;
+}
+
+int cOmxDevice::PlayVideo(const uchar *Data, int Length)
+{
+ m_mutex->Lock();
+
+ if (m_state != ePlay)
+ {
+ m_mutex->Unlock();
+ dsyslog("rpihddevice: PlayVideo() not replaying!");
+ return 0;
+ }
+
+ if (!PesHasLength(Data))
+ {
+ esyslog("rpihddevice: video packet dropped!");
+ m_mutex->Unlock();
+ return Length;
+ }
+
+ int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0;
+
+ const uchar *payload = Data + PesPayloadOffset(Data);
+ int length = PesLength(Data) - PesPayloadOffset(Data);
+
+ // first packet of a new stream needs valid PTS and MPEG start code
+ if (m_firstVideoPacket &&
+ (pts == 0 || !(payload[0] == 0x00 && payload[1] == 0x00 &&
+ payload[2] == 0x01 && payload[3] == 0xb3)))
+ {
+ m_mutex->Unlock();
+ return Length;
+ }
+
+ if (m_firstVideoPacket)
+ OmxSetVideoCodec(payload);
+
+ OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_omx->comp[cOmx::eVideoDecoder], 130, 1);
+ if (buf == NULL)
+ {
+ esyslog("rpihddevice: failed to get video buffer!");
+ m_mutex->Unlock();
+ return 0;
+ }
+
+ cOmx::PtsToTicks(pts, buf->nTimeStamp);
+ buf->nFlags = m_firstVideoPacket ? OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN;
+
+ if (length <= buf->nAllocLen)
+ {
+ memcpy(buf->pBuffer, payload, length);
+ buf->nFilledLen = length;
+ }
+ else
+ esyslog("rpihddevice: video packet too long for video buffer!");
+
+// dsyslog("V: %u.%u - %lld", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart, pts);
+// dsyslog("rpihddevice: PlayVideo(%u.%u, %02x %02x %02x %02x, %d)", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart,
+// buf->pBuffer[0], buf->pBuffer[1], buf->pBuffer[2], buf->pBuffer[3], buf->nFilledLen);
+
+ if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_omx->comp[cOmx::eVideoDecoder]), buf) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to pass buffer to video decoder!");
+
+ m_firstVideoPacket = false;
+
+ m_mutex->Unlock();
+ return Length;
+}
+
+int64_t cOmxDevice::GetSTC(void)
+{
+ int64_t stc = -1;
+
+/* OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp;
+ memset(&timestamp, 0, sizeof(timestamp));
+ timestamp.nSize = sizeof(timestamp);
+ timestamp.nVersion.nVersion = OMX_VERSION;
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]),
+ OMX_IndexConfigTimeCurrentMediaTime, &timestamp) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed get current clock reference!");
+ else
+ stc = cOmx::TicksToPts(timestamp.nTimestamp);
+
+ dsyslog("rpihddevice: GetSTC() = %llu", stc);
+*/
+ return stc;
+}
+
+void cOmxDevice::Play(void)
+{
+ dsyslog("rpihddevice: Play()");
+
+}
+
+bool cOmxDevice::Flush(int TimeoutMs)
+{
+ dsyslog("rpihddevice: flush()");
+
+ return true;
+}
+
+void cOmxDevice::MakePrimaryDevice(bool On)
+{
+ if (On && m_onPrimaryDevice)
+ m_onPrimaryDevice();
+ cDevice::MakePrimaryDevice(On);
+}
+
+void cOmxDevice::HandleEndOfStream(unsigned int portId)
+{
+ dsyslog("rpihddevice: HandleEndOfStream(%d)", portId);
+
+ switch (portId)
+ {
+ case 131:
+ break;
+
+ case 11:
+ break;
+
+ case 90:
+ m_eosEvent->Signal();
+ break;
+ }
+}
+
+void cOmxDevice::HandlePortSettingsChanged(unsigned int portId)
+{
+ dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId);
+
+ switch (portId)
+ {
+ case 131:
+ if (m_state == ePlay)
+ {
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eVideoDecoderToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from video decoder to video scheduler!");
+ if (ilclient_change_component_state(m_omx->comp[cOmx::eVideoScheduler], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to enable video scheduler!");
+ }
+ else
+ esyslog("HandlePortSettingsChanged: a");
+ break;
+
+ case 11:
+ if (m_state == ePlay)
+ {
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eVideoSchedulerToVideoRender], 0, 1000) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from scheduler to render!");
+ if (ilclient_change_component_state(m_omx->comp[cOmx::eVideoRender], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to enable video render!");
+ }
+ else
+ esyslog("HandlePortSettingsChanged: b");
+ break;
+ }
+}
+
+void cOmxDevice::SetClockState(eClockState clockState)
+{
+ dsyslog("rpihddevice: SetClockState(%s)",
+ clockState == eClockStateRunning ? "eClockStateRunning" :
+ clockState == eClockStateStopped ? "eClockStateStopped" :
+ clockState == eClockStateWaiting ? "eClockStateWaiting" : "unknown");
+
+ OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
+ memset(&cstate, 0, sizeof(cstate));
+ cstate.nSize = sizeof(cstate);
+ cstate.nVersion.nVersion = OMX_VERSION;
+
+ if (OMX_GetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed get clock state!");
+
+ if ((clockState == eClockStateWaiting) && (cstate.eState == OMX_TIME_ClockStateRunning))
+ {
+ esyslog("rpihddevice: need to disable clock first!");
+ cstate.eState = OMX_TIME_ClockStateStopped;
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed set clock state!");
+ }
+
+ switch (clockState)
+ {
+ case eClockStateRunning:
+ cstate.eState = OMX_TIME_ClockStateRunning;
+ break;
+
+ case eClockStateStopped:
+ cstate.eState = OMX_TIME_ClockStateStopped;
+ break;
+
+ case eClockStateWaiting:
+ cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
+ cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1;
+ break;
+ }
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed set clock state!");
+}
+
+void cOmxDevice::SetClockScale(int scale)
+{
+ OMX_TIME_CONFIG_SCALETYPE scaleType;
+ memset(&scaleType, 0, sizeof(scaleType));
+ scaleType.xScale = scale;
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]), OMX_IndexConfigTimeScale, &scaleType) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed configuring clock!");
+}
+
+int cOmxDevice::OmxInit()
+{
+ dsyslog("OmxInit()");
+
+ m_omx->client = ilclient_init();
+ if (m_omx->client == NULL)
+ esyslog("rpihddevice: ilclient_init() failed!");
+
+ if (OMX_Init() != OMX_ErrorNone)
+ esyslog("rpihddevice: OMX_Init() failed!");
+
+ ilclient_set_error_callback(m_omx->client, cOmx::OmxError, this);
+ ilclient_set_empty_buffer_done_callback(m_omx->client, cOmx::OmxBufferEmpty, this);
+ ilclient_set_port_settings_callback(m_omx->client, cOmx::OmxPortSettingsChanged, this);
+ ilclient_set_eos_callback(m_omx->client, cOmx::OmxEndOfStream, this);
+
+ // create video_decode
+ if (ilclient_create_component(m_omx->client, &m_omx->comp[cOmx::eVideoDecoder],
+ "video_decode", (ILCLIENT_CREATE_FLAGS_T)(ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
+ esyslog("rpihddevice: failed creating video decoder!");
+
+ // create video_render
+ if (ilclient_create_component(m_omx->client, &m_omx->comp[cOmx::eVideoRender],
+ "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating video render!");
+
+ //create clock
+ if (ilclient_create_component(m_omx->client, &m_omx->comp[cOmx::eClock],
+ "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating clock!");
+
+ // create audio_render
+ if (ilclient_create_component(m_omx->client, &m_omx->comp[cOmx::eAudioRender],
+ "audio_render", (ILCLIENT_CREATE_FLAGS_T)(ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0)
+ esyslog("rpihddevice: failed creating audio render!");
+
+ //create video_scheduler
+ if (ilclient_create_component(m_omx->client, &m_omx->comp[cOmx::eVideoScheduler],
+ "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0)
+ esyslog("rpihddevice: failed creating video scheduler!");
+
+ // setup tunnels
+ set_tunnel(&m_omx->tun[cOmx::eVideoDecoderToVideoScheduler],
+ m_omx->comp[cOmx::eVideoDecoder], 131, m_omx->comp[cOmx::eVideoScheduler], 10);
+
+ set_tunnel(&m_omx->tun[cOmx::eVideoSchedulerToVideoRender],
+ m_omx->comp[cOmx::eVideoScheduler], 11, m_omx->comp[cOmx::eVideoRender], 90);
+
+ set_tunnel(&m_omx->tun[cOmx::eClockToVideoScheduler],
+ m_omx->comp[cOmx::eClock], 80, m_omx->comp[cOmx::eVideoScheduler], 12);
+
+ set_tunnel(&m_omx->tun[cOmx::eClockToAudioRender],
+ m_omx->comp[cOmx::eClock], 81, m_omx->comp[cOmx::eAudioRender], 101);
+
+ // setup clock tunnels first
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eClockToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eClockToAudioRender], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to audio render!");
+
+ OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refclock;
+ memset(&refclock, 0, sizeof(refclock));
+ refclock.nSize = sizeof(refclock);
+ refclock.nVersion.nVersion = OMX_VERSION;
+ refclock.eClock = OMX_TIME_RefClockAudio;
+// refclock.eClock = OMX_TIME_RefClockVideo;
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eClock]), OMX_IndexConfigTimeActiveRefClock, &refclock) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed set active clock reference!");
+
+ ilclient_change_component_state(m_omx->comp[cOmx::eClock], OMX_StateExecuting);
+ ilclient_change_component_state(m_omx->comp[cOmx::eVideoDecoder], OMX_StateIdle);
+
+ OMX_PARAM_PORTDEFINITIONTYPE param;
+ memset(&param, 0, sizeof(param));
+ param.nSize = sizeof(param);
+ param.nVersion.nVersion = OMX_VERSION;
+ param.nPortIndex = 100;
+ if (OMX_GetParameter(ILC_GET_HANDLE(m_omx->comp[cOmx::eAudioRender]), OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get audio render port parameters!");
+
+ // set up the number and size of buffers
+ param.nBufferSize = 65536;
+ param.nBufferSize = (param.nBufferSize + 15) & ~15; // 16 byte aligned
+ param.nBufferCountActual = 16;
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_omx->comp[cOmx::eAudioRender]), OMX_IndexParamPortDefinition, &param) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to get audio render port parameters!");
+
+ // configure audio render
+ OMX_AUDIO_PARAM_PCMMODETYPE pcmMode;
+ memset(&pcmMode, 0, sizeof(pcmMode));
+ pcmMode.nSize = sizeof(pcmMode);
+ pcmMode.nVersion.nVersion = OMX_VERSION;
+ pcmMode.nPortIndex = 100;
+ pcmMode.nChannels = 2;
+ pcmMode.eNumData = OMX_NumericalDataSigned;
+ pcmMode.eEndian = OMX_EndianLittle;
+ pcmMode.bInterleaved = OMX_TRUE;
+ pcmMode.nBitPerSample = 16;
+ pcmMode.nSamplingRate = 48000;
+ pcmMode.ePCMMode = OMX_AUDIO_PCMModeLinear;
+ pcmMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_omx->comp[cOmx::eAudioRender]), OMX_IndexParamAudioPcm, &pcmMode) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio render parameters!");
+
+ OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
+ memset(&audioDest, 0, sizeof(audioDest));
+ audioDest.nSize = sizeof(audioDest);
+ audioDest.nVersion.nVersion = OMX_VERSION;
+ strcpy((char *)audioDest.sName, "local");
+
+ if (OMX_SetConfig(ILC_GET_HANDLE(m_omx->comp[cOmx::eAudioRender]), OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set audio destination!");
+
+ return 0;
+}
+
+void cOmxDevice::Start()
+{
+ dsyslog("rpihddevice: Start()");
+ m_mutex->Lock();
+
+ m_state = ePlay;
+ m_firstVideoPacket = true;
+ m_firstAudioPacket = true;
+
+ SetClockState(eClockStateWaiting);
+
+ m_mutex->Unlock();
+ dsyslog("rpihddevice: Start() end");
+}
+
+void cOmxDevice::Stop()
+{
+ dsyslog("rpihddevice: Stop()");
+ m_mutex->Lock();
+
+ m_state = eStop;
+/*
+ OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_omx->comp[cOmx::eVideoDecoder], 130, 1);
+ if (buf == NULL)
+ return;
+
+ buf->nFilledLen = 0;
+ buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
+
+ if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_omx->comp[cOmx::eVideoDecoder]), buf) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to send empty packet to video decoder!");
+
+ if (!m_eosEvent->Wait(10000))
+ esyslog("rpihddevice: time out waiting for EOS event!");
+*/
+ // need to flush the renderer to allow video_decode to disable its input port
+
+ // put video decoder into idle
+ ilclient_change_component_state(m_omx->comp[cOmx::eVideoDecoder], OMX_StateIdle);
+
+ // put video scheduler into idle
+ ilclient_flush_tunnels(&m_omx->tun[cOmx::eVideoDecoderToVideoScheduler], 1);
+ ilclient_disable_tunnel(&m_omx->tun[cOmx::eVideoDecoderToVideoScheduler]);
+ ilclient_flush_tunnels(&m_omx->tun[cOmx::eClockToVideoScheduler], 1);
+ ilclient_disable_tunnel(&m_omx->tun[cOmx::eClockToVideoScheduler]);
+ ilclient_change_component_state(m_omx->comp[cOmx::eVideoScheduler], OMX_StateIdle);
+
+ // put video render into idle
+ ilclient_flush_tunnels(&m_omx->tun[cOmx::eVideoSchedulerToVideoRender], 1);
+ ilclient_disable_tunnel(&m_omx->tun[cOmx::eVideoSchedulerToVideoRender]);
+ ilclient_change_component_state(m_omx->comp[cOmx::eVideoRender], OMX_StateIdle);
+
+ // put audio render onto idle
+ ilclient_flush_tunnels(&m_omx->tun[cOmx::eClockToAudioRender], 1);
+ ilclient_disable_tunnel(&m_omx->tun[cOmx::eClockToAudioRender]);
+ ilclient_change_component_state(m_omx->comp[cOmx::eAudioRender], OMX_StateIdle);
+
+ // disable port buffers and allow video decoder and audio render to reconfig
+ ilclient_disable_port_buffers(m_omx->comp[cOmx::eVideoDecoder], 130, NULL, NULL, NULL);
+ ilclient_disable_port_buffers(m_omx->comp[cOmx::eAudioRender], 100, NULL, NULL, NULL);
+
+ SetClockState(eClockStateStopped);
+
+ m_mutex->Unlock();
+ dsyslog("rpihddevice: Stop() end");
+}
+
+void cOmxDevice::OmxSetVideoCodec(const uchar *data)
+{
+ dsyslog("rpihddevice: OmxSetVideoCodec()");
+
+ // configure video decoder
+ OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat;
+ memset(&videoFormat, 0, sizeof(videoFormat));
+ videoFormat.nSize = sizeof(videoFormat);
+ videoFormat.nVersion.nVersion = OMX_VERSION;
+ videoFormat.nPortIndex = 130;
+ videoFormat.eCompressionFormat = OMX_VIDEO_CodingMPEG2;
+
+ if (OMX_SetParameter(ILC_GET_HANDLE(m_omx->comp[cOmx::eVideoDecoder]), OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone)
+ esyslog("rpihddevice: failed to set video decoder parameters!");
+
+ if (ilclient_enable_port_buffers(m_omx->comp[cOmx::eVideoDecoder], 130, NULL, NULL, NULL) != 0)
+ esyslog("rpihddevice: failed to enable port buffer on video decoder!");
+
+ if (ilclient_change_component_state(m_omx->comp[cOmx::eVideoDecoder], OMX_StateExecuting) != 0)
+ esyslog("rpihddevice: failed to set video decoder to executing state!");
+
+ // setup clock tunnels first
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eClockToVideoScheduler], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ dsyslog("rpihddevice: OmxSetVideoCodec() end");
+}
+
+void cOmxDevice::OmxSetAudioCodec(const uchar *data)
+{
+ dsyslog("rpihddevice: OmxSetAudioCodec()");
+
+ if (ilclient_enable_port_buffers(m_omx->comp[cOmx::eAudioRender], 100, NULL, NULL, NULL) != 0)
+ esyslog("rpihddevice: failed to enable port buffer on audio render!");
+
+ ilclient_change_component_state(m_omx->comp[cOmx::eAudioRender], OMX_StateExecuting);
+
+ if (ilclient_setup_tunnel(&m_omx->tun[cOmx::eClockToAudioRender], 0, 0) != 0)
+ esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!");
+
+ dsyslog("rpihddevice: OmxSetAudioCodec() end");
+}
+
+int cOmxDevice::OmxDeInit()
+{
+ dsyslog("rpihddevice: OmxDeInit()");
+
+ // need to flush the renderer to allow video_decode to disable its input port
+// ilclient_flush_tunnels(m_omx->tun, 0);
+
+// ilclient_disable_tunnel(&m_omx->tun[cOmx::eVideoDecoderToVideoScheduler]);
+// ilclient_disable_tunnel(&m_omx->tun[cOmx::eVideoSchedulerToVideoRender]);
+// ilclient_disable_tunnel(&m_omx->tun[cOmx::eClockToVideoScheduler]);
+// ilclient_disable_tunnel(&m_omx->tun[cOmx::eClockToAudioRender]);
+
+ ilclient_teardown_tunnels(m_omx->tun);
+
+ ilclient_state_transition(m_omx->comp, OMX_StateIdle);
+ ilclient_state_transition(m_omx->comp, OMX_StateLoaded);
+
+ ilclient_cleanup_components(m_omx->comp);
+
+ OMX_Deinit();
+ ilclient_destroy(m_omx->client);
+
+ return 0;
+}
+
+void cOmxDevice::GetDisplaySize(int &width, int &height, double &aspect)
+{
+ uint32_t screenWidth;
+ uint32_t screenHeight;
+
+ if (graphics_get_display_size(0 /* LCD */, &screenWidth, &screenHeight) < 0)
+ esyslog("rpihddevice: failed to get display size!");
+ else
+ {
+ width = (int)screenWidth;
+ height = (int)screenHeight;
+ aspect = 1;
+ }
+}
+
+
+
diff --git a/omxdevice.h b/omxdevice.h
new file mode 100644
index 0000000..a06d822
--- /dev/null
+++ b/omxdevice.h
@@ -0,0 +1,90 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef OMX_DEVICE_H
+#define OMX_DEVICE_H
+
+#include <vdr/device.h>
+#include <vdr/thread.h>
+
+class cOmx;
+class cAudio;
+
+class cOmxDevice : cDevice
+{
+
+public:
+
+ enum eState {
+ eStop,
+ ePlay
+ };
+
+ enum eClockState {
+ eClockStateRunning,
+ eClockStateStopped,
+ eClockStateWaiting
+ };
+
+ cOmxDevice(void (*onPrimaryDevice)(void));
+ virtual ~cOmxDevice();
+
+ virtual bool HasDecoder(void) const { return true; };
+
+ virtual bool SetPlayMode(ePlayMode PlayMode);
+
+ virtual bool CanReplay(void) const;
+
+ virtual int PlayVideo(const uchar *Data, int Length);
+ virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
+
+ virtual int64_t GetSTC(void);
+
+ virtual void Play(void);
+
+ virtual bool Flush(int TimeoutMs = 0);
+
+ virtual void HandlePortSettingsChanged(unsigned int portId);
+ virtual void HandleEndOfStream(unsigned int portId);
+
+ virtual int OmxInit();
+ virtual int OmxDeInit();
+
+ virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect)
+ { cOmxDevice::GetDisplaySize(Width, Height, PixelAspect); }
+
+ static void GetDisplaySize(int &width, int &height, double &aspect);
+
+protected:
+
+ virtual void MakePrimaryDevice(bool On);
+
+private:
+
+ void (*m_onPrimaryDevice)(void);
+
+ virtual void Start(void);
+ virtual void Stop(void);
+
+ virtual void SetClockScale(int scale);
+ virtual void SetClockState(eClockState clockState);
+
+ virtual void OmxSetVideoCodec(const uchar *data);
+ virtual void OmxSetAudioCodec(const uchar *data);
+
+ cOmx *m_omx;
+ cAudio *m_audio;
+ cCondWait *m_eosEvent;
+ cMutex *m_mutex;
+
+ eState m_state;
+
+ bool m_firstVideoPacket;
+ bool m_firstAudioPacket;
+
+};
+
+#endif
diff --git a/ovgosd.c b/ovgosd.c
new file mode 100644
index 0000000..2e472f2
--- /dev/null
+++ b/ovgosd.c
@@ -0,0 +1,297 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <VG/openvg.h>
+#include <VG/vgu.h>
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+
+#include "ovgosd.h"
+#include "omxdevice.h"
+
+class cOvg : public cThread
+{
+public:
+
+ cOvg() :
+ cThread(),
+ m_do(0),
+ m_done(0),
+ m_mutex(0),
+ m_width(0),
+ m_height(0),
+ m_pixmap(0),
+ m_aspect(0),
+ m_d(0),
+ m_x(0),
+ m_y(0),
+ m_w(0),
+ m_h(0),
+ m_clear(false)
+ {
+ cOmxDevice::GetDisplaySize(m_width, m_height, m_aspect);
+
+ m_do = new cCondWait();
+ m_done = new cCondWait();
+ m_mutex = new cMutex();
+
+ Start();
+ }
+
+ ~cOvg()
+ {
+ Cancel(-1);
+ Clear();
+
+ delete m_do;
+ delete m_done;
+ delete m_mutex;
+ }
+
+ void GetDisplaySize(int &width, int &height, double &aspect)
+ {
+ width = m_width;
+ height = m_height;
+ aspect = m_aspect;
+ }
+
+ void DrawPixmap(int x, int y, int w, int h, int d, const uint8_t *data)
+ {
+ m_mutex->Lock();
+ m_pixmap = data;
+ m_d = d;
+ m_x = x;
+ m_y = y;
+ m_w = w;
+ m_h = h;
+ m_do->Signal();
+ m_done->Wait();
+ m_mutex->Unlock();
+ }
+
+ void Clear()
+ {
+ m_mutex->Lock();
+ m_clear = true;
+ m_do->Signal();
+ m_done->Wait();
+ m_mutex->Unlock();
+ }
+
+protected:
+
+ virtual void Action(void)
+ {
+ dsyslog("rpihddevice: cOvg() thread started");
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY)
+ esyslog("rpihddevice: failed to get EGL display connection!");
+
+ if (eglInitialize(display, NULL, NULL) == EGL_FALSE)
+ esyslog("rpihddevice: failed to init EGL display connection!");
+
+ eglBindAPI(EGL_OPENVG_API);
+
+ const EGLint fbAttr[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_CONFORMANT, EGL_OPENVG_BIT,
+ EGL_NONE
+ };
+
+ EGLConfig config;
+ EGLint numConfig;
+
+ // get an appropriate EGL frame buffer configuration
+ if (eglChooseConfig(display, fbAttr, &config, 1, &numConfig) == EGL_FALSE)
+ esyslog("rpihddevice: failed to get EGL frame buffer config!");
+
+ // create an EGL rendering context
+ EGLContext context = eglCreateContext(display, config, NULL, NULL);
+ if (context == EGL_NO_CONTEXT)
+ esyslog("rpihddevice: failed to create EGL rendering context!");
+
+ DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = vc_dispmanx_display_open(0 /* LCD */);
+ DISPMANX_UPDATE_HANDLE_T dispmanUpdate = vc_dispmanx_update_start(0);
+
+ VC_RECT_T dstRect = { 0, 0, m_width, m_height };
+ VC_RECT_T srcRect = { 0, 0, m_width << 16, m_height << 16 };
+
+ DISPMANX_ELEMENT_HANDLE_T dispmanElement = vc_dispmanx_element_add(
+ dispmanUpdate, dispmanDisplay, 2, &dstRect, 0, &srcRect,
+ DISPMANX_PROTECTION_NONE, 0, 0, (DISPMANX_TRANSFORM_T)0);
+
+ vc_dispmanx_update_submit_sync(dispmanUpdate);
+
+ EGL_DISPMANX_WINDOW_T nativewindow;
+ nativewindow.element = dispmanElement;
+ nativewindow.width = m_width;
+ nativewindow.height = m_height;
+
+ const EGLint windowAttr[] = {
+ EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER,
+ EGL_NONE
+ };
+
+ EGLSurface surface = eglCreateWindowSurface(display, config, &nativewindow, windowAttr);
+ if (surface == EGL_NO_SURFACE)
+ esyslog("rpihddevice: failed to create EGL window surface!");
+
+ // connect the context to the surface
+ if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
+ esyslog("rpihddevice: failed to connect context to surface!");
+
+ float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ vgSetfv(VG_CLEAR_COLOR, 4, color);
+ vgClear(0, 0, m_width, m_height);
+ eglSwapBuffers(display, surface);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
+ vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+
+ vgLoadIdentity();
+ vgScale(1.0f, -1.0f);
+ vgTranslate(0.0f, -m_height);
+
+ VGImage image = vgCreateImage(VG_sARGB_8888, m_width, m_height, VG_IMAGE_QUALITY_BETTER);
+
+ while (Running())
+ {
+ m_do->Wait();
+ if (m_pixmap)
+ {
+ vgClearImage(image, m_x, m_y, m_w, m_h);
+ vgImageSubData(image, m_pixmap, m_d, VG_sARGB_8888, m_x, m_y, m_w, m_h);
+ vgDrawImage(image);
+ m_pixmap = 0;
+ }
+ if (m_clear)
+ {
+ vgClearImage(image, 0, 0, m_width, m_height);
+ vgDrawImage(image);
+ m_clear = false;
+ }
+ eglSwapBuffers(display, surface);
+ m_done->Signal();
+ }
+
+ vgDestroyImage(image);
+
+ // clear screen
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(display, surface);
+
+ // Release OpenGL resources
+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroySurface(display, surface);
+ eglDestroyContext(display, context);
+ eglTerminate(display);
+
+ dsyslog("rpihddevice: cOvg() thread ended");
+ }
+
+private:
+
+ cCondWait* m_do;
+ cCondWait* m_done;
+ cMutex* m_mutex;
+
+ int m_width;
+ int m_height;
+ double m_aspect;
+
+ const uint8_t *m_pixmap;
+ int m_d;
+ int m_x;
+ int m_y;
+ int m_w;
+ int m_h;
+
+ bool m_clear;
+
+};
+
+cRpiOsdProvider::cRpiOsdProvider() :
+ cOsdProvider(),
+ m_ovg(0)
+{
+ dsyslog("rpihddevice: new cOsdProvider()");
+ m_ovg = new cOvg();
+}
+
+cRpiOsdProvider::~cRpiOsdProvider()
+{
+ dsyslog("rpihddevice: delete cOsdProvider()");
+ delete m_ovg;
+}
+
+cOsd *cRpiOsdProvider::CreateOsd(int Left, int Top, uint Level)
+{
+ return new cOvgOsd(Left, Top, Level, m_ovg);
+}
+
+cOvgOsd::cOvgOsd(int Left, int Top, uint Level, cOvg *ovg) :
+ cOsd(Left, Top, Level),
+ m_ovg(ovg)
+{
+}
+
+cOvgOsd::~cOvgOsd()
+{
+ m_ovg->Clear();
+}
+
+void cOvgOsd::Flush(void)
+{
+ if (IsTrueColor())
+ {
+ LOCK_PIXMAPS;
+
+ while (cPixmapMemory *pm = RenderPixmaps()) {
+ int w = pm->ViewPort().Width();
+ int h = pm->ViewPort().Height();
+ int d = w * sizeof(tColor);
+ m_ovg->DrawPixmap(
+ Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y(),
+ pm->ViewPort().Width(), pm->ViewPort().Height(),
+ pm->ViewPort().Width() * sizeof(tColor), pm->Data());
+ delete pm;
+ }
+
+ return;
+ }
+
+ for (int i = 0; cBitmap *bitmap = GetBitmap(i); ++i)
+ {
+ int x1, y1, x2, y2;
+ if (bitmap->Dirty(x1, y1, x2, y2))
+ {
+ int w = x2 - x1 + 1;
+ int h = y2 - y1 + 1;
+ uint8_t *argb = (uint8_t *) malloc(w * h * sizeof(uint32_t));
+
+ for (int y = y1; y <= y2; ++y)
+ {
+ for (int x = x1; x <= x2; ++x)
+ {
+ ((uint32_t *) argb)[x - x1 + (y - y1) * w] =
+ bitmap->GetColor(x, y);
+ }
+ }
+ m_ovg->DrawPixmap(Left() + bitmap->X0() + x1,
+ Top() + bitmap->Y0() + y1, w, h, w * sizeof(tColor), argb);
+ bitmap->Clean();
+ free(argb);
+ }
+ }
+}
+
diff --git a/ovgosd.h b/ovgosd.h
new file mode 100644
index 0000000..d4a7c0d
--- /dev/null
+++ b/ovgosd.h
@@ -0,0 +1,52 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef OVG_OSD_H
+#define OVG_OSD_H
+
+#include <vdr/osd.h>
+
+class cOvg;
+
+class cRpiOsdProvider : public cOsdProvider
+{
+
+public:
+
+ cRpiOsdProvider();
+ ~cRpiOsdProvider();
+
+protected:
+
+ virtual cOsd *CreateOsd(int Left, int Top, uint Level);
+ virtual bool ProvidesTrueColor(void) { return true; }
+ virtual int StoreImageData(const cImage &Image) { return 0; }
+ virtual void DropImageData(int ImageHandle) {}
+
+private:
+
+ cOvg *m_ovg;
+};
+
+
+class cOvgOsd : public cOsd
+{
+
+public:
+
+ cOvgOsd(int Left, int Top, uint Level, cOvg *ovg);
+ virtual ~cOvgOsd();
+
+ virtual void Flush(void);
+
+private:
+
+ cOvg *m_ovg;
+
+};
+
+#endif
+
diff --git a/rpihddevice.c b/rpihddevice.c
new file mode 100644
index 0000000..b66af7e
--- /dev/null
+++ b/rpihddevice.c
@@ -0,0 +1,98 @@
+/*
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <vdr/plugin.h>
+#include <vdr/config.h>
+
+#include "ovgosd.h"
+#include "omxdevice.h"
+
+#include "bcm_host.h"
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "HD output device for Raspberry Pi";
+
+class cDummyDevice : cDevice
+{
+
+public:
+
+ cDummyDevice() { }
+ virtual ~cDummyDevice() { }
+ virtual bool HasDecoder(void) const { return true; }
+ virtual bool SetPlayMode(ePlayMode PlayMode) { return true; }
+ virtual int PlayVideo(const uchar *Data, int Length) { return Length; }
+ virtual int PlayAudio(const uchar *Data, int Length, uchar Id) { return Length; }
+ virtual bool Poll(cPoller &Poller, int TimeoutMs = 0) { return true; }
+ virtual bool Flush(int TimeoutMs = 0) { return true; }
+ virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect)
+ { cOmxDevice::GetDisplaySize(Width, Height, PixelAspect); }
+ bool Start(void) {return true;}
+
+protected:
+ virtual void MakePrimaryDevice(bool On) { if (On) new cRpiOsdProvider(); }
+
+};
+
+class cPluginRpiHdDevice : public cPlugin
+{
+private:
+
+ cOmxDevice *m_device;
+
+ static void OnPrimaryDevice() { new cRpiOsdProvider(); }
+
+public:
+ cPluginRpiHdDevice(void);
+ virtual ~cPluginRpiHdDevice();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void) { return NULL; }
+ virtual bool ProcessArgs(int argc, char *argv[]) { return true; }
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void) {}
+ virtual const char *MainMenuEntry(void) { return NULL; }
+ virtual cOsdObject *MainMenuAction(void) { return NULL; }
+ virtual cMenuSetupPage *SetupMenu(void) { return NULL; }
+ virtual bool SetupParse(const char *Name, const char *Value) { return false; };
+};
+
+cPluginRpiHdDevice::cPluginRpiHdDevice(void) :
+ m_device(0)
+{
+ bcm_host_init();
+
+ m_device = new cOmxDevice(&cPluginRpiHdDevice::OnPrimaryDevice);
+ //new cDummyDevice();
+}
+
+cPluginRpiHdDevice::~cPluginRpiHdDevice()
+{
+ delete m_device;
+}
+
+bool cPluginRpiHdDevice::Initialize(void)
+{
+ if (m_device)
+ return (m_device->OmxInit() == 0);
+
+ return true;
+}
+
+bool cPluginRpiHdDevice::Start(void)
+{
+ return true;
+}
+
+void cPluginRpiHdDevice::Stop(void)
+{
+ if (m_device)
+ m_device->OmxDeInit();
+}
+
+VDRPLUGINCREATOR(cPluginRpiHdDevice); // Don't touch this!