mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-18 20:25:25 +00:00
Merging gst-rtsp-server
This commit is contained in:
commit
a9d9189aa2
128 changed files with 73730 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*~
|
||||
/build
|
||||
/_build
|
||||
/b/
|
1
.gitlab-ci.yml
Normal file
1
.gitlab-ci.yml
Normal file
|
@ -0,0 +1 @@
|
|||
include: "https://gitlab.freedesktop.org/gstreamer/gst-ci/raw/master/gitlab/ci_template.yml"
|
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
|||
Wim Taymans <wim.taymans@collabora.co.uk>
|
503
COPYING
Normal file
503
COPYING
Normal file
|
@ -0,0 +1,503 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin St, 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.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
503
COPYING.LIB
Normal file
503
COPYING.LIB
Normal file
|
@ -0,0 +1,503 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin St, 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.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
299
NEWS
Normal file
299
NEWS
Normal file
|
@ -0,0 +1,299 @@
|
|||
GStreamer 1.20 Release Notes
|
||||
|
||||
GStreamer 1.20 has not been released yet. It is scheduled for release
|
||||
around October/November 2021.
|
||||
|
||||
1.19.x is the unstable development version that is being developed in
|
||||
the git main branch and which will eventually result in 1.20, and 1.19.2
|
||||
is the current development release in that series
|
||||
|
||||
It is expected that feature freeze will be in early October 2021,
|
||||
followed by one or two 1.19.9x pre-releases and the new 1.20 stable
|
||||
release around October/November 2021.
|
||||
|
||||
1.20 will be backwards-compatible to the stable 1.18, 1.16, 1.14, 1.12,
|
||||
1.10, 1.8, 1.6,, 1.4, 1.2 and 1.0 release series.
|
||||
|
||||
See https://gstreamer.freedesktop.org/releases/1.20/ for the latest
|
||||
version of this document.
|
||||
|
||||
Last updated: Wednesday 22 September 2021, 18:00 UTC (log)
|
||||
|
||||
Introduction
|
||||
|
||||
The GStreamer team is proud to announce a new major feature release in
|
||||
the stable 1.x API series of your favourite cross-platform multimedia
|
||||
framework!
|
||||
|
||||
As always, this release is again packed with many new features, bug
|
||||
fixes and other improvements.
|
||||
|
||||
Highlights
|
||||
|
||||
- this section will be completed in due course
|
||||
|
||||
Major new features and changes
|
||||
|
||||
Noteworthy new features and API
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
New elements
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
New element features and additions
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Plugin and library moves
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
- There were no plugin moves or library moves in this cycle.
|
||||
|
||||
Plugin removals
|
||||
|
||||
The following elements or plugins have been removed:
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Miscellaneous API additions
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Miscellaneous performance, latency and memory optimisations
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Miscellaneous other changes and enhancements
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Tracing framework and debugging improvements
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Tools
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer RTSP server
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer VAAPI
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer OMX
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer Editing Services and NLE
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer validate
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer Python Bindings
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer C# Bindings
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
GStreamer Rust Bindings and Rust Plugins
|
||||
|
||||
The GStreamer Rust bindings are released separately with a different
|
||||
release cadence that’s tied to gtk-rs, but the latest release has
|
||||
already been updated for the upcoming new GStreamer 1.20 API.
|
||||
|
||||
gst-plugins-rs, the module containing GStreamer plugins written in Rust,
|
||||
has also seen lots of activity with many new elements and plugins.
|
||||
|
||||
What follows is a list of elements and plugins available in
|
||||
gst-plugins-rs, so people don’t miss out on all those potentially useful
|
||||
elements that have no C equivalent.
|
||||
|
||||
- FIXME: add new elements
|
||||
|
||||
Rust audio plugins
|
||||
|
||||
- audiornnoise: New element for audio denoising which implements the
|
||||
noise removal algorithm of the Xiph RNNoise library, in Rust
|
||||
- rsaudioecho: Port of the audioecho element from gst-plugins-good
|
||||
rsaudioloudnorm: Live audio loudness normalization element based on
|
||||
the FFmpeg af_loudnorm filter
|
||||
- claxondec: FLAC lossless audio codec decoder element based on the
|
||||
pure-Rust claxon implementation
|
||||
- csoundfilter: Audio filter that can use any filter defined via the
|
||||
Csound audio programming language
|
||||
- lewtondec: Vorbis audio decoder element based on the pure-Rust
|
||||
lewton implementation
|
||||
|
||||
Rust video plugins
|
||||
|
||||
- cdgdec/cdgparse: Decoder and parser for the CD+G video codec based
|
||||
on a pure-Rust CD+G implementation, used for example by karaoke CDs
|
||||
- cea608overlay: CEA-608 Closed Captions overlay element
|
||||
- cea608tott: CEA-608 Closed Captions to timed-text (e.g. VTT or SRT
|
||||
subtitles) converter
|
||||
- tttocea608: CEA-608 Closed Captions from timed-text converter
|
||||
- mccenc/mccparse: MacCaption Closed Caption format encoder and parser
|
||||
- sccenc/sccparse: Scenarist Closed Caption format encoder and parser
|
||||
- dav1dec: AV1 video decoder based on the dav1d decoder implementation
|
||||
by the VLC project
|
||||
- rav1enc: AV1 video encoder based on the fast and pure-Rust rav1e
|
||||
encoder implementation
|
||||
- rsflvdemux: Alternative to the flvdemux FLV demuxer element from
|
||||
gst-plugins-good, not feature-equivalent yet
|
||||
- rsgifenc/rspngenc: GIF/PNG encoder elements based on the pure-Rust
|
||||
implementations by the image-rs project
|
||||
|
||||
Rust text plugins
|
||||
|
||||
- textwrap: Element for line-wrapping timed text (e.g. subtitles) for
|
||||
better screen-fitting, including hyphenation support for some
|
||||
languages
|
||||
|
||||
Rust network plugins
|
||||
|
||||
- reqwesthttpsrc: HTTP(S) source element based on the Rust
|
||||
reqwest/hyper HTTP implementations and almost feature-equivalent
|
||||
with the main GStreamer HTTP source souphttpsrc
|
||||
- s3src/s3sink: Source/sink element for the Amazon S3 cloud storage
|
||||
- awstranscriber: Live audio to timed text transcription element using
|
||||
the Amazon AWS Transcribe API
|
||||
|
||||
Generic Rust plugins
|
||||
|
||||
- sodiumencrypter/sodiumdecrypter: Encryption/decryption element based
|
||||
on libsodium/NaCl
|
||||
- togglerecord: Recording element that allows to pause/resume
|
||||
recordings easily and considers keyframe boundaries
|
||||
- fallbackswitch/fallbacksrc: Elements for handling potentially
|
||||
failing (network) sources, restarting them on errors/timeout and
|
||||
showing a fallback stream instead
|
||||
- threadshare: Set of elements that provide alternatives for various
|
||||
existing GStreamer elements but allow to share the streaming threads
|
||||
between each other to reduce the number of threads
|
||||
- rsfilesrc/rsfilesink: File source/sink elements as replacements for
|
||||
the existing filesrc/filesink elements
|
||||
|
||||
Build and Dependencies
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
gst-build
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Cerbero
|
||||
|
||||
Cerbero is a meta build system used to build GStreamer plus dependencies
|
||||
on platforms where dependencies are not readily available, such as
|
||||
Windows, Android, iOS and macOS.
|
||||
|
||||
General improvements
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
macOS / iOS
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Windows
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Windows MSI installer
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Linux
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Android
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Platform-specific changes and improvements
|
||||
|
||||
Android
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
macOS and iOS
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Windows
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Linux
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Documentation improvements
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
Possibly Breaking Changes
|
||||
|
||||
- this section will be filled in in due course
|
||||
- MPEG-TS SCTE-35 API changes (FIXME: flesh out)
|
||||
- gst_parse_launch() and friends now error out on non-existing
|
||||
properties on top-level bins where they would silently fail and
|
||||
ignore those before.
|
||||
|
||||
Known Issues
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
- There are a couple of known WebRTC-related regressions/blockers:
|
||||
|
||||
- webrtc: DTLS setup with Chrome is broken
|
||||
- webrtcbin: First keyframe is usually lost
|
||||
|
||||
Contributors
|
||||
|
||||
- this section will be filled in in due course
|
||||
|
||||
… and many others who have contributed bug reports, translations, sent
|
||||
suggestions or helped testing.
|
||||
|
||||
Stable 1.20 branch
|
||||
|
||||
After the 1.20.0 release there will be several 1.20.x bug-fix releases
|
||||
which will contain bug fixes which have been deemed suitable for a
|
||||
stable branch, but no new features or intrusive changes will be added to
|
||||
a bug-fix release usually. The 1.20.x bug-fix releases will be made from
|
||||
the git 1.20 branch, which will be a stable branch.
|
||||
|
||||
1.20.0
|
||||
|
||||
1.20.0 is scheduled to be released around October/November 2021.
|
||||
|
||||
Schedule for 1.22
|
||||
|
||||
Our next major feature release will be 1.22, and 1.21 will be the
|
||||
unstable development version leading up to the stable 1.22 release. The
|
||||
development of 1.21/1.22 will happen in the git main branch.
|
||||
|
||||
The plan for the 1.22 development cycle is yet to be confirmed.
|
||||
|
||||
1.22 will be backwards-compatible to the stable 1.20, 1.18, 1.16, 1.14,
|
||||
1.12, 1.10, 1.8, 1.6, 1.4, 1.2 and 1.0 release series.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
These release notes have been prepared by Tim-Philipp Müller with
|
||||
contributions from …
|
||||
|
||||
License: CC BY-SA 4.0
|
4
README
Normal file
4
README
Normal file
|
@ -0,0 +1,4 @@
|
|||
gst-rtsp-server is a library on top of GStreamer for building an RTSP server
|
||||
|
||||
There are some examples in the examples/ directory and more comprehensive
|
||||
documentation in docs/README.
|
96
RELEASE
Normal file
96
RELEASE
Normal file
|
@ -0,0 +1,96 @@
|
|||
This is GStreamer gst-rtsp-server 1.19.2.
|
||||
|
||||
GStreamer 1.19 is the development branch leading up to the next major
|
||||
stable version which will be 1.20.
|
||||
|
||||
The 1.19 development series adds new features on top of the 1.18 series and is
|
||||
part of the API and ABI-stable 1.x release series of the GStreamer multimedia
|
||||
framework.
|
||||
|
||||
Full release notes will one day be found at:
|
||||
|
||||
https://gstreamer.freedesktop.org/releases/1.20/
|
||||
|
||||
Binaries for Android, iOS, Mac OS X and Windows will usually be provided
|
||||
shortly after the release.
|
||||
|
||||
This module will not be very useful by itself and should be used in conjunction
|
||||
with other GStreamer modules for a complete multimedia experience.
|
||||
|
||||
- gstreamer: provides the core GStreamer libraries and some generic plugins
|
||||
|
||||
- gst-plugins-base: a basic set of well-supported plugins and additional
|
||||
media-specific GStreamer helper libraries for audio,
|
||||
video, rtsp, rtp, tags, OpenGL, etc.
|
||||
|
||||
- gst-plugins-good: a set of well-supported plugins under our preferred
|
||||
license
|
||||
|
||||
- gst-plugins-ugly: a set of well-supported plugins which might pose
|
||||
problems for distributors
|
||||
|
||||
- gst-plugins-bad: a set of plugins of varying quality that have not made
|
||||
their way into one of core/base/good/ugly yet, for one
|
||||
reason or another. Many of these are are production quality
|
||||
elements, but may still be missing documentation or unit
|
||||
tests; others haven't passed the rigorous quality testing
|
||||
we expect yet.
|
||||
|
||||
- gst-libav: a set of codecs plugins based on the ffmpeg library. This is
|
||||
where you can find audio and video decoders and encoders
|
||||
for a wide variety of formats including H.264, AAC, etc.
|
||||
|
||||
- gstreamer-vaapi: hardware-accelerated video decoding and encoding using
|
||||
VA-API on Linux. Primarily for Intel graphics hardware.
|
||||
|
||||
- gst-omx: hardware-accelerated video decoding and encoding, primarily for
|
||||
embedded Linux systems that provide an OpenMax
|
||||
implementation layer such as the Raspberry Pi.
|
||||
|
||||
- gst-rtsp-server: library to serve files or streaming pipelines via RTSP
|
||||
|
||||
- gst-editing-services: library an plugins for non-linear editing
|
||||
|
||||
==== Download ====
|
||||
|
||||
You can find source releases of gstreamer in the download
|
||||
directory: https://gstreamer.freedesktop.org/src/gstreamer/
|
||||
|
||||
The git repository and details how to clone it can be found at
|
||||
https://gitlab.freedesktop.org/gstreamer/
|
||||
|
||||
==== Homepage ====
|
||||
|
||||
The project's website is https://gstreamer.freedesktop.org/
|
||||
|
||||
==== Support and Bugs ====
|
||||
|
||||
We have recently moved from GNOME Bugzilla to GitLab on freedesktop.org
|
||||
for bug reports and feature requests:
|
||||
|
||||
https://gitlab.freedesktop.org/gstreamer
|
||||
|
||||
Please submit patches via GitLab as well, in form of Merge Requests. See
|
||||
|
||||
https://gstreamer.freedesktop.org/documentation/contribute/
|
||||
|
||||
for more details.
|
||||
|
||||
For help and support, please subscribe to and send questions to the
|
||||
gstreamer-devel mailing list (see below for details).
|
||||
|
||||
There is also a #gstreamer IRC channel on the Freenode IRC network.
|
||||
|
||||
==== Developers ====
|
||||
|
||||
GStreamer source code repositories can be found on GitLab on freedesktop.org:
|
||||
|
||||
https://gitlab.freedesktop.org/gstreamer
|
||||
|
||||
and can also be cloned from there and this is also where you can submit
|
||||
Merge Requests or file issues for bugs or feature requests.
|
||||
|
||||
Interested developers of the core library, plugins, and applications should
|
||||
subscribe to the gstreamer-devel list:
|
||||
|
||||
https://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
|
3
REQUIREMENTS
Normal file
3
REQUIREMENTS
Normal file
|
@ -0,0 +1,3 @@
|
|||
You need to have GStreamer. You can use an installed version of
|
||||
GStreamer or from its build dir.
|
||||
|
3
TODO
Normal file
3
TODO
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
- use a config file to configure the server
|
||||
- error recovery
|
498
docs/README
Normal file
498
docs/README
Normal file
|
@ -0,0 +1,498 @@
|
|||
README
|
||||
------
|
||||
|
||||
(Last updated on Mon 15 jul 2013, version 0.11.90.1)
|
||||
|
||||
This HOWTO describes the basic usage of the GStreamer RTSP libraries and how you
|
||||
can build simple server applications with it.
|
||||
|
||||
* General
|
||||
|
||||
The server relies heavily on the RTSP infrastructure of GStreamer. This includes
|
||||
all of the media acquisition, decoding, encoding, payloading and UDP/TCP
|
||||
streaming. We use the rtpbin element for all the session management. Most of
|
||||
the RTSP message parsing and construction in the server is done using the RTSP
|
||||
library that comes with gst-plugins-base.
|
||||
|
||||
The result is that the server is rather small (a few 11000 lines of code) and easy
|
||||
to understand and extend. In its current state of development, things change
|
||||
fast, API and ABI are unstable. We encourage people to use it for their various
|
||||
use cases and participate by suggesting changes/features.
|
||||
|
||||
Most of the server is built as a library containing a bunch of GObject objects
|
||||
that provide reasonable default functionality but has a fair amount of hooks
|
||||
to override the default behaviour.
|
||||
|
||||
The server currently integrates with the glib mainloop nicely. It's currently
|
||||
not meant to be used in high-load scenarios and because no security audit has
|
||||
been done, you should probably not put it on a public IP address.
|
||||
|
||||
* Initialisation
|
||||
|
||||
You need to initialize GStreamer before using any of the RTSP server functions.
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
The server itself currently does not have any specific initialisation function
|
||||
but that might change in the future.
|
||||
|
||||
|
||||
* Creating the server
|
||||
|
||||
The first thing you want to do is create a new GstRTSPServer object. This object
|
||||
will handle all the new client connections to your server once it is added to a
|
||||
GMainLoop. You can create a new server object like this:
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
GstRTSPServer *server;
|
||||
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
The server will by default listen on port 8554 for new connections. This can be
|
||||
changed by calling gst_rtsp_server_set_service() or with the 'service' GObject
|
||||
property. This makes it possible to run multiple server instances listening on
|
||||
multiple ports on one machine.
|
||||
|
||||
We can make the server start listening on its default port by attaching it to a
|
||||
mainloop. The following example shows how this is done and will start a server
|
||||
on the default 8554 port. For any request we make, we will get a NOT_FOUND
|
||||
error code because we need to configure more things before the server becomes
|
||||
useful.
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GstRTSPServer *server;
|
||||
GMainLoop *loop;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* make a mainloop for the default context */
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_main_loop_run (loop);
|
||||
}
|
||||
|
||||
The server manages four other objects: GstRTSPSessionPool,
|
||||
GstRTSPMountPoints, GstRTSPAuth and GstRTSPThreadPool.
|
||||
|
||||
The GstRTSPSessionPool is an object that keeps track of all the active sessions
|
||||
in the server. A session will usually be kept for each client that performed a
|
||||
SETUP request for a certain media stream. It contains the configuration that
|
||||
the client negotiated with the server to receive the particular stream, ie. the
|
||||
transport used and port pairs for UDP along with the state of the streaming.
|
||||
The default implementation of the session pool is usually sufficient but
|
||||
alternative implementation can be used by the server.
|
||||
|
||||
The GstRTSPMountPoints object is more interesting and needs more configuration
|
||||
before the server object is useful. This object manages the mapping from a
|
||||
request URL to a specific stream and its configuration. We explain in the next
|
||||
topic how to configure this object.
|
||||
|
||||
GstRTSPAuth is an object that authenticates users and authorizes actions
|
||||
performed by users. By default, a server does not have a GstRTSPAuth object and
|
||||
thus does not try to perform any authentication or authorization.
|
||||
|
||||
GstRTSPThreadPool manages the threads used for client connections and media
|
||||
pipelines. The server has a default implementation of a threadpool that should
|
||||
be sufficient in most cases.
|
||||
|
||||
|
||||
* Making url mount points
|
||||
|
||||
Next we need to define what media is attached to a particular URL. What we want
|
||||
to achieve is that when the user asks our server for a specific URL, say /test,
|
||||
that we create (or reuse) a GStreamer pipeline that produces one or more RTP
|
||||
streams.
|
||||
|
||||
The object that can create such pipeline is called a GstRTSPMediaFactory object.
|
||||
The default implementation of GstRTSPMediaFactory allows you to easily create
|
||||
GStreamer pipelines using the gst-launch syntax. It is possible to create a
|
||||
GstRTSPMediaFactory subclass that uses different methods for constructing
|
||||
pipelines.
|
||||
|
||||
The default GstRTSPMediaFactory can be configured with a gst-launch line that
|
||||
produces a toplevel bin (use '(' and ')' around the pipeline description to
|
||||
force a toplevel GstBin instead of the default GstPipeline toplevel element).
|
||||
The pipeline description should contain elements named payN, one for each
|
||||
stream (ex. pay0, pay1, ...). Also, for increased compatibility each stream
|
||||
should have a different payload type which can be configured on the payloader.
|
||||
|
||||
The following code snippet illustrates how to create a media factory that
|
||||
creates an RTP feed of an H264 encoded test video signal.
|
||||
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( videotestsrc ! x264enc ! rtph264pay pt=96 name=pay0 )");
|
||||
|
||||
Now that we have the media factory, we can attach it to a specific url. To do
|
||||
this we get the default GstRTSPMountPoints from our server and add the url to
|
||||
factory mount points to it like this:
|
||||
|
||||
GstRTSPMountPoints *mounts;
|
||||
|
||||
...create server..create factory..
|
||||
|
||||
/* get the default mount points from the server */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* attach the video test signal to the "/test" URL */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
g_object_unref (mounts);
|
||||
|
||||
When starting the server now and directing an RTP client to the URL (like with
|
||||
vlc, mplayer or gstreamer):
|
||||
|
||||
rtsp://localhost:8554/test
|
||||
|
||||
a test signal will be streamed to the client. The full example code can be
|
||||
found in the examples/test-readme.c file.
|
||||
|
||||
Note that by default the factory will create a new pipeline for each client. If
|
||||
you want to share a pipeline between clients, use
|
||||
gst_rtsp_media_factory_set_shared().
|
||||
|
||||
|
||||
* more on GstRTSPMediaFactory
|
||||
|
||||
The GstRTSPMediaFactory is responsible for creating and caching GstRTSPMedia
|
||||
objects.
|
||||
|
||||
A freshly created GstRTSPMedia object from the factory initially only contains a
|
||||
GstElement containing the elements to produce the RTP streams for the media and
|
||||
a GPtrArray of GstRTSPStream objects describing the payloader and its source
|
||||
pad. The media is unprepared in this state.
|
||||
|
||||
Usually the url will determine what kind of pipeline should be created. You can
|
||||
for example use query parameters to configure certain parts of the pipeline or
|
||||
select encoders and payloaders based on some url pattern.
|
||||
|
||||
When dealing with a live stream from, for example, a webcam, it can be
|
||||
interesting to share the pipeline with multiple clients. This must be done when
|
||||
only one instance of the video capture element can be used at a time. In this
|
||||
case, the shared property of GstRTSPMedia must be used to instruct the default
|
||||
GstRTSPMediaFactory implementation to cache the media.
|
||||
|
||||
When all objects created from a factory can be shared, you can set the shared
|
||||
property directly on the factory.
|
||||
|
||||
* more on GstRTSPMedia
|
||||
|
||||
After creating the GstRTSPMedia object from the factory, it can be prepared
|
||||
with gst_rtsp_media_prepare(). This method will put those objects in a
|
||||
GstPipeline and will construct and link the streaming elements and the
|
||||
rtpbin session manager object.
|
||||
|
||||
The _prepare() method will then preroll the pipeline in order to figure out the
|
||||
caps on the payloaders. After the GstRTSPMedia prerolled it will be in the
|
||||
prepared state and can be used for creating SDP files or for streaming to
|
||||
clients.
|
||||
|
||||
The prepare method will also create 2 UDP ports for each stream that can be
|
||||
used for sending and receiving RTP/RTCP from clients. These port numbers will
|
||||
have to be negotiated with the client in the SETUP requests.
|
||||
|
||||
When preparing a GstRTSPMedia, an appsink and asppsrc is also constructed
|
||||
for streaming the stream over TCP when requested.
|
||||
|
||||
Media is prepared by the server when DESCRIBE or SETUP requests are received
|
||||
from the client.
|
||||
|
||||
|
||||
* the GstRTSPClient object
|
||||
|
||||
When a server detects a new client connection on its port, it will accept the
|
||||
connection, check if the connection is allowed and then call the vmethod
|
||||
create_client. The default implementation of this function will create
|
||||
a new GstRTCPClient object, will configure the session pool, mount points,
|
||||
auth and thread pool objects in it.
|
||||
|
||||
The server will then attach the new client to a server mainloop to let it
|
||||
handle further communication with the client. In RTSP it is usual to keep
|
||||
the connection open between multiple RTSP requests. The client watch will
|
||||
be dispatched by the server mainloop when a new GstRTSPMessage is received,
|
||||
which will then be handled and a response will be sent.
|
||||
|
||||
The GstRTSPClient object remains alive for as long as a client has a TCP
|
||||
connection open with the server. Since is possible for a client to open and close
|
||||
the TCP connection between requests, we cannot store the state related
|
||||
to the configured RTSP session in the GstRTSPClient object. This server state
|
||||
is instead stored in the GstRTSPSession object, identified with the session
|
||||
id.
|
||||
|
||||
|
||||
* GstRTSPSession
|
||||
|
||||
This object contains state about a specific RTSP session identified with a
|
||||
session id. This state contains the configured streams and their associated
|
||||
transports.
|
||||
|
||||
When a GstRTSPClient performs a SETUP request, the server will allocate a new
|
||||
GstRTSPSession with a unique session id from the GstRTSPSessionPool. The pool
|
||||
maintains a list of all existing sessions and makes sure that no session id is
|
||||
used multiple times. The session id is sent to the client so that the client
|
||||
can refer to its previously configured state by sending the session id in
|
||||
further requests.
|
||||
|
||||
A client will then use the session id to configure one or more
|
||||
GstRTSPSessionMedia objects, identified by their url. This SessionMedia object
|
||||
contains the configuration of a GstRTSPMedia and its configured
|
||||
GstRTSPStreamTransport.
|
||||
|
||||
|
||||
* GstRTSPSessionMedia and GstRTSPStreamTransport
|
||||
|
||||
A GstRTSPSessionMedia is identified by a URL and is referenced by a
|
||||
GstRTSPSession. It is created as soon as a client performs a SETUP operation on
|
||||
a particular URL. It will contain a link to the GstRTSPMedia object associated
|
||||
with the URL along with the state of the media and the configured transports
|
||||
for each of the streams in the media.
|
||||
|
||||
Each SETUP request performed by the client will configure a
|
||||
GstRTSPStreamTransport object linked to by the GstRTSPSessionMedia structure.
|
||||
It will contain the transport information needed to send this stream to the
|
||||
client. The GstRTSPStreamTransport also contains a link to the GstRTSPStream
|
||||
object that generates the actual data to be streamed to the client.
|
||||
|
||||
Note how GstRTSPMedia and GstRTSPStream (the providers of the data to
|
||||
stream) are decoupled from GstRTSPSessionMedia and GstRTSPStreamTransport (the
|
||||
configuration of how to send this stream to a client) in order to be able to
|
||||
send the data of one GstRTSPMedia to multiple clients.
|
||||
|
||||
|
||||
* media control
|
||||
|
||||
After a client has configured the transports for a GstRTSPMedia and its
|
||||
GstRTSPStreams, the client can play/pause/stop the stream.
|
||||
|
||||
The GstRTSPMedia object was prepared in the DESCRIBE call (or during SETUP when
|
||||
the client skipped the DESCRIBE request). As seen earlier, this configures a
|
||||
couple of udpsink and udpsrc elements to respectively send and receive the
|
||||
media to clients.
|
||||
|
||||
When a client performs a PLAY request, its configured destination UDP ports are
|
||||
added to the GstRTSPStream target destinations, at which point data will
|
||||
be sent to the client. The corresponding GstRTSPMedia object will be set to the
|
||||
PLAYING state if it was not already in order to send the data to the
|
||||
destination.
|
||||
|
||||
The server needs to prepare an RTP-Info header field in the PLAY response,
|
||||
which consists of the sequence number and the RTP timestamp of the next RTP
|
||||
packet. In order to achive this, the server queries the payloaders for this
|
||||
information when it prerolled the pipeline.
|
||||
|
||||
When a client performs a PAUSE request, the destination UDP ports are removed
|
||||
from the GstRTSPStream object and the GstRTSPMedia object is set to PAUSED
|
||||
if no other destinations are configured anymore.
|
||||
|
||||
|
||||
* seeking
|
||||
|
||||
A seek is performed when a client sends a Range header in the PLAY request.
|
||||
This only works when not dealing with shared (live) streams.
|
||||
|
||||
The server performs a GStreamer flushing seek on the media, waits for the
|
||||
pipeline to preroll again and then responds to the client after collecting the
|
||||
new RTP sequence number and timestamp from the payloaders.
|
||||
|
||||
|
||||
* session management
|
||||
|
||||
The server has to react to clients that suddenly disappear because of network
|
||||
problems or otherwise. It needs to make sure that it can reasonably free the
|
||||
resources that are used by the various objects in use for streaming when the
|
||||
client appears to be gone.
|
||||
|
||||
Each of the GstRTSPSession objects managed by a GstRTSPSessionPool has
|
||||
therefore a last_access field that contains the timestamp of when activity from
|
||||
a client was last recorded.
|
||||
|
||||
Various ways exist to detect activity from a client:
|
||||
|
||||
- RTSP keepalive requests. When a client is receiving RTP data, the RTSP TCP
|
||||
connection is largely unused. It is the client's responsibility to
|
||||
periodically send keep-alive requests over the TCP channel.
|
||||
|
||||
Whenever a keep-alive request is received by the server (any request that
|
||||
contains a session id, usually an OPTION or GET_PARAMETER request) the
|
||||
last_access of the session is updated.
|
||||
|
||||
- Since it is not required for a client to keep the RTSP TCP connection open
|
||||
while streaming, gst-rtsp-server also detects activity from clients by
|
||||
looking at the RTCP messages it receives.
|
||||
|
||||
When an RTCP message is received from a client, the server looks in its list
|
||||
of active ports if this message originates from a known host/port pair that
|
||||
is currently active in a GstRTSPSession. If this is the case, the session is
|
||||
kept alive.
|
||||
|
||||
Since the server does not know anything about the port number that will be
|
||||
used by the client to send RTCP, this method does not always work. Later
|
||||
RTSP RFCs will include support for negotiating this port number with the
|
||||
server. Most clients however use the same port number for sending and
|
||||
receiving RTCP exactly for this reason.
|
||||
|
||||
If there was no activity in a particular session for a long time (by default 60
|
||||
seconds), the application should remove the session from the pool. For this,
|
||||
the application should periodically (say every 2 seconds) check if no sessions
|
||||
expired and call gst_rtsp_session_pool_cleanup() to remove them.
|
||||
|
||||
When a session is removed from the sessionpool and its last reference is
|
||||
unreffed, all related objects and media are destroyed as if a TEARDOWN happened
|
||||
from the client.
|
||||
|
||||
|
||||
* TEARDOWN
|
||||
|
||||
A TEARDOWN request will first locate the GstRTSPSessionMedia of the URL. It
|
||||
will then remove all transports from the streams, making sure that streaming
|
||||
stops to the clients. It will then remove the GstRTSPSessionMedia and
|
||||
GstRTSPStreamTransport objects. Finally the GstRTSPSession is released back
|
||||
into the pool.
|
||||
|
||||
When there are no more references to the GstRTSPMedia, the media pipeline is
|
||||
shut down (with _unprepare) and destroyed. This will then also destroy the
|
||||
GstRTSPStream objects.
|
||||
|
||||
|
||||
* Security
|
||||
|
||||
The security of the server and the policy is implemented in a GstRTSPAuth
|
||||
object. The object is reponsible for:
|
||||
|
||||
- authenticate the user of the server.
|
||||
|
||||
- check if the current user is authorized to perform an operation.
|
||||
|
||||
For critical operations, the server will call gst_rtsp_auth_check() with
|
||||
a string describing the operation which should be validated. The installed
|
||||
GstRTSPAuth object is then responsible for checking if the operation is
|
||||
allowed.
|
||||
|
||||
Implementations of GstRTSPAuth objects can use the following infrastructure
|
||||
bits of the rtsp server to implement these checks:
|
||||
|
||||
- GstRTSPToken: a generic structure describing roles and permissions granted
|
||||
to a user.
|
||||
|
||||
- GstRTSPPermissions: a generic list of roles and matching permissions. These
|
||||
can be attached to media and factories currently.
|
||||
|
||||
An Auth implementation will usually authenticate a user, using a method such as
|
||||
Basic authentication or client certificates or perhaps simply use the IP address.
|
||||
The result of the authentication of the user will be a GstRTSPToken that is made
|
||||
current in the context of the ongoing request.
|
||||
|
||||
The auth module can then implement the various checks in the server by looking
|
||||
at the current token and, if needed, compare it to the required GstRTSPPermissions
|
||||
of the current object.
|
||||
|
||||
The security is deliberately kept generic with a default implementation of the
|
||||
GstRTSPAuth object providing a usable and simple implementaion. To make more
|
||||
complicated security modules, the auth object should be subclassed and new
|
||||
implementations for the checks needs to be made.
|
||||
|
||||
|
||||
Objects
|
||||
-------
|
||||
|
||||
GstRTSPServer
|
||||
- Toplevel object listening for connections and creating new
|
||||
GstRTSPClient objects
|
||||
|
||||
GstRTSPClient
|
||||
- Handle RTSP Requests from connected clients. All other objects
|
||||
are called by this object.
|
||||
|
||||
GstRTSPContext
|
||||
- Helper structure containing the current state of the request
|
||||
handled by the client.
|
||||
|
||||
|
||||
GstRTSPMountPoints
|
||||
- Maps a url to a GstRTSPMediaFactory implementation. The default
|
||||
implementation uses a simple hashtable to map a url to a factory.
|
||||
|
||||
GstRTSPMediaFactory
|
||||
- Creates and caches GstRTSPMedia objects. The default implementation
|
||||
can create GstRTSPMedia objects based on gst-launch syntax.
|
||||
|
||||
GstRTSPMediaFactoryURI
|
||||
- Specialized GstRTSPMediaFactory that can stream the content of any
|
||||
URI.
|
||||
|
||||
GstRTSPMedia
|
||||
- The object that contains the media pipeline and various GstRTSPStream
|
||||
objects that produce RTP packets
|
||||
|
||||
GstRTSPStream
|
||||
- Manages the elements to stream a stream of a GstRTSPMedia to one or
|
||||
more GstRTSPStreamTransports.
|
||||
|
||||
|
||||
GstRTSPSessionPool
|
||||
- Creates and manages GstRTSPSession objects identified by an id.
|
||||
|
||||
GstRTSPSession
|
||||
- An object containing the various GstRTSPSessionMedia objects managed
|
||||
by this session.
|
||||
|
||||
GstRTSPSessionMedia
|
||||
- The state of a GstRTSPMedia and the configuration of a GstRTSPStream
|
||||
objects. The configuration for the GstRTSPStream is stored in
|
||||
GstRTSPStreamTransport objects.
|
||||
|
||||
GstRTSPStreamTransport
|
||||
- Configuration of how a GstRTSPStream is send to a particular client. It
|
||||
contains the transport that was negotiated with the client in the SETUP
|
||||
request.
|
||||
|
||||
|
||||
GstRTSPSDP
|
||||
- helper functions for creating SDP messages from gstRTSPMedia
|
||||
|
||||
GstRTSPAddressPool
|
||||
- a pool of multicast and unicast addresses used in streaming
|
||||
|
||||
GstRTSPThreadPool
|
||||
- a pool of threads used for various server tasks such as handling clients and
|
||||
managing media pipelines.
|
||||
|
||||
|
||||
GstRTSPAuth
|
||||
- Hooks for checking authorizations, all client activity will call this
|
||||
object with the GstRTSPContext structure. By default it supports
|
||||
basic authentication.
|
||||
|
||||
GstRTSPToken
|
||||
- Credentials of a user. This contrains the roles that the user is allowed
|
||||
to assume and other permissions or capabilities of the user.
|
||||
|
||||
GstRTSPPermissions
|
||||
- A list of permissions for each role. The permissions are usually attached
|
||||
to objects to describe what roles have what permissions.
|
||||
|
||||
GstRTSPParams
|
||||
- object to handle get and set parameter requests.
|
||||
|
35
docs/design/gst-rtp-server-design
Normal file
35
docs/design/gst-rtp-server-design
Normal file
|
@ -0,0 +1,35 @@
|
|||
RTSP server
|
||||
-----------
|
||||
|
||||
This directory contains an example RTSP server built with various GStreamer
|
||||
components and libraries. It also uses GStreamer for all of the multimedia
|
||||
procesing and RTP bits. The following features are implemented:
|
||||
|
||||
-
|
||||
|
||||
Server Design
|
||||
-------------
|
||||
|
||||
The toplevel component of the server is a GstRTSPServer object. This object
|
||||
creates and binds on the server socket and attaches into the mainloop.
|
||||
|
||||
For each request a new GstRTSPClient object is created that will accept the
|
||||
request and a thread is started to handle further communication with the
|
||||
client until the connection is closed.
|
||||
|
||||
When a client issues a SETUP request we create a GstRTSPSession object,
|
||||
identified with a sessionid, that will keep track of the state of a client.
|
||||
The object is destroyed when a TEARDOWN request is made for that sessionid.
|
||||
|
||||
We also maintain a pool of URL to media pipeline mappings. Each url is mapped to
|
||||
an object that is able to provide a pipeline for that media. We provide
|
||||
pipelines to stream live captured data, on-demand file streaming or on-demand
|
||||
transcoding of a file or stream.
|
||||
|
||||
A pool of currently active pipelines is also maintained. Usually the active
|
||||
pipelines are in use by one or more GstRTSPSession objects. An active pipeline
|
||||
becomes inactive when no more sessions refer to it.
|
||||
|
||||
A client can choose to start a new pipeline or join a currently active pipeline.
|
||||
Some active pipeline cannot be joined (such as on-demand streams) but a new
|
||||
instance of that pipeline can be created.
|
503
docs/gst_plugins_cache.json
Normal file
503
docs/gst_plugins_cache.json
Normal file
|
@ -0,0 +1,503 @@
|
|||
{
|
||||
"rtspclientsink": {
|
||||
"description": "RTSP client sink element",
|
||||
"elements": {
|
||||
"rtspclientsink": {
|
||||
"author": "Jan Schmidt <jan@centricular.com>",
|
||||
"description": "Send data over the network via RTSP RECORD(RFC 2326)",
|
||||
"hierarchy": [
|
||||
"GstRTSPClientSink",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy",
|
||||
"GstURIHandler"
|
||||
],
|
||||
"klass": "Sink/Network",
|
||||
"long-name": "RTSP RECORD client",
|
||||
"pad-templates": {
|
||||
"sink_%%u": {
|
||||
"caps": "ANY",
|
||||
"direction": "sink",
|
||||
"presence": "request",
|
||||
"type": "GstRtspClientSinkPad"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"debug": {
|
||||
"blurb": "Dump request and response messages to stdout",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "false",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gboolean",
|
||||
"writable": true
|
||||
},
|
||||
"do-rtsp-keep-alive": {
|
||||
"blurb": "Send RTSP keep alive packets, disable for old incompatible server.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "true",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gboolean",
|
||||
"writable": true
|
||||
},
|
||||
"latency": {
|
||||
"blurb": "Amount of ms to buffer",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "2000",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"location": {
|
||||
"blurb": "Location of the RTSP url to read",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"multicast-iface": {
|
||||
"blurb": "The network interface on which to join the multicast group",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"ntp-time-source": {
|
||||
"blurb": "NTP time source for RTCP packets",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "ntp (0)",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstRTSPClientSinkNtpTimeSource",
|
||||
"writable": true
|
||||
},
|
||||
"port-range": {
|
||||
"blurb": "Client port range that can be used to receive RTCP data, eg. 3000-3005 (NULL = no restrictions)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"profiles": {
|
||||
"blurb": "Allowed RTSP profiles",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "avp",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstRTSPProfile",
|
||||
"writable": true
|
||||
},
|
||||
"protocols": {
|
||||
"blurb": "Allowed lower transport protocols",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "tcp+udp-mcast+udp",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstRTSPLowerTrans",
|
||||
"writable": true
|
||||
},
|
||||
"proxy": {
|
||||
"blurb": "Proxy settings for HTTP tunneling. Format: [http://][user:passwd@]host[:port]",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"proxy-id": {
|
||||
"blurb": "HTTP proxy URI user id for authentication",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"proxy-pw": {
|
||||
"blurb": "HTTP proxy URI user password for authentication",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"retry": {
|
||||
"blurb": "Max number of retries when allocating RTP ports.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "20",
|
||||
"max": "65535",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"rtp-blocksize": {
|
||||
"blurb": "RTP package size to suggest to server (0 = disabled)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "65536",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"rtx-time": {
|
||||
"blurb": "Amount of ms to buffer for retransmission. 0 disables retransmission",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "500",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"sdes": {
|
||||
"blurb": "The SDES items of this session",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstStructure",
|
||||
"writable": true
|
||||
},
|
||||
"tcp-timeout": {
|
||||
"blurb": "Fail after timeout microseconds on TCP connections (0 = disabled)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "20000000",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"timeout": {
|
||||
"blurb": "Retry TCP transport after UDP timeout microseconds (0 = disabled)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "5000000",
|
||||
"max": "18446744073709551615",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint64",
|
||||
"writable": true
|
||||
},
|
||||
"tls-database": {
|
||||
"blurb": "TLS database with anchor certificate authorities used to validate the server certificate",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GTlsDatabase",
|
||||
"writable": true
|
||||
},
|
||||
"tls-interaction": {
|
||||
"blurb": "A GTlsInteraction object to prompt the user for password or certificate",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GTlsInteraction",
|
||||
"writable": true
|
||||
},
|
||||
"tls-validation-flags": {
|
||||
"blurb": "TLS certificate validation flags used to validate the server certificate",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "validate-all",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GTlsCertificateFlags",
|
||||
"writable": true
|
||||
},
|
||||
"udp-buffer-size": {
|
||||
"blurb": "Size of the kernel UDP receive buffer in bytes, 0=default",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "524288",
|
||||
"max": "2147483647",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gint",
|
||||
"writable": true
|
||||
},
|
||||
"udp-reconnect": {
|
||||
"blurb": "Reconnect to the server if RTSP connection is closed when doing UDP",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "true",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gboolean",
|
||||
"writable": true
|
||||
},
|
||||
"user-agent": {
|
||||
"blurb": "The User-Agent string to send to the server",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "GStreamer/1.19.2",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"user-id": {
|
||||
"blurb": "RTSP location URI user id for authentication",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"user-pw": {
|
||||
"blurb": "RTSP location URI user password for authentication",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "NULL",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
}
|
||||
},
|
||||
"rank": "none",
|
||||
"signals": {
|
||||
"accept-certificate": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "GTlsConnection"
|
||||
},
|
||||
{
|
||||
"name": "arg1",
|
||||
"type": "GTlsCertificate"
|
||||
},
|
||||
{
|
||||
"name": "arg2",
|
||||
"type": "GTlsCertificateFlags"
|
||||
}
|
||||
],
|
||||
"return-type": "gboolean",
|
||||
"when": "last"
|
||||
},
|
||||
"handle-request": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "GstRTSPMessage"
|
||||
},
|
||||
{
|
||||
"name": "arg1",
|
||||
"type": "GstRTSPMessage"
|
||||
}
|
||||
],
|
||||
"return-type": "void"
|
||||
},
|
||||
"new-manager": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "GstElement"
|
||||
}
|
||||
],
|
||||
"return-type": "void",
|
||||
"when": "first"
|
||||
},
|
||||
"new-payloader": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "GstElement"
|
||||
}
|
||||
],
|
||||
"return-type": "void",
|
||||
"when": "first"
|
||||
},
|
||||
"request-rtcp-key": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "guint"
|
||||
}
|
||||
],
|
||||
"return-type": "GstCaps",
|
||||
"when": "last"
|
||||
},
|
||||
"update-sdp": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "GstSDPMessage"
|
||||
}
|
||||
],
|
||||
"return-type": "void"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filename": "gstrtspclientsink",
|
||||
"license": "LGPL",
|
||||
"other-types": {
|
||||
"GstRTSPClientSinkNtpTimeSource": {
|
||||
"kind": "enum",
|
||||
"values": [
|
||||
{
|
||||
"desc": "NTP time based on realtime clock",
|
||||
"name": "ntp",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"desc": "UNIX time based on realtime clock",
|
||||
"name": "unix",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"desc": "Running time based on pipeline clock",
|
||||
"name": "running-time",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"desc": "Pipeline clock time",
|
||||
"name": "clock-time",
|
||||
"value": "3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"GstRtspClientSinkPad": {
|
||||
"hierarchy": [
|
||||
"GstRtspClientSinkPad",
|
||||
"GstGhostPad",
|
||||
"GstProxyPad",
|
||||
"GstPad",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"kind": "object",
|
||||
"properties": {
|
||||
"payloader": {
|
||||
"blurb": "The payloader element to use (NULL = default automatically selected)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstElement",
|
||||
"writable": true
|
||||
},
|
||||
"ulpfec-percentage": {
|
||||
"blurb": "The percentage of ULP redundancy to apply",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "0",
|
||||
"max": "100",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"package": "GStreamer RTSP Server Library",
|
||||
"source": "gst-rtsp-server",
|
||||
"tracers": {},
|
||||
"url": "Unknown package origin"
|
||||
}
|
||||
}
|
1
docs/index.md
Normal file
1
docs/index.md
Normal file
|
@ -0,0 +1 @@
|
|||
# GStreamer RTSP Server
|
99
docs/meson.build
Normal file
99
docs/meson.build
Normal file
|
@ -0,0 +1,99 @@
|
|||
build_hotdoc = false
|
||||
|
||||
if meson.is_cross_build()
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but building the doc while cross building is not supported yet.')
|
||||
endif
|
||||
|
||||
message('Documentation not built as building it while cross building is not supported yet.')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
required_hotdoc_extensions = ['gi-extension', 'gst-extension']
|
||||
if gst_dep.type_name() == 'internal'
|
||||
gst_proj = subproject('gstreamer')
|
||||
plugins_cache_generator = gst_proj.get_variable('plugins_cache_generator')
|
||||
else
|
||||
required_hotdoc_extensions += ['gst-extension']
|
||||
plugins_cache_generator = find_program(join_paths(gst_dep.get_pkgconfig_variable('libexecdir'), 'gstreamer-' + api_version, 'gst-plugins-doc-cache-generator'),
|
||||
required: false)
|
||||
endif
|
||||
|
||||
plugins_cache = join_paths(meson.current_source_dir(), 'gst_plugins_cache.json')
|
||||
if plugins.length() == 0
|
||||
message('All rtsp-server plugins have been disabled')
|
||||
elif plugins_cache_generator.found()
|
||||
plugins_doc_dep = custom_target('rtsp-server-plugins-doc-cache',
|
||||
command: [plugins_cache_generator, plugins_cache, '@OUTPUT@', '@INPUT@'],
|
||||
input: plugins,
|
||||
output: 'gst_plugins_cache.json',
|
||||
)
|
||||
else
|
||||
warning('GStreamer plugin inspector for documentation not found, can\'t update the cache')
|
||||
endif
|
||||
|
||||
hotdoc_p = find_program('hotdoc', required: get_option('doc'))
|
||||
if not hotdoc_p.found()
|
||||
message('Hotdoc not found, not building the documentation')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
hotdoc_req = '>= 0.11.0'
|
||||
hotdoc_version = run_command(hotdoc_p, '--version').stdout()
|
||||
if not hotdoc_version.version_compare(hotdoc_req)
|
||||
if get_option('doc').enabled()
|
||||
error('Hotdoc version @0@ not found, got @1@'.format(hotdoc_req, hotdoc_version))
|
||||
else
|
||||
message('Hotdoc version @0@ not found, got @1@'.format(hotdoc_req, hotdoc_version))
|
||||
subdir_done()
|
||||
endif
|
||||
endif
|
||||
|
||||
hotdoc = import('hotdoc')
|
||||
foreach extension: required_hotdoc_extensions
|
||||
if not hotdoc.has_extensions(extension)
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but @0@ missing'.format(extension))
|
||||
endif
|
||||
|
||||
message('@0@ extension not found, not building documentation'.format(extension))
|
||||
subdir_done()
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if not build_gir
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but introspection not built.')
|
||||
endif
|
||||
|
||||
message('Introspection not built, can\'t build the documentation')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
build_hotdoc = true
|
||||
hotdoc = import('hotdoc')
|
||||
|
||||
libs_doc = [hotdoc.generate_doc('gst-rtsp-server',
|
||||
project_version: api_version,
|
||||
gi_c_sources: ['../gst/rtsp-server/*.[hc]'],
|
||||
gi_sources: rtsp_server_gir[0].full_path(),
|
||||
sitemap: 'sitemap.txt',
|
||||
index: 'index.md',
|
||||
gi_index: 'index.md',
|
||||
gi_smart_index: true,
|
||||
gi_order_generated_subpages: true,
|
||||
)]
|
||||
|
||||
plugins_doc = [hotdoc.generate_doc('rtspclientsink',
|
||||
project_version: api_version,
|
||||
sitemap: 'plugin-sitemap.txt',
|
||||
index: 'plugin-index.md',
|
||||
gst_index: 'plugin-index.md',
|
||||
gst_c_sources: ['../gst/rtsp-sink/*.[ch]'],
|
||||
gst_dl_sources: [rtspsink.full_path()],
|
||||
gst_smart_index: true,
|
||||
dependencies: gst_rtsp_server_deps + [rtspsink],
|
||||
gst_cache_file: plugins_cache,
|
||||
gst_plugin_name: 'rtspclientsink',
|
||||
)]
|
||||
doc = libs_doc[0]
|
1
docs/plugin-index.md
Normal file
1
docs/plugin-index.md
Normal file
|
@ -0,0 +1 @@
|
|||
# rtspclientsink
|
1
docs/plugin-sitemap.txt
Normal file
1
docs/plugin-sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gst-index
|
2
docs/sitemap.md
Normal file
2
docs/sitemap.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
gi-index
|
||||
gst-index
|
1
docs/sitemap.txt
Normal file
1
docs/sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gi-index
|
40
examples/meson.build
Normal file
40
examples/meson.build
Normal file
|
@ -0,0 +1,40 @@
|
|||
examples = [
|
||||
'test-appsrc',
|
||||
'test-appsrc2',
|
||||
'test-auth',
|
||||
'test-auth-digest',
|
||||
'test-launch',
|
||||
'test-mp4',
|
||||
'test-multicast2',
|
||||
'test-multicast',
|
||||
'test-netclock',
|
||||
'test-netclock-client',
|
||||
'test-ogg',
|
||||
'test-onvif-client',
|
||||
'test-onvif-server',
|
||||
'test-readme',
|
||||
'test-record-auth',
|
||||
'test-record',
|
||||
'test-replay-server',
|
||||
'test-sdp',
|
||||
'test-uri',
|
||||
'test-video',
|
||||
'test-video-rtx',
|
||||
]
|
||||
|
||||
foreach example : examples
|
||||
executable(example, '@0@.c'.format(example),
|
||||
c_args : rtspserver_args,
|
||||
include_directories : rtspserver_incs,
|
||||
dependencies : [glib_dep, gst_dep, gstapp_dep, gstnet_dep, gst_rtsp_server_dep],
|
||||
install: false)
|
||||
endforeach
|
||||
|
||||
cgroup_dep = dependency('libcgroup', version : '>= 0.26', required : false)
|
||||
if cgroup_dep.found()
|
||||
executable('test-cgroups', 'test-cgroups.c',
|
||||
c_args : rtspserver_args,
|
||||
include_directories : rtspserver_incs,
|
||||
dependencies : [glib_dep, gst_dep, gstnet_dep, gst_rtsp_server_dep, cgroup_dep],
|
||||
install: false)
|
||||
endif
|
140
examples/test-appsrc.c
Normal file
140
examples/test-appsrc.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean white;
|
||||
GstClockTime timestamp;
|
||||
} MyContext;
|
||||
|
||||
/* called when we need to give data to appsrc */
|
||||
static void
|
||||
need_data (GstElement * appsrc, guint unused, MyContext * ctx)
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
guint size;
|
||||
GstFlowReturn ret;
|
||||
|
||||
size = 385 * 288 * 2;
|
||||
|
||||
buffer = gst_buffer_new_allocate (NULL, size, NULL);
|
||||
|
||||
/* this makes the image black/white */
|
||||
gst_buffer_memset (buffer, 0, ctx->white ? 0xff : 0x0, size);
|
||||
|
||||
ctx->white = !ctx->white;
|
||||
|
||||
/* increment the timestamp every 1/2 second */
|
||||
GST_BUFFER_PTS (buffer) = ctx->timestamp;
|
||||
GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
|
||||
ctx->timestamp += GST_BUFFER_DURATION (buffer);
|
||||
|
||||
g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
|
||||
/* called when a new media pipeline is constructed. We can query the
|
||||
* pipeline and configure our appsrc */
|
||||
static void
|
||||
media_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstElement *element, *appsrc;
|
||||
MyContext *ctx;
|
||||
|
||||
/* get the element used for providing the streams of the media */
|
||||
element = gst_rtsp_media_get_element (media);
|
||||
|
||||
/* get our appsrc, we named it 'mysrc' with the name property */
|
||||
appsrc = gst_bin_get_by_name_recurse_up (GST_BIN (element), "mysrc");
|
||||
|
||||
/* this instructs appsrc that we will be dealing with timed buffer */
|
||||
gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
|
||||
/* configure the caps of the video */
|
||||
g_object_set (G_OBJECT (appsrc), "caps",
|
||||
gst_caps_new_simple ("video/x-raw",
|
||||
"format", G_TYPE_STRING, "RGB16",
|
||||
"width", G_TYPE_INT, 384,
|
||||
"height", G_TYPE_INT, 288,
|
||||
"framerate", GST_TYPE_FRACTION, 0, 1, NULL), NULL);
|
||||
|
||||
ctx = g_new0 (MyContext, 1);
|
||||
ctx->white = FALSE;
|
||||
ctx->timestamp = 0;
|
||||
/* make sure ther datais freed when the media is gone */
|
||||
g_object_set_data_full (G_OBJECT (media), "my-extra-data", ctx,
|
||||
(GDestroyNotify) g_free);
|
||||
|
||||
/* install the callback that will be called when a buffer is needed */
|
||||
g_signal_connect (appsrc, "need-data", (GCallback) need_data, ctx);
|
||||
gst_object_unref (appsrc);
|
||||
gst_object_unref (element);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( appsrc name=mysrc ! videoconvert ! x264enc ! rtph264pay name=pay0 pt=96 )");
|
||||
|
||||
/* notify when our media is ready, This is called whenever someone asks for
|
||||
* the media and a new pipeline with our appsrc is created */
|
||||
g_signal_connect (factory, "media-configure", (GCallback) media_configure,
|
||||
NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mounts anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
196
examples/test-appsrc2.c
Normal file
196
examples/test-appsrc2.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/app.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstElement *generator_pipe;
|
||||
GstElement *vid_appsink;
|
||||
GstElement *vid_appsrc;
|
||||
GstElement *aud_appsink;
|
||||
GstElement *aud_appsrc;
|
||||
} MyContext;
|
||||
|
||||
/* called when we need to give data to an appsrc */
|
||||
static void
|
||||
need_data (GstElement * appsrc, guint unused, MyContext * ctx)
|
||||
{
|
||||
GstSample *sample;
|
||||
GstFlowReturn ret;
|
||||
|
||||
if (appsrc == ctx->vid_appsrc)
|
||||
sample = gst_app_sink_pull_sample (GST_APP_SINK (ctx->vid_appsink));
|
||||
else
|
||||
sample = gst_app_sink_pull_sample (GST_APP_SINK (ctx->aud_appsink));
|
||||
|
||||
if (sample) {
|
||||
GstBuffer *buffer = gst_sample_get_buffer (sample);
|
||||
GstSegment *seg = gst_sample_get_segment (sample);
|
||||
GstClockTime pts, dts;
|
||||
|
||||
/* Convert the PTS/DTS to running time so they start from 0 */
|
||||
pts = GST_BUFFER_PTS (buffer);
|
||||
if (GST_CLOCK_TIME_IS_VALID (pts))
|
||||
pts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, pts);
|
||||
|
||||
dts = GST_BUFFER_DTS (buffer);
|
||||
if (GST_CLOCK_TIME_IS_VALID (dts))
|
||||
dts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, dts);
|
||||
|
||||
if (buffer) {
|
||||
/* Make writable so we can adjust the timestamps */
|
||||
buffer = gst_buffer_copy (buffer);
|
||||
GST_BUFFER_PTS (buffer) = pts;
|
||||
GST_BUFFER_DTS (buffer) = dts;
|
||||
g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
|
||||
}
|
||||
|
||||
/* we don't need the appsink sample anymore */
|
||||
gst_sample_unref (sample);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ctx_free (MyContext * ctx)
|
||||
{
|
||||
gst_element_set_state (ctx->generator_pipe, GST_STATE_NULL);
|
||||
|
||||
gst_object_unref (ctx->generator_pipe);
|
||||
gst_object_unref (ctx->vid_appsrc);
|
||||
gst_object_unref (ctx->vid_appsink);
|
||||
gst_object_unref (ctx->aud_appsrc);
|
||||
gst_object_unref (ctx->aud_appsink);
|
||||
|
||||
g_free (ctx);
|
||||
}
|
||||
|
||||
/* called when a new media pipeline is constructed. We can query the
|
||||
* pipeline and configure our appsrc */
|
||||
static void
|
||||
media_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstElement *element, *appsrc, *appsink;
|
||||
GstCaps *caps;
|
||||
MyContext *ctx;
|
||||
|
||||
ctx = g_new0 (MyContext, 1);
|
||||
/* This pipeline generates H264 video and PCM audio. The appsinks are kept small so that if delivery is slow,
|
||||
* encoded buffers are dropped as needed. There's slightly more buffers (32) allowed for audio */
|
||||
ctx->generator_pipe =
|
||||
gst_parse_launch
|
||||
("videotestsrc is-live=true ! x264enc speed-preset=superfast tune=zerolatency ! h264parse ! appsink name=vid max-buffers=1 drop=true "
|
||||
"audiotestsrc is-live=true ! appsink name=aud max-buffers=32 drop=true",
|
||||
NULL);
|
||||
|
||||
/* make sure the data is freed when the media is gone */
|
||||
g_object_set_data_full (G_OBJECT (media), "rtsp-extra-data", ctx,
|
||||
(GDestroyNotify) ctx_free);
|
||||
|
||||
/* get the element (bin) used for providing the streams of the media */
|
||||
element = gst_rtsp_media_get_element (media);
|
||||
|
||||
/* Find the 2 app sources (video / audio), and configure them, connect to the
|
||||
* signals to request data */
|
||||
/* configure the caps of the video */
|
||||
caps = gst_caps_new_simple ("video/x-h264",
|
||||
"stream-format", G_TYPE_STRING, "byte-stream",
|
||||
"alignment", G_TYPE_STRING, "au",
|
||||
"width", G_TYPE_INT, 384, "height", G_TYPE_INT, 288,
|
||||
"framerate", GST_TYPE_FRACTION, 15, 1, NULL);
|
||||
ctx->vid_appsrc = appsrc =
|
||||
gst_bin_get_by_name_recurse_up (GST_BIN (element), "videosrc");
|
||||
ctx->vid_appsink = appsink =
|
||||
gst_bin_get_by_name (GST_BIN (ctx->generator_pipe), "vid");
|
||||
gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
|
||||
g_object_set (G_OBJECT (appsrc), "caps", caps, NULL);
|
||||
g_object_set (G_OBJECT (appsink), "caps", caps, NULL);
|
||||
/* install the callback that will be called when a buffer is needed */
|
||||
g_signal_connect (appsrc, "need-data", (GCallback) need_data, ctx);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, "S24BE",
|
||||
"layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 48000,
|
||||
"channels", G_TYPE_INT, 2, NULL);
|
||||
ctx->aud_appsrc = appsrc =
|
||||
gst_bin_get_by_name_recurse_up (GST_BIN (element), "audiosrc");
|
||||
ctx->aud_appsink = appsink =
|
||||
gst_bin_get_by_name (GST_BIN (ctx->generator_pipe), "aud");
|
||||
gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
|
||||
g_object_set (G_OBJECT (appsrc), "caps", caps, NULL);
|
||||
g_object_set (G_OBJECT (appsink), "caps", caps, NULL);
|
||||
g_signal_connect (appsrc, "need-data", (GCallback) need_data, ctx);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_element_set_state (ctx->generator_pipe, GST_STATE_PLAYING);
|
||||
gst_object_unref (element);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( appsrc name=videosrc ! h264parse ! rtph264pay name=pay0 pt=96 "
|
||||
" appsrc name=audiosrc ! audioconvert ! rtpL24pay name=pay1 pt=97 )");
|
||||
|
||||
/* notify when our media is ready, This is called whenever someone asks for
|
||||
* the media and a new pipeline with our appsrc is created */
|
||||
g_signal_connect (factory, "media-configure", (GCallback) media_configure,
|
||||
NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mounts anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
229
examples/test-auth-digest.c
Normal file
229
examples/test-auth-digest.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
static gchar *htdigest_path = NULL;
|
||||
static gchar *realm = NULL;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"htdigest-path", 'h', 0, G_OPTION_ARG_STRING, &htdigest_path,
|
||||
"Path to an htdigest file to parse (default: None)", "PATH"},
|
||||
{"realm", 'r', 0, G_OPTION_ARG_STRING, &realm,
|
||||
"Authentication realm (default: None)", "REALM"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
static gboolean
|
||||
remove_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
|
||||
GstRTSPServer * server)
|
||||
{
|
||||
return GST_RTSP_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_sessions (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
g_print ("removing all sessions\n");
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_filter (pool,
|
||||
(GstRTSPSessionPoolFilterFunc) remove_func, server);
|
||||
g_object_unref (pool);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
|
||||
optctx = g_option_context_new (NULL);
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mounts for this server, every server has a default mapper object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* allow user and admin to access this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "user",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
gst_rtsp_media_factory_add_role (factory, "admin",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
/* admin2 can look at the media but not construct so he gets a
|
||||
* 401 Unauthorized */
|
||||
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||
/* Anonymous user can do the same things as admin2 on this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "anonymous",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||
|
||||
/* make another factory */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=30/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 )");
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test2", factory);
|
||||
|
||||
/* allow admin2 to access this resource */
|
||||
/* user and admin have no permissions so they can't even see the
|
||||
* media and get a 404 Not Found */
|
||||
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* make a new authentication manager */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
gst_rtsp_auth_set_supported_methods (auth, GST_RTSP_AUTH_DIGEST);
|
||||
|
||||
/* make default token, it has no permissions */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"anonymous", NULL);
|
||||
gst_rtsp_auth_set_default_token (auth, token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make user token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
gst_rtsp_auth_add_digest (auth, "user", "password", token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
if (htdigest_path) {
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
|
||||
if (!gst_rtsp_auth_parse_htdigest (auth, htdigest_path, token)) {
|
||||
g_printerr ("Could not parse htdigest at %s\n", htdigest_path);
|
||||
gst_rtsp_token_unref (token);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
gst_rtsp_token_unref (token);
|
||||
}
|
||||
|
||||
if (realm)
|
||||
gst_rtsp_auth_set_realm (auth, realm);
|
||||
|
||||
/* make admin token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"admin", NULL);
|
||||
gst_rtsp_auth_add_digest (auth, "admin", "power", token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make admin2 token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"admin2", NULL);
|
||||
gst_rtsp_auth_add_digest (auth, "admin2", "power2", token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* set as the server authentication manager */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
g_object_unref (auth);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
g_timeout_add_seconds (10, (GSourceFunc) remove_sessions, server);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream with user:password ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_print ("stream with admin:power ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_print ("stream with admin2:power2 ready at rtsp://127.0.0.1:8554/test2\n");
|
||||
|
||||
if (htdigest_path)
|
||||
g_print
|
||||
("stream with htdigest users ready at rtsp://127.0.0.1:8554/test\n");
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
190
examples/test-auth.c
Normal file
190
examples/test-auth.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
static gboolean
|
||||
remove_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
|
||||
GstRTSPServer * server)
|
||||
{
|
||||
return GST_RTSP_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_sessions (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
g_print ("removing all sessions\n");
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_filter (pool,
|
||||
(GstRTSPSessionPoolFilterFunc) remove_func, server);
|
||||
g_object_unref (pool);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
gchar *basic;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mounts for this server, every server has a default mapper object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* allow user and admin to access this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "user",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
gst_rtsp_media_factory_add_role (factory, "admin",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
/* admin2 can look at the media but not construct so he gets a
|
||||
* 401 Unauthorized */
|
||||
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||
/* Anonymous user can do the same things as admin2 on this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "anonymous",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||
|
||||
/* make another factory */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=30/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 )");
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test2", factory);
|
||||
|
||||
/* allow admin2 to access this resource */
|
||||
/* user and admin have no permissions so they can't even see the
|
||||
* media and get a 404 Not Found */
|
||||
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* make a new authentication manager */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
|
||||
/* make default token, it has no permissions */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"anonymous", NULL);
|
||||
gst_rtsp_auth_set_default_token (auth, token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make user token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("user", "password");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make admin token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"admin", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("admin", "power");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make admin2 token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"admin2", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("admin2", "power2");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* set as the server authentication manager */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
g_object_unref (auth);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
g_timeout_add_seconds (10, (GSourceFunc) remove_sessions, server);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream with user:password ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_print ("stream with admin:power ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_print ("stream with admin2:power2 ready at rtsp://127.0.0.1:8554/test2\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
276
examples/test-cgroups.c
Normal file
276
examples/test-cgroups.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2013 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/* Runs a pipeline and clasifies the media pipelines based on the
|
||||
* authenticated user.
|
||||
*
|
||||
* This test requires 2 cpu cgroups to exist named 'user' and 'admin'.
|
||||
* The rtsp server should have permission to add its threads to the
|
||||
* cgroups.
|
||||
*
|
||||
* sudo cgcreate -t uid:gid -g cpu:/user
|
||||
* sudo cgcreate -t uid:gid -g cpu:/admin
|
||||
*
|
||||
* With -t you can give the user and group access to the task file to
|
||||
* write the thread ids. The user running the server can be used.
|
||||
*
|
||||
* Then you would want to change the cpu shares assigned to each group:
|
||||
*
|
||||
* sudo cgset -r cpu.shares=100 user
|
||||
* sudo cgset -r cpu.shares=1024 admin
|
||||
*
|
||||
* Then start clients for 'user' until the stream is degraded because of
|
||||
* lack of CPU. Then start a client for 'admin' and check that the stream
|
||||
* is not degraded.
|
||||
*/
|
||||
|
||||
#include <libcgroup.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
typedef struct _GstRTSPCGroupPool GstRTSPCGroupPool;
|
||||
typedef struct _GstRTSPCGroupPoolClass GstRTSPCGroupPoolClass;
|
||||
|
||||
#define GST_TYPE_RTSP_CGROUP_POOL (gst_rtsp_cgroup_pool_get_type ())
|
||||
#define GST_IS_RTSP_CGROUP_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_CGROUP_POOL))
|
||||
#define GST_IS_RTSP_CGROUP_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_CGROUP_POOL))
|
||||
#define GST_RTSP_CGROUP_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_CGROUP_POOL, GstRTSPCGroupPoolClass))
|
||||
#define GST_RTSP_CGROUP_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_CGROUP_POOL, GstRTSPCGroupPool))
|
||||
#define GST_RTSP_CGROUP_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_CGROUP_POOL, GstRTSPCGroupPoolClass))
|
||||
#define GST_RTSP_CGROUP_POOL_CAST(obj) ((GstRTSPCGroupPool*)(obj))
|
||||
#define GST_RTSP_CGROUP_POOL_CLASS_CAST(klass) ((GstRTSPCGroupPoolClass*)(klass))
|
||||
|
||||
struct _GstRTSPCGroupPool
|
||||
{
|
||||
GstRTSPThreadPool parent;
|
||||
|
||||
struct cgroup *user;
|
||||
struct cgroup *admin;
|
||||
};
|
||||
|
||||
struct _GstRTSPCGroupPoolClass
|
||||
{
|
||||
GstRTSPThreadPoolClass parent_class;
|
||||
};
|
||||
|
||||
static GQuark thread_cgroup;
|
||||
|
||||
static void gst_rtsp_cgroup_pool_finalize (GObject * obj);
|
||||
|
||||
static void default_thread_enter (GstRTSPThreadPool * pool,
|
||||
GstRTSPThread * thread);
|
||||
static void default_configure_thread (GstRTSPThreadPool * pool,
|
||||
GstRTSPThread * thread, GstRTSPContext * ctx);
|
||||
|
||||
static GType gst_rtsp_cgroup_pool_get_type (void);
|
||||
|
||||
G_DEFINE_TYPE (GstRTSPCGroupPool, gst_rtsp_cgroup_pool,
|
||||
GST_TYPE_RTSP_THREAD_POOL);
|
||||
|
||||
static void
|
||||
gst_rtsp_cgroup_pool_class_init (GstRTSPCGroupPoolClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstRTSPThreadPoolClass *tpool_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
tpool_class = GST_RTSP_THREAD_POOL_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_rtsp_cgroup_pool_finalize;
|
||||
|
||||
tpool_class->configure_thread = default_configure_thread;
|
||||
tpool_class->thread_enter = default_thread_enter;
|
||||
|
||||
thread_cgroup = g_quark_from_string ("cgroup.pool.thread.cgroup");
|
||||
|
||||
cgroup_init ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_cgroup_pool_init (GstRTSPCGroupPool * pool)
|
||||
{
|
||||
pool->user = cgroup_new_cgroup ("user");
|
||||
if (cgroup_add_controller (pool->user, "cpu") == NULL)
|
||||
g_error ("Failed to add cpu controller to user cgroup");
|
||||
pool->admin = cgroup_new_cgroup ("admin");
|
||||
if (cgroup_add_controller (pool->admin, "cpu") == NULL)
|
||||
g_error ("Failed to add cpu controller to admin cgroup");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_cgroup_pool_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPCGroupPool *pool = GST_RTSP_CGROUP_POOL (obj);
|
||||
|
||||
GST_INFO ("finalize pool %p", pool);
|
||||
|
||||
cgroup_free (&pool->user);
|
||||
cgroup_free (&pool->admin);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_cgroup_pool_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
default_thread_enter (GstRTSPThreadPool * pool, GstRTSPThread * thread)
|
||||
{
|
||||
struct cgroup *cgroup;
|
||||
|
||||
cgroup = gst_mini_object_get_qdata (GST_MINI_OBJECT (thread), thread_cgroup);
|
||||
if (cgroup) {
|
||||
gint res = 0;
|
||||
|
||||
res = cgroup_attach_task (cgroup);
|
||||
|
||||
if (res != 0)
|
||||
GST_ERROR ("error: %d (%s)", res, cgroup_strerror (res));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
default_configure_thread (GstRTSPThreadPool * pool,
|
||||
GstRTSPThread * thread, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPCGroupPool *cpool = GST_RTSP_CGROUP_POOL (pool);
|
||||
const gchar *cls;
|
||||
struct cgroup *cgroup;
|
||||
|
||||
if (ctx->token)
|
||||
cls = gst_rtsp_token_get_string (ctx->token, "cgroup.pool.media.class");
|
||||
else
|
||||
cls = NULL;
|
||||
|
||||
GST_DEBUG ("manage cgroup %s", cls);
|
||||
|
||||
if (!g_strcmp0 (cls, "admin"))
|
||||
cgroup = cpool->admin;
|
||||
else
|
||||
cgroup = cpool->user;
|
||||
|
||||
/* attach the cgroup to the thread */
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT (thread), thread_cgroup,
|
||||
cgroup, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
gchar *basic;
|
||||
GstRTSPThreadPool *thread_pool;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mounts for this server, every server has a default mapper object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=640,height=480,framerate=50/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* allow user and admin to access this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "user",
|
||||
"media.factory.access", G_TYPE_BOOLEAN, TRUE,
|
||||
"media.factory.construct", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
gst_rtsp_media_factory_add_role (factory, "admin",
|
||||
"media.factory.access", G_TYPE_BOOLEAN, TRUE,
|
||||
"media.factory.construct", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* make a new authentication manager */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
|
||||
/* make user token */
|
||||
token = gst_rtsp_token_new ("cgroup.pool.media.class", G_TYPE_STRING, "user",
|
||||
"media.factory.role", G_TYPE_STRING, "user", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("user", "password");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make admin token */
|
||||
token = gst_rtsp_token_new ("cgroup.pool.media.class", G_TYPE_STRING, "admin",
|
||||
"media.factory.role", G_TYPE_STRING, "admin", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("admin", "power");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* set as the server authentication manager */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
g_object_unref (auth);
|
||||
|
||||
thread_pool = g_object_new (GST_TYPE_RTSP_CGROUP_POOL, NULL);
|
||||
gst_rtsp_server_set_thread_pool (server, thread_pool);
|
||||
g_object_unref (thread_pool);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream with user:password ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_print ("stream with admin:power ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
93
examples/test-launch.c
Normal file
93
examples/test-launch.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
#define DEFAULT_DISABLE_RTCP FALSE
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
static gboolean disable_rtcp = DEFAULT_DISABLE_RTCP;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{"disable-rtcp", '\0', 0, G_OPTION_ARG_NONE, &disable_rtcp,
|
||||
"Whether RTCP should be disabled (default false)", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
|
||||
optctx = g_option_context_new ("<launch line> - Test RTSP Server, Launch\n\n"
|
||||
"Example: \"( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )\"");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, argv[1]);
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
gst_rtsp_media_factory_set_enable_rtcp (factory, !disable_rtcp);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
177
examples/test-mp4.c
Normal file
177
examples/test-mp4.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/* called when a stream has received an RTCP packet from the client */
|
||||
static void
|
||||
on_ssrc_active (GObject * session, GObject * source, GstRTSPMedia * media)
|
||||
{
|
||||
GstStructure *stats;
|
||||
|
||||
GST_INFO ("source %p in session %p is active", source, session);
|
||||
|
||||
g_object_get (source, "stats", &stats, NULL);
|
||||
if (stats) {
|
||||
gchar *sstr;
|
||||
|
||||
sstr = gst_structure_to_string (stats);
|
||||
g_print ("structure: %s\n", sstr);
|
||||
g_free (sstr);
|
||||
|
||||
gst_structure_free (stats);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_sender_ssrc_active (GObject * session, GObject * source,
|
||||
GstRTSPMedia * media)
|
||||
{
|
||||
GstStructure *stats;
|
||||
|
||||
GST_INFO ("source %p in session %p is active", source, session);
|
||||
|
||||
g_object_get (source, "stats", &stats, NULL);
|
||||
if (stats) {
|
||||
gchar *sstr;
|
||||
|
||||
sstr = gst_structure_to_string (stats);
|
||||
g_print ("Sender stats:\nstructure: %s\n", sstr);
|
||||
g_free (sstr);
|
||||
|
||||
gst_structure_free (stats);
|
||||
}
|
||||
}
|
||||
|
||||
/* signal callback when the media is prepared for streaming. We can get the
|
||||
* session manager for each of the streams and connect to some signals. */
|
||||
static void
|
||||
media_prepared_cb (GstRTSPMedia * media)
|
||||
{
|
||||
guint i, n_streams;
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
|
||||
GST_INFO ("media %p is prepared and has %u streams", media, n_streams);
|
||||
|
||||
for (i = 0; i < n_streams; i++) {
|
||||
GstRTSPStream *stream;
|
||||
GObject *session;
|
||||
|
||||
stream = gst_rtsp_media_get_stream (media, i);
|
||||
if (stream == NULL)
|
||||
continue;
|
||||
|
||||
session = gst_rtsp_stream_get_rtpsession (stream);
|
||||
GST_INFO ("watching session %p on stream %u", session, i);
|
||||
|
||||
g_signal_connect (session, "on-ssrc-active",
|
||||
(GCallback) on_ssrc_active, media);
|
||||
g_signal_connect (session, "on-sender-ssrc-active",
|
||||
(GCallback) on_sender_ssrc_active, media);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
media_configure_cb (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
||||
{
|
||||
/* connect our prepared signal so that we can see when this media is
|
||||
* prepared for streaming */
|
||||
g_signal_connect (media, "prepared", (GCallback) media_prepared_cb, factory);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
gchar *str;
|
||||
|
||||
optctx = g_option_context_new ("<filename.mp4> - Test RTSP Server, MP4");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
str = g_strdup_printf ("( "
|
||||
"filesrc location=\"%s\" ! qtdemux name=d "
|
||||
"d. ! queue ! rtph264pay pt=96 name=pay0 "
|
||||
"d. ! queue ! rtpmp4apay pt=97 name=pay1 " ")", argv[1]);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, str);
|
||||
g_signal_connect (factory, "media-configure", (GCallback) media_configure_cb,
|
||||
factory);
|
||||
g_free (str);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
104
examples/test-multicast.c
Normal file
104
examples/test-multicast.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPAddressPool *pool;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
|
||||
/* make a new address pool */
|
||||
pool = gst_rtsp_address_pool_new ();
|
||||
gst_rtsp_address_pool_add_range (pool,
|
||||
"224.3.0.0", "224.3.0.10", 5000, 5010, 16);
|
||||
gst_rtsp_media_factory_set_address_pool (factory, pool);
|
||||
/* only allow multicast */
|
||||
gst_rtsp_media_factory_set_protocols (factory,
|
||||
GST_RTSP_LOWER_TRANS_UDP_MCAST);
|
||||
g_object_unref (pool);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
125
examples/test-multicast2.c
Normal file
125
examples/test-multicast2.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
media_constructed (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
||||
{
|
||||
guint i, n_streams;
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
|
||||
for (i = 0; i < n_streams; i++) {
|
||||
GstRTSPAddressPool *pool;
|
||||
GstRTSPStream *stream;
|
||||
gchar *min, *max;
|
||||
|
||||
stream = gst_rtsp_media_get_stream (media, i);
|
||||
|
||||
/* make a new address pool */
|
||||
pool = gst_rtsp_address_pool_new ();
|
||||
|
||||
min = g_strdup_printf ("224.3.0.%d", (2 * i) + 1);
|
||||
max = g_strdup_printf ("224.3.0.%d", (2 * i) + 2);
|
||||
gst_rtsp_address_pool_add_range (pool, min, max,
|
||||
5000 + (10 * i), 5010 + (10 * i), 1);
|
||||
g_free (min);
|
||||
g_free (max);
|
||||
|
||||
gst_rtsp_stream_set_address_pool (stream, pool);
|
||||
g_object_unref (pool);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
|
||||
g_signal_connect (factory, "media-constructed", (GCallback)
|
||||
media_constructed, NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
147
examples/test-netclock-client.c
Normal file
147
examples/test-netclock-client.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2014 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/net/gstnet.h>
|
||||
|
||||
#define PLAYBACK_DELAY_MS 40
|
||||
|
||||
static void
|
||||
source_created (GstElement * pipe, GstElement * source)
|
||||
{
|
||||
g_object_set (source, "latency", PLAYBACK_DELAY_MS,
|
||||
"ntp-time-source", 3, "buffer-mode", 4, "ntp-sync", TRUE, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
message (GstBus * bus, GstMessage * message, gpointer user_data)
|
||||
{
|
||||
GMainLoop *loop = user_data;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_ERROR:{
|
||||
GError *err = NULL;
|
||||
gchar *name, *debug = NULL;
|
||||
|
||||
name = gst_object_get_path_string (message->src);
|
||||
gst_message_parse_error (message, &err, &debug);
|
||||
|
||||
g_printerr ("ERROR: from element %s: %s\n", name, err->message);
|
||||
if (debug != NULL)
|
||||
g_printerr ("Additional debug info:\n%s\n", debug);
|
||||
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
g_free (name);
|
||||
|
||||
g_main_loop_quit (loop);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_WARNING:{
|
||||
GError *err = NULL;
|
||||
gchar *name, *debug = NULL;
|
||||
|
||||
name = gst_object_get_path_string (message->src);
|
||||
gst_message_parse_warning (message, &err, &debug);
|
||||
|
||||
g_printerr ("ERROR: from element %s: %s\n", name, err->message);
|
||||
if (debug != NULL)
|
||||
g_printerr ("Additional debug info:\n%s\n", debug);
|
||||
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_EOS:
|
||||
g_print ("Got EOS\n");
|
||||
g_main_loop_quit (loop);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GstClock *net_clock;
|
||||
gchar *server;
|
||||
gint clock_port;
|
||||
GstElement *pipe;
|
||||
GMainLoop *loop;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
if (argc < 2) {
|
||||
g_print ("usage: %s rtsp://URI clock-IP clock-PORT\n"
|
||||
"example: %s rtsp://localhost:8554/test 127.0.0.1 8554\n",
|
||||
argv[0], argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
server = argv[2];
|
||||
clock_port = atoi (argv[3]);
|
||||
|
||||
net_clock = gst_net_client_clock_new ("net_clock", server, clock_port, 0);
|
||||
if (net_clock == NULL) {
|
||||
g_print ("Failed to create net clock client for %s:%d\n",
|
||||
server, clock_port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wait for the clock to stabilise */
|
||||
gst_clock_wait_for_sync (net_clock, GST_CLOCK_TIME_NONE);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
pipe = gst_element_factory_make ("playbin", NULL);
|
||||
g_object_set (pipe, "uri", argv[1], NULL);
|
||||
g_signal_connect (pipe, "source-setup", G_CALLBACK (source_created), NULL);
|
||||
|
||||
gst_pipeline_use_clock (GST_PIPELINE (pipe), net_clock);
|
||||
|
||||
/* Set this high enough so that it's higher than the minimum latency
|
||||
* on all receivers */
|
||||
gst_pipeline_set_latency (GST_PIPELINE (pipe), 500 * GST_MSECOND);
|
||||
|
||||
if (gst_element_set_state (pipe,
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
|
||||
g_print ("Failed to set state to PLAYING\n");
|
||||
goto exit;
|
||||
};
|
||||
|
||||
gst_bus_add_signal_watch (GST_ELEMENT_BUS (pipe));
|
||||
g_signal_connect (GST_ELEMENT_BUS (pipe), "message", G_CALLBACK (message),
|
||||
loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
exit:
|
||||
gst_element_set_state (pipe, GST_STATE_NULL);
|
||||
gst_object_unref (pipe);
|
||||
g_main_loop_unref (loop);
|
||||
|
||||
return 0;
|
||||
}
|
123
examples/test-netclock.c
Normal file
123
examples/test-netclock.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2014 Jan Schmidt <jan@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/net/gstnettimeprovider.h>
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
GstClock *global_clock;
|
||||
|
||||
#define TEST_TYPE_RTSP_MEDIA_FACTORY (test_rtsp_media_factory_get_type ())
|
||||
#define TEST_TYPE_RTSP_MEDIA (test_rtsp_media_get_type ())
|
||||
|
||||
GType test_rtsp_media_get_type (void);
|
||||
|
||||
typedef struct TestRTSPMediaClass TestRTSPMediaClass;
|
||||
typedef struct TestRTSPMedia TestRTSPMedia;
|
||||
|
||||
struct TestRTSPMediaClass
|
||||
{
|
||||
GstRTSPMediaClass parent;
|
||||
};
|
||||
|
||||
struct TestRTSPMedia
|
||||
{
|
||||
GstRTSPMedia parent;
|
||||
};
|
||||
|
||||
static gboolean custom_setup_rtpbin (GstRTSPMedia * media, GstElement * rtpbin);
|
||||
|
||||
G_DEFINE_TYPE (TestRTSPMedia, test_rtsp_media, GST_TYPE_RTSP_MEDIA);
|
||||
|
||||
static void
|
||||
test_rtsp_media_class_init (TestRTSPMediaClass * test_klass)
|
||||
{
|
||||
GstRTSPMediaClass *klass = (GstRTSPMediaClass *) (test_klass);
|
||||
klass->setup_rtpbin = custom_setup_rtpbin;
|
||||
}
|
||||
|
||||
static void
|
||||
test_rtsp_media_init (TestRTSPMedia * media)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_setup_rtpbin (GstRTSPMedia * media, GstElement * rtpbin)
|
||||
{
|
||||
g_object_set (rtpbin, "ntp-time-source", 3, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
if (argc < 2) {
|
||||
g_print ("usage: %s <launch line> \n"
|
||||
"example: %s \"( videotestsrc is-live=true ! x264enc ! rtph264pay name=pay0 pt=96 )\"\n"
|
||||
"Pipeline must be live for synchronisation to work properly with this method!\n",
|
||||
argv[0], argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
global_clock = gst_system_clock_obtain ();
|
||||
gst_net_time_provider_new (global_clock, "0.0.0.0", 8554);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, argv[1]);
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
gst_rtsp_media_factory_set_media_gtype (factory, TEST_TYPE_RTSP_MEDIA);
|
||||
gst_rtsp_media_factory_set_clock (factory, global_clock);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
93
examples/test-ogg.c
Normal file
93
examples/test-ogg.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
gchar *str;
|
||||
|
||||
optctx = g_option_context_new ("<filename.ogg> - Test RTSP Server, OGG");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
str = g_strdup_printf ("( "
|
||||
"filesrc location=%s ! oggdemux name=d "
|
||||
"d. ! queue ! rtptheorapay name=pay0 pt=96 "
|
||||
"d. ! queue ! rtpvorbispay name=pay1 pt=97 " ")", argv[1]);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, str);
|
||||
g_free (str);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
71
examples/test-onvif-backchannel.c
Normal file
71
examples/test-onvif-backchannel.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp-server/rtsp-onvif-server.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_onvif_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_onvif_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( videotestsrc is-live=true ! x264enc ! rtph264pay name=pay0 pt=96 audiotestsrc is-live=true ! mulawenc ! rtppcmupay name=pay1 )");
|
||||
gst_rtsp_onvif_media_factory_set_backchannel_launch
|
||||
(GST_RTSP_ONVIF_MEDIA_FACTORY (factory),
|
||||
"( capsfilter caps=\"application/x-rtp, media=audio, payload=0, clock-rate=8000, encoding-name=PCMU\" name=depay_backchannel ! rtppcmudepay ! fakesink async=false )");
|
||||
gst_rtsp_media_factory_set_shared (factory, FALSE);
|
||||
gst_rtsp_media_factory_set_media_gtype (factory, GST_TYPE_RTSP_ONVIF_MEDIA);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
729
examples/test-onvif-client.c
Normal file
729
examples/test-onvif-client.c
Normal file
|
@ -0,0 +1,729 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/rtsp.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *range;
|
||||
gdouble speed;
|
||||
gchar *frames;
|
||||
gchar *rate_control;
|
||||
gboolean reverse;
|
||||
} SeekParameters;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstElement *src;
|
||||
GstElement *sink;
|
||||
GstElement *pipe;
|
||||
SeekParameters *seek_params;
|
||||
GMainLoop *loop;
|
||||
GIOChannel *io;
|
||||
gboolean new_range;
|
||||
guint io_watch_id;
|
||||
gboolean reset_sync;
|
||||
} Context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *name;
|
||||
gboolean has_argument;
|
||||
const gchar *help;
|
||||
gboolean (*func) (Context * ctx, gchar * arg, gboolean * async);
|
||||
} Command;
|
||||
|
||||
static gboolean cmd_help (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_pause (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_play (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_reverse (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_range (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_speed (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_frames (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_rate_control (Context * ctx, gchar * arg, gboolean * async);
|
||||
static gboolean cmd_step_forward (Context * ctx, gchar * arg, gboolean * async);
|
||||
|
||||
static Command commands[] = {
|
||||
{"help", FALSE, "Display list of valid commands", cmd_help},
|
||||
{"pause", FALSE, "Pause playback", cmd_pause},
|
||||
{"play", FALSE, "Resume playback", cmd_play},
|
||||
{"reverse", FALSE, "Reverse playback direction", cmd_reverse},
|
||||
{"range", TRUE,
|
||||
"Seek to the specified range, example: \"range: 19000101T000000Z-19000101T000200Z\"",
|
||||
cmd_range},
|
||||
{"speed", TRUE, "Set the playback speed, example: \"speed: 1.0\"", cmd_speed},
|
||||
{"frames", TRUE,
|
||||
"Set the frames trickmode, example: \"frames: intra\", \"frames: predicted\", \"frames: intra/1000\"",
|
||||
cmd_frames},
|
||||
{"rate-control", TRUE,
|
||||
"Set the rate control mode, example: \"rate-control: no\"",
|
||||
cmd_rate_control},
|
||||
{"s", FALSE, "Step to the following frame (in current playback direction)",
|
||||
cmd_step_forward},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static gchar *rtsp_address;
|
||||
|
||||
#define MAKE_AND_ADD(var, pipe, name, label, elem_name) \
|
||||
G_STMT_START { \
|
||||
if (G_UNLIKELY (!(var = (gst_element_factory_make (name, elem_name))))) { \
|
||||
GST_ERROR ("Could not create element %s", name); \
|
||||
goto label; \
|
||||
} \
|
||||
if (G_UNLIKELY (!gst_bin_add (GST_BIN_CAST (pipe), var))) { \
|
||||
GST_ERROR ("Could not add element %s", name); \
|
||||
goto label; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define DEFAULT_RANGE "19000101T000000Z-19000101T000200Z"
|
||||
#define DEFAULT_SPEED 1.0
|
||||
#define DEFAULT_FRAMES "none"
|
||||
#define DEFAULT_RATE_CONTROL "yes"
|
||||
#define DEFAULT_REVERSE FALSE
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * src, GstPad * srcpad, GstElement * peer)
|
||||
{
|
||||
GstPad *sinkpad = gst_element_get_static_pad (peer, "sink");
|
||||
|
||||
gst_pad_link (srcpad, sinkpad);
|
||||
|
||||
gst_object_unref (sinkpad);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setup (Context * ctx)
|
||||
{
|
||||
GstElement *onvifparse, *queue, *vdepay, *vdec, *vconv, *toverlay, *tee,
|
||||
*vqueue;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
MAKE_AND_ADD (ctx->src, ctx->pipe, "rtspsrc", done, NULL);
|
||||
MAKE_AND_ADD (queue, ctx->pipe, "queue", done, NULL);
|
||||
MAKE_AND_ADD (onvifparse, ctx->pipe, "rtponvifparse", done, NULL);
|
||||
MAKE_AND_ADD (vdepay, ctx->pipe, "rtph264depay", done, NULL);
|
||||
MAKE_AND_ADD (vdec, ctx->pipe, "avdec_h264", done, NULL);
|
||||
MAKE_AND_ADD (vconv, ctx->pipe, "videoconvert", done, NULL);
|
||||
MAKE_AND_ADD (toverlay, ctx->pipe, "timeoverlay", done, NULL);
|
||||
MAKE_AND_ADD (tee, ctx->pipe, "tee", done, NULL);
|
||||
MAKE_AND_ADD (vqueue, ctx->pipe, "queue", done, NULL);
|
||||
MAKE_AND_ADD (ctx->sink, ctx->pipe, "xvimagesink", done, NULL);
|
||||
|
||||
g_object_set (ctx->src, "location", rtsp_address, NULL);
|
||||
g_object_set (ctx->src, "onvif-mode", TRUE, NULL);
|
||||
g_object_set (ctx->src, "tcp-timeout", 0, NULL);
|
||||
g_object_set (toverlay, "show-times-as-dates", TRUE, NULL);
|
||||
|
||||
g_object_set (toverlay, "datetime-format", "%a %d, %b %Y - %T", NULL);
|
||||
|
||||
g_signal_connect (ctx->src, "pad-added", G_CALLBACK (pad_added_cb), queue);
|
||||
|
||||
if (!gst_element_link_many (queue, onvifparse, vdepay, vdec, vconv, toverlay,
|
||||
tee, vqueue, ctx->sink, NULL)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
g_object_set (ctx->src, "onvif-rate-control", FALSE, "is-live", FALSE, NULL);
|
||||
|
||||
if (!g_strcmp0 (ctx->seek_params->rate_control, "no")) {
|
||||
g_object_set (ctx->sink, "sync", FALSE, NULL);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
get_current_position (Context * ctx, gboolean reverse)
|
||||
{
|
||||
GstSample *sample;
|
||||
GstBuffer *buffer;
|
||||
GstClockTime ret;
|
||||
|
||||
g_object_get (ctx->sink, "last-sample", &sample, NULL);
|
||||
|
||||
buffer = gst_sample_get_buffer (sample);
|
||||
|
||||
ret = GST_BUFFER_PTS (buffer);
|
||||
|
||||
if (reverse && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer)))
|
||||
ret += GST_BUFFER_DURATION (buffer);
|
||||
|
||||
gst_sample_unref (sample);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_seek_parameters (Context * ctx, SeekParameters * seek_params)
|
||||
{
|
||||
GstEvent *ret = NULL;
|
||||
gchar *range_str = NULL;
|
||||
GstRTSPTimeRange *rtsp_range;
|
||||
GstSeekType start_type, stop_type;
|
||||
GstClockTime start, stop;
|
||||
gdouble rate;
|
||||
GstSeekFlags flags;
|
||||
gchar **split = NULL;
|
||||
GstClockTime trickmode_interval = 0;
|
||||
|
||||
range_str = g_strdup_printf ("clock=%s", seek_params->range);
|
||||
|
||||
if (gst_rtsp_range_parse (range_str, &rtsp_range) != GST_RTSP_OK) {
|
||||
GST_ERROR ("Failed to parse range %s", range_str);
|
||||
goto done;
|
||||
}
|
||||
|
||||
gst_rtsp_range_get_times (rtsp_range, &start, &stop);
|
||||
|
||||
if (start > stop) {
|
||||
GST_ERROR ("Invalid range, start > stop: %s", seek_params->range);
|
||||
goto done;
|
||||
}
|
||||
|
||||
start_type = GST_SEEK_TYPE_SET;
|
||||
stop_type = GST_SEEK_TYPE_SET;
|
||||
|
||||
if (!ctx->new_range) {
|
||||
GstClockTime current_position =
|
||||
get_current_position (ctx, seek_params->reverse);
|
||||
|
||||
if (seek_params->reverse) {
|
||||
stop_type = GST_SEEK_TYPE_SET;
|
||||
stop = current_position;
|
||||
} else {
|
||||
start_type = GST_SEEK_TYPE_SET;
|
||||
start = current_position;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->new_range = FALSE;
|
||||
|
||||
flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE;
|
||||
|
||||
split = g_strsplit (seek_params->frames, "/", 2);
|
||||
|
||||
if (!g_strcmp0 (split[0], "intra")) {
|
||||
if (split[1]) {
|
||||
guint64 interval;
|
||||
gchar *end;
|
||||
|
||||
interval = g_ascii_strtoull (split[1], &end, 10);
|
||||
|
||||
if (!end || *end != '\0') {
|
||||
GST_ERROR ("Unexpected interval value %s", split[1]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
trickmode_interval = interval * GST_MSECOND;
|
||||
}
|
||||
flags |= GST_SEEK_FLAG_TRICKMODE_KEY_UNITS;
|
||||
} else if (!g_strcmp0 (split[0], "predicted")) {
|
||||
if (split[1]) {
|
||||
GST_ERROR ("Predicted frames mode does not allow an interval (%s)",
|
||||
seek_params->frames);
|
||||
goto done;
|
||||
}
|
||||
flags |= GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED;
|
||||
} else if (g_strcmp0 (split[0], "none")) {
|
||||
GST_ERROR ("Invalid frames mode (%s)", seek_params->frames);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (seek_params->reverse) {
|
||||
rate = -1.0 * seek_params->speed;
|
||||
} else {
|
||||
rate = 1.0 * seek_params->speed;
|
||||
}
|
||||
|
||||
ret = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
|
||||
start_type, start, stop_type, stop);
|
||||
|
||||
if (trickmode_interval)
|
||||
gst_event_set_seek_trickmode_interval (ret, trickmode_interval);
|
||||
|
||||
done:
|
||||
if (split)
|
||||
g_strfreev (split);
|
||||
g_free (range_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void prompt_on (Context * ctx);
|
||||
static void prompt_off (Context * ctx);
|
||||
|
||||
static gboolean
|
||||
cmd_help (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
guint i;
|
||||
|
||||
*async = FALSE;
|
||||
|
||||
for (i = 0; commands[i].name; i++) {
|
||||
g_print ("%s: %s\n", commands[i].name, commands[i].help);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_pause (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret;
|
||||
GstStateChangeReturn state_ret;
|
||||
|
||||
g_print ("Pausing\n");
|
||||
|
||||
state_ret = gst_element_set_state (ctx->pipe, GST_STATE_PAUSED);
|
||||
|
||||
*async = state_ret == GST_STATE_CHANGE_ASYNC;
|
||||
ret = state_ret != GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_play (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret;
|
||||
GstStateChangeReturn state_ret;
|
||||
|
||||
g_print ("Playing\n");
|
||||
|
||||
state_ret = gst_element_set_state (ctx->pipe, GST_STATE_PLAYING);
|
||||
|
||||
*async = state_ret == GST_STATE_CHANGE_ASYNC;
|
||||
ret = state_ret != GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_seek (Context * ctx)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstEvent *event;
|
||||
|
||||
if (!(event = translate_seek_parameters (ctx, ctx->seek_params))) {
|
||||
GST_ERROR ("Failed to create seek event");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ctx->seek_params->reverse)
|
||||
g_object_set (ctx->src, "onvif-rate-control", FALSE, NULL);
|
||||
|
||||
if (ctx->reset_sync) {
|
||||
g_object_set (ctx->sink, "sync", TRUE, NULL);
|
||||
ctx->reset_sync = FALSE;
|
||||
}
|
||||
|
||||
if (!gst_element_send_event (ctx->src, event)) {
|
||||
GST_ERROR ("Failed to seek rtspsrc");
|
||||
g_main_loop_quit (ctx->loop);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_reverse (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
g_print ("Reversing playback direction\n");
|
||||
|
||||
ctx->seek_params->reverse = !ctx->seek_params->reverse;
|
||||
|
||||
ret = do_seek (ctx);
|
||||
|
||||
*async = ret == TRUE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_range (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
g_print ("Switching to new range\n");
|
||||
|
||||
g_free (ctx->seek_params->range);
|
||||
ctx->seek_params->range = g_strdup (arg);
|
||||
ctx->new_range = TRUE;
|
||||
|
||||
ret = do_seek (ctx);
|
||||
|
||||
*async = ret == TRUE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_speed (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gchar *endptr = NULL;
|
||||
gdouble new_speed;
|
||||
|
||||
new_speed = g_ascii_strtod (arg, &endptr);
|
||||
|
||||
g_print ("Switching gears\n");
|
||||
|
||||
if (endptr == NULL || *endptr != '\0' || new_speed <= 0.0) {
|
||||
GST_ERROR ("Invalid value for speed: %s", arg);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ctx->seek_params->speed = new_speed;
|
||||
ret = do_seek (ctx);
|
||||
|
||||
done:
|
||||
*async = ret == TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_frames (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
g_print ("Changing Frames trickmode\n");
|
||||
|
||||
g_free (ctx->seek_params->frames);
|
||||
ctx->seek_params->frames = g_strdup (arg);
|
||||
ret = do_seek (ctx);
|
||||
*async = ret == TRUE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_rate_control (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
*async = FALSE;
|
||||
|
||||
if (!g_strcmp0 (arg, "no")) {
|
||||
g_object_set (ctx->sink, "sync", FALSE, NULL);
|
||||
ret = TRUE;
|
||||
} else if (!g_strcmp0 (arg, "yes")) {
|
||||
/* TODO: there probably is a solution that doesn't involve sending
|
||||
* a request to the server to reset our position */
|
||||
ctx->reset_sync = TRUE;
|
||||
ret = do_seek (ctx);
|
||||
*async = TRUE;
|
||||
} else {
|
||||
GST_ERROR ("Invalid rate-control: %s", arg);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cmd_step_forward (Context * ctx, gchar * arg, gboolean * async)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstEvent *event;
|
||||
|
||||
event = gst_event_new_step (GST_FORMAT_BUFFERS, 1, 1.0, TRUE, FALSE);
|
||||
|
||||
g_print ("Stepping\n");
|
||||
|
||||
if (!gst_element_send_event (ctx->sink, event)) {
|
||||
GST_ERROR ("Failed to step forward");
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
*async = ret == TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_command (Context * ctx, gchar * cmd)
|
||||
{
|
||||
gchar **split;
|
||||
guint i;
|
||||
gboolean valid_command = FALSE;
|
||||
|
||||
split = g_strsplit (cmd, ":", 0);
|
||||
|
||||
cmd = g_strstrip (split[0]);
|
||||
|
||||
if (cmd == NULL || *cmd == '\0') {
|
||||
g_print ("> ");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; commands[i].name; i++) {
|
||||
if (!g_strcmp0 (commands[i].name, cmd)) {
|
||||
valid_command = TRUE;
|
||||
if (commands[i].has_argument && g_strv_length (split) != 2) {
|
||||
g_print ("Command %s expects exactly one argument:\n%s: %s\n", cmd,
|
||||
commands[i].name, commands[i].help);
|
||||
} else if (!commands[i].has_argument && g_strv_length (split) != 1) {
|
||||
g_print ("Command %s expects no argument:\n%s: %s\n", cmd,
|
||||
commands[i].name, commands[i].help);
|
||||
} else {
|
||||
gboolean async = FALSE;
|
||||
|
||||
if (commands[i].func (ctx,
|
||||
commands[i].has_argument ? g_strstrip (split[1]) : NULL, &async)
|
||||
&& async)
|
||||
prompt_off (ctx);
|
||||
else
|
||||
g_print ("> ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_command) {
|
||||
g_print ("Invalid command %s\n> ", cmd);
|
||||
}
|
||||
|
||||
done:
|
||||
g_strfreev (split);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
io_callback (GIOChannel * io, GIOCondition condition, Context * ctx)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
gchar *str;
|
||||
GError *error = NULL;
|
||||
|
||||
switch (condition) {
|
||||
case G_IO_PRI:
|
||||
case G_IO_IN:
|
||||
switch (g_io_channel_read_line (io, &str, NULL, NULL, &error)) {
|
||||
case G_IO_STATUS_ERROR:
|
||||
GST_ERROR ("Failed to read commands from stdin: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
g_main_loop_quit (ctx->loop);
|
||||
break;
|
||||
case G_IO_STATUS_EOF:
|
||||
g_print ("EOF received, bye\n");
|
||||
g_main_loop_quit (ctx->loop);
|
||||
break;
|
||||
case G_IO_STATUS_AGAIN:
|
||||
break;
|
||||
case G_IO_STATUS_NORMAL:
|
||||
handle_command (ctx, str);
|
||||
g_free (str);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case G_IO_ERR:
|
||||
case G_IO_HUP:
|
||||
GST_ERROR ("Failed to read commands from stdin");
|
||||
g_main_loop_quit (ctx->loop);
|
||||
break;
|
||||
case G_IO_OUT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef STDIN_FILENO
|
||||
#ifdef G_OS_WIN32
|
||||
#define STDIN_FILENO _fileno(stdin)
|
||||
#else /* !G_OS_WIN32 */
|
||||
#define STDIN_FILENO 0
|
||||
#endif /* G_OS_WIN32 */
|
||||
#endif /* STDIN_FILENO */
|
||||
|
||||
static void
|
||||
prompt_on (Context * ctx)
|
||||
{
|
||||
g_assert (!ctx->io);
|
||||
ctx->io = g_io_channel_unix_new (STDIN_FILENO);
|
||||
ctx->io_watch_id =
|
||||
g_io_add_watch (ctx->io, G_IO_IN, (GIOFunc) io_callback, ctx);
|
||||
g_print ("> ");
|
||||
}
|
||||
|
||||
static void
|
||||
prompt_off (Context * ctx)
|
||||
{
|
||||
g_assert (ctx->io);
|
||||
g_source_remove (ctx->io_watch_id);
|
||||
g_io_channel_unref (ctx->io);
|
||||
ctx->io = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
bus_message_cb (GstBus * bus, GstMessage * message, Context * ctx)
|
||||
{
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_STATE_CHANGED:{
|
||||
GstState olds, news, pendings;
|
||||
|
||||
if (GST_MESSAGE_SRC (message) == GST_OBJECT (ctx->pipe)) {
|
||||
gst_message_parse_state_changed (message, &olds, &news, &pendings);
|
||||
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (ctx->pipe),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL, "playing");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_ERROR:{
|
||||
GError *error = NULL;
|
||||
gchar *debug;
|
||||
|
||||
gst_message_parse_error (message, &error, &debug);
|
||||
|
||||
gst_printerr ("Error: %s (%s)\n", error->message, debug);
|
||||
g_clear_error (&error);
|
||||
g_free (debug);
|
||||
g_main_loop_quit (ctx->loop);
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_LATENCY:{
|
||||
gst_bin_recalculate_latency (GST_BIN (ctx->pipe));
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_ASYNC_DONE:{
|
||||
prompt_on (ctx);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GOptionContext *optctx;
|
||||
Context ctx = { 0, };
|
||||
GstBus *bus;
|
||||
gint ret = 1;
|
||||
GError *error = NULL;
|
||||
const gchar *range = NULL;
|
||||
const gchar *frames = NULL;
|
||||
const gchar *rate_control = NULL;
|
||||
gchar *default_speed =
|
||||
g_strdup_printf ("Speed to request (default: %.1f)", DEFAULT_SPEED);
|
||||
SeekParameters seek_params =
|
||||
{ NULL, DEFAULT_SPEED, NULL, NULL, DEFAULT_REVERSE };
|
||||
GOptionEntry entries[] = {
|
||||
{"range", 0, 0, G_OPTION_ARG_STRING, &range,
|
||||
"Range to seek (default: " DEFAULT_RANGE ")", "RANGE"},
|
||||
{"speed", 0, 0, G_OPTION_ARG_DOUBLE, &seek_params.speed,
|
||||
default_speed, "SPEED"},
|
||||
{"frames", 0, 0, G_OPTION_ARG_STRING, &frames,
|
||||
"Frames to request (default: " DEFAULT_FRAMES ")", "FRAMES"},
|
||||
{"rate-control", 0, 0, G_OPTION_ARG_STRING, &rate_control,
|
||||
"Apply rate control on the client side (default: "
|
||||
DEFAULT_RATE_CONTROL ")", "RATE_CONTROL"},
|
||||
{"reverse", 0, 0, G_OPTION_ARG_NONE, &seek_params.reverse,
|
||||
"Playback direction", ""},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
optctx = g_option_context_new ("<rtsp-url> - ONVIF RTSP Client");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
rtsp_address = argv[1];
|
||||
g_option_context_free (optctx);
|
||||
|
||||
seek_params.range = g_strdup (range ? range : DEFAULT_RANGE);
|
||||
seek_params.frames = g_strdup (frames ? frames : DEFAULT_FRAMES);
|
||||
seek_params.rate_control =
|
||||
g_strdup (rate_control ? rate_control : DEFAULT_RATE_CONTROL);
|
||||
|
||||
if (seek_params.speed <= 0.0) {
|
||||
GST_ERROR ("SPEED must be a positive number");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx.seek_params = &seek_params;
|
||||
ctx.new_range = TRUE;
|
||||
ctx.reset_sync = FALSE;
|
||||
|
||||
ctx.pipe = gst_pipeline_new (NULL);
|
||||
if (!setup (&ctx)) {
|
||||
g_printerr ("Damn\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
g_print ("Type help for the list of available commands\n");
|
||||
|
||||
do_seek (&ctx);
|
||||
|
||||
ctx.loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
bus = gst_pipeline_get_bus (GST_PIPELINE (ctx.pipe));
|
||||
gst_bus_add_watch (bus, (GstBusFunc) bus_message_cb, &ctx);
|
||||
|
||||
/* This will make rtspsrc progress to the OPEN state, at which point we can seek it */
|
||||
if (!gst_element_set_state (ctx.pipe, GST_STATE_PLAYING))
|
||||
goto done;
|
||||
|
||||
g_main_loop_run (ctx.loop);
|
||||
|
||||
g_main_loop_unref (ctx.loop);
|
||||
|
||||
gst_bus_remove_watch (bus);
|
||||
gst_object_unref (bus);
|
||||
gst_element_set_state (ctx.pipe, GST_STATE_NULL);
|
||||
gst_object_unref (ctx.pipe);
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
g_free (seek_params.range);
|
||||
g_free (seek_params.frames);
|
||||
g_free (seek_params.rate_control);
|
||||
g_free (default_speed);
|
||||
return ret;
|
||||
}
|
654
examples/test-onvif-server.c
Normal file
654
examples/test-onvif-server.c
Normal file
|
@ -0,0 +1,654 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#include "test-onvif-server.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (onvif_server_debug);
|
||||
#define GST_CAT_DEFAULT (onvif_server_debug)
|
||||
|
||||
#define MAKE_AND_ADD(var, pipe, name, label, elem_name) \
|
||||
G_STMT_START { \
|
||||
if (G_UNLIKELY (!(var = (gst_element_factory_make (name, elem_name))))) { \
|
||||
GST_ERROR ("Could not create element %s", name); \
|
||||
goto label; \
|
||||
} \
|
||||
if (G_UNLIKELY (!gst_bin_add (GST_BIN_CAST (pipe), var))) { \
|
||||
GST_ERROR ("Could not add element %s", name); \
|
||||
goto label; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
/* This simulates an archive of recordings running from 01-01-1900 to 01-01-2000.
|
||||
*
|
||||
* This is implemented by repeating the file provided at the command line, with
|
||||
* an empty interval of 5 seconds in-between. We intercept relevant events to
|
||||
* translate them, and update the timestamps on the output buffers.
|
||||
*/
|
||||
|
||||
#define INTERVAL (5 * GST_SECOND)
|
||||
|
||||
/* January the first, 2000 */
|
||||
#define END_DATE 3155673600 * GST_SECOND
|
||||
|
||||
static gchar *filename;
|
||||
|
||||
struct _ReplayBin
|
||||
{
|
||||
GstBin parent;
|
||||
|
||||
GstEvent *incoming_seek;
|
||||
GstEvent *outgoing_seek;
|
||||
GstClockTime trickmode_interval;
|
||||
|
||||
GstSegment segment;
|
||||
const GstSegment *incoming_segment;
|
||||
gboolean sent_segment;
|
||||
GstClockTime ts_offset;
|
||||
gint64 remainder;
|
||||
GstClockTime min_pts;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ReplayBin, replay_bin, GST_TYPE_BIN);
|
||||
|
||||
static void
|
||||
replay_bin_init (ReplayBin * self)
|
||||
{
|
||||
self->incoming_seek = NULL;
|
||||
self->outgoing_seek = NULL;
|
||||
self->trickmode_interval = 0;
|
||||
self->ts_offset = 0;
|
||||
self->sent_segment = FALSE;
|
||||
self->min_pts = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
replay_bin_class_init (ReplayBinClass * klass)
|
||||
{
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
replay_bin_new (void)
|
||||
{
|
||||
return GST_ELEMENT (g_object_new (replay_bin_get_type (), NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
demux_pad_added_cb (GstElement * demux, GstPad * pad, GstGhostPad * ghost)
|
||||
{
|
||||
GstCaps *caps = gst_pad_get_current_caps (pad);
|
||||
GstStructure *s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (gst_structure_has_name (s, "video/x-h264")) {
|
||||
gst_ghost_pad_set_target (ghost, pad);
|
||||
}
|
||||
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
static void
|
||||
query_seekable (GstPad * ghost, gint64 * start, gint64 * stop)
|
||||
{
|
||||
GstPad *target;
|
||||
GstQuery *query;
|
||||
GstFormat format;
|
||||
gboolean seekable;
|
||||
|
||||
target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost));
|
||||
|
||||
query = gst_query_new_seeking (GST_FORMAT_TIME);
|
||||
|
||||
gst_pad_query (target, query);
|
||||
|
||||
gst_query_parse_seeking (query, &format, &seekable, start, stop);
|
||||
|
||||
g_assert (seekable);
|
||||
|
||||
gst_object_unref (target);
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_seek (ReplayBin * self, GstPad * pad, GstEvent * ievent)
|
||||
{
|
||||
GstEvent *oevent = NULL;
|
||||
gdouble rate;
|
||||
GstFormat format;
|
||||
GstSeekFlags flags;
|
||||
GstSeekType start_type, stop_type;
|
||||
gint64 start, stop;
|
||||
gint64 istart, istop; /* Incoming */
|
||||
gint64 ustart, ustop; /* Upstream */
|
||||
gint64 ostart, ostop; /* Outgoing */
|
||||
guint32 seqnum = gst_event_get_seqnum (ievent);
|
||||
|
||||
gst_event_parse_seek (ievent, &rate, &format, &flags, &start_type, &start,
|
||||
&stop_type, &stop);
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (stop))
|
||||
stop = END_DATE;
|
||||
|
||||
gst_event_parse_seek_trickmode_interval (ievent, &self->trickmode_interval);
|
||||
|
||||
istart = start;
|
||||
istop = stop;
|
||||
|
||||
query_seekable (pad, &ustart, &ustop);
|
||||
|
||||
if (rate > 0) {
|
||||
/* First, from where we should seek the file */
|
||||
ostart = istart % (ustop + INTERVAL);
|
||||
|
||||
/* This may end up in our empty interval */
|
||||
if (ostart > ustop) {
|
||||
istart += ostart - ustop;
|
||||
ostart = 0;
|
||||
}
|
||||
|
||||
/* Then, up to where we should seek it */
|
||||
ostop = MIN (ustop, ostart + (istop - istart));
|
||||
} else {
|
||||
/* First up to where we should seek the file */
|
||||
ostop = istop % (ustop + INTERVAL);
|
||||
|
||||
/* This may end up in our empty interval */
|
||||
if (ostop > ustop) {
|
||||
istop -= ostop - ustop;
|
||||
ostop = ustop;
|
||||
}
|
||||
|
||||
ostart = MAX (0, ostop - (istop - istart));
|
||||
}
|
||||
|
||||
/* We may be left with nothing to actually play, in this
|
||||
* case we won't seek upstream, and emit the expected events
|
||||
* ourselves */
|
||||
if (istart > istop) {
|
||||
GstSegment segment;
|
||||
GstEvent *event;
|
||||
gboolean update;
|
||||
|
||||
event = gst_event_new_flush_start ();
|
||||
gst_event_set_seqnum (event, seqnum);
|
||||
gst_pad_push_event (pad, event);
|
||||
|
||||
event = gst_event_new_flush_stop (TRUE);
|
||||
gst_event_set_seqnum (event, seqnum);
|
||||
gst_pad_push_event (pad, event);
|
||||
|
||||
gst_segment_init (&segment, format);
|
||||
gst_segment_do_seek (&segment, rate, format, flags, start_type, start,
|
||||
stop_type, stop, &update);
|
||||
|
||||
event = gst_event_new_segment (&segment);
|
||||
gst_event_set_seqnum (event, seqnum);
|
||||
gst_pad_push_event (pad, event);
|
||||
|
||||
event = gst_event_new_eos ();
|
||||
gst_event_set_seqnum (event, seqnum);
|
||||
gst_pad_push_event (pad, event);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Lastly, how much will remain to play back (this remainder includes the interval) */
|
||||
if (stop - start > ostop - ostart)
|
||||
self->remainder = (stop - start) - (ostop - ostart);
|
||||
|
||||
flags |= GST_SEEK_FLAG_SEGMENT;
|
||||
|
||||
oevent =
|
||||
gst_event_new_seek (rate, format, flags, start_type, ostart, stop_type,
|
||||
ostop);
|
||||
gst_event_set_seek_trickmode_interval (oevent, self->trickmode_interval);
|
||||
gst_event_set_seqnum (oevent, seqnum);
|
||||
|
||||
GST_DEBUG ("Translated event to %" GST_PTR_FORMAT
|
||||
" (remainder: %" G_GINT64_FORMAT ")", oevent, self->remainder);
|
||||
|
||||
done:
|
||||
return oevent;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
replay_bin_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
ReplayBin *self = REPLAY_BIN (parent);
|
||||
gboolean ret = TRUE;
|
||||
gboolean forward = TRUE;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEEK:
|
||||
{
|
||||
GST_DEBUG ("Processing seek event %" GST_PTR_FORMAT, event);
|
||||
|
||||
self->incoming_seek = event;
|
||||
|
||||
gst_event_replace (&self->outgoing_seek, NULL);
|
||||
self->sent_segment = FALSE;
|
||||
|
||||
event = translate_seek (self, pad, event);
|
||||
|
||||
if (!event)
|
||||
forward = FALSE;
|
||||
else
|
||||
self->outgoing_seek = gst_event_ref (event);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (forward)
|
||||
return gst_pad_event_default (pad, parent, event);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
replay_bin_query_func (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||
{
|
||||
ReplayBin *self = REPLAY_BIN (parent);
|
||||
gboolean ret = TRUE;
|
||||
gboolean forward = TRUE;
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_SEEKING:
|
||||
/* We are seekable from the beginning till the end of time */
|
||||
gst_query_set_seeking (query, GST_FORMAT_TIME, TRUE, 0,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
forward = FALSE;
|
||||
break;
|
||||
case GST_QUERY_SEGMENT:
|
||||
gst_query_set_segment (query, self->segment.rate, self->segment.format,
|
||||
self->segment.start, self->segment.stop);
|
||||
forward = FALSE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Processed query %" GST_PTR_FORMAT, query);
|
||||
|
||||
if (forward)
|
||||
return gst_pad_query_default (pad, parent, query);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_segment (GstPad * pad, GstEvent * ievent)
|
||||
{
|
||||
ReplayBin *self = REPLAY_BIN (GST_OBJECT_PARENT (pad));
|
||||
GstEvent *ret;
|
||||
gdouble irate, orate;
|
||||
GstFormat iformat, oformat;
|
||||
GstSeekFlags iflags, oflags;
|
||||
GstSeekType istart_type, ostart_type, istop_type, ostop_type;
|
||||
gint64 istart, ostart, istop, ostop;
|
||||
gboolean update;
|
||||
|
||||
gst_event_parse_segment (ievent, &self->incoming_segment);
|
||||
|
||||
if (!self->outgoing_seek) {
|
||||
GstSegment segment;
|
||||
gboolean update;
|
||||
|
||||
gst_segment_init (&segment, GST_FORMAT_TIME);
|
||||
|
||||
gst_segment_do_seek (&segment, 1.0, GST_FORMAT_TIME, 0, GST_SEEK_TYPE_SET,
|
||||
0, GST_SEEK_TYPE_SET, END_DATE, &update);
|
||||
|
||||
ret = gst_event_new_segment (&segment);
|
||||
gst_event_unref (ievent);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!self->sent_segment) {
|
||||
gst_event_parse_seek (self->incoming_seek, &irate, &iformat, &iflags,
|
||||
&istart_type, &istart, &istop_type, &istop);
|
||||
gst_event_parse_seek (self->outgoing_seek, &orate, &oformat, &oflags,
|
||||
&ostart_type, &ostart, &ostop_type, &ostop);
|
||||
|
||||
if (istop == -1)
|
||||
istop = END_DATE;
|
||||
|
||||
if (self->incoming_segment->rate > 0)
|
||||
self->ts_offset = istart - ostart;
|
||||
else
|
||||
self->ts_offset = istop - ostop;
|
||||
|
||||
istart += self->incoming_segment->start - ostart;
|
||||
istop += self->incoming_segment->stop - ostop;
|
||||
|
||||
gst_segment_init (&self->segment, self->incoming_segment->format);
|
||||
|
||||
gst_segment_do_seek (&self->segment, self->incoming_segment->rate,
|
||||
self->incoming_segment->format,
|
||||
(GstSeekFlags) self->incoming_segment->flags, GST_SEEK_TYPE_SET,
|
||||
(guint64) istart, GST_SEEK_TYPE_SET, (guint64) istop, &update);
|
||||
|
||||
self->min_pts = istart;
|
||||
|
||||
ret = gst_event_new_segment (&self->segment);
|
||||
|
||||
self->sent_segment = TRUE;
|
||||
|
||||
gst_event_unref (ievent);
|
||||
|
||||
GST_DEBUG ("Translated segment: %" GST_PTR_FORMAT ", "
|
||||
"ts_offset: %" G_GUINT64_FORMAT, ret, self->ts_offset);
|
||||
} else {
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_segment_done (ReplayBin * self, GstPad * pad)
|
||||
{
|
||||
GstEvent *event;
|
||||
|
||||
if (self->remainder < INTERVAL) {
|
||||
self->remainder = 0;
|
||||
event = gst_event_new_eos ();
|
||||
gst_event_set_seqnum (event, gst_event_get_seqnum (self->incoming_seek));
|
||||
gst_pad_push_event (pad, event);
|
||||
} else {
|
||||
gint64 ustart, ustop;
|
||||
gint64 ostart, ostop;
|
||||
GstPad *target;
|
||||
GstStructure *s;
|
||||
|
||||
/* Signify the end of a contiguous section of recording */
|
||||
s = gst_structure_new ("GstNtpOffset",
|
||||
"ntp-offset", G_TYPE_UINT64, 0, "discont", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
|
||||
event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
|
||||
|
||||
gst_pad_push_event (pad, event);
|
||||
|
||||
query_seekable (pad, &ustart, &ustop);
|
||||
|
||||
self->remainder -= INTERVAL;
|
||||
|
||||
if (self->incoming_segment->rate > 0) {
|
||||
ostart = 0;
|
||||
ostop = MIN (ustop, self->remainder);
|
||||
} else {
|
||||
ostart = MAX (ustop - self->remainder, 0);
|
||||
ostop = ustop;
|
||||
}
|
||||
|
||||
self->remainder = MAX (self->remainder - ostop - ostart, 0);
|
||||
|
||||
event =
|
||||
gst_event_new_seek (self->segment.rate, self->segment.format,
|
||||
self->segment.flags & ~GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, ostart,
|
||||
GST_SEEK_TYPE_SET, ostop);
|
||||
gst_event_set_seek_trickmode_interval (event, self->trickmode_interval);
|
||||
|
||||
if (self->incoming_segment->rate > 0)
|
||||
self->ts_offset += INTERVAL + ustop;
|
||||
else
|
||||
self->ts_offset -= INTERVAL + ustop;
|
||||
|
||||
GST_DEBUG ("New offset: %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (self->ts_offset));
|
||||
|
||||
GST_DEBUG ("Seeking to %" GST_PTR_FORMAT, event);
|
||||
target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
|
||||
gst_pad_send_event (target, event);
|
||||
gst_object_unref (target);
|
||||
}
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
replay_bin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
|
||||
{
|
||||
ReplayBin *self = REPLAY_BIN (GST_OBJECT_PARENT (pad));
|
||||
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
|
||||
|
||||
GST_DEBUG ("Probed %" GST_PTR_FORMAT, info->data);
|
||||
|
||||
switch (GST_EVENT_TYPE (info->data)) {
|
||||
case GST_EVENT_SEGMENT:
|
||||
{
|
||||
GstEvent *translated;
|
||||
|
||||
GST_DEBUG ("Probed segment %" GST_PTR_FORMAT, info->data);
|
||||
|
||||
translated = translate_segment (pad, GST_EVENT (info->data));
|
||||
if (translated)
|
||||
info->data = translated;
|
||||
else
|
||||
ret = GST_PAD_PROBE_HANDLED;
|
||||
|
||||
break;
|
||||
}
|
||||
case GST_EVENT_SEGMENT_DONE:
|
||||
{
|
||||
handle_segment_done (self, pad);
|
||||
ret = GST_PAD_PROBE_HANDLED;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
replay_bin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
|
||||
{
|
||||
ReplayBin *self = REPLAY_BIN (GST_OBJECT_PARENT (pad));
|
||||
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
|
||||
|
||||
if (GST_BUFFER_PTS (info->data) > self->incoming_segment->stop) {
|
||||
ret = GST_PAD_PROBE_DROP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (info->data)))
|
||||
GST_BUFFER_PTS (info->data) += self->ts_offset;
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (info->data)))
|
||||
GST_BUFFER_DTS (info->data) += self->ts_offset;
|
||||
|
||||
GST_LOG ("Pushing buffer %" GST_PTR_FORMAT, info->data);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
create_replay_bin (GstElement * parent)
|
||||
{
|
||||
GstElement *ret, *src, *demux;
|
||||
GstPad *ghost;
|
||||
|
||||
ret = replay_bin_new ();
|
||||
if (!gst_bin_add (GST_BIN (parent), ret)) {
|
||||
gst_object_unref (ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MAKE_AND_ADD (src, ret, "filesrc", fail, NULL);
|
||||
MAKE_AND_ADD (demux, ret, "qtdemux", fail, NULL);
|
||||
|
||||
ghost = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
|
||||
gst_element_add_pad (ret, ghost);
|
||||
|
||||
gst_pad_set_event_function (ghost, replay_bin_event_func);
|
||||
gst_pad_add_probe (ghost, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
||||
replay_bin_event_probe, NULL, NULL);
|
||||
gst_pad_add_probe (ghost, GST_PAD_PROBE_TYPE_BUFFER, replay_bin_buffer_probe,
|
||||
NULL, NULL);
|
||||
gst_pad_set_query_function (ghost, replay_bin_query_func);
|
||||
|
||||
if (!gst_element_link (src, demux))
|
||||
goto fail;
|
||||
|
||||
g_object_set (src, "location", filename, NULL);
|
||||
g_signal_connect (demux, "pad-added", G_CALLBACK (demux_pad_added_cb), ghost);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
ret = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* A simple factory to set up our replay bin */
|
||||
|
||||
struct _OnvifFactory
|
||||
{
|
||||
GstRTSPOnvifMediaFactory parent;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (OnvifFactory, onvif_factory, GST_TYPE_RTSP_MEDIA_FACTORY);
|
||||
|
||||
static void
|
||||
onvif_factory_init (OnvifFactory * factory)
|
||||
{
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
onvif_factory_create_element (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstElement *replay_bin, *q1, *parse, *pay, *onvifts, *q2;
|
||||
GstElement *ret = gst_bin_new (NULL);
|
||||
GstElement *pbin = gst_bin_new ("pay0");
|
||||
GstPad *sinkpad, *srcpad;
|
||||
|
||||
if (!(replay_bin = create_replay_bin (ret)))
|
||||
goto fail;
|
||||
|
||||
MAKE_AND_ADD (q1, pbin, "queue", fail, NULL);
|
||||
MAKE_AND_ADD (parse, pbin, "h264parse", fail, NULL);
|
||||
MAKE_AND_ADD (pay, pbin, "rtph264pay", fail, NULL);
|
||||
MAKE_AND_ADD (onvifts, pbin, "rtponviftimestamp", fail, NULL);
|
||||
MAKE_AND_ADD (q2, pbin, "queue", fail, NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (ret), pbin);
|
||||
|
||||
if (!gst_element_link_many (q1, parse, pay, onvifts, q2, NULL))
|
||||
goto fail;
|
||||
|
||||
sinkpad = gst_element_get_static_pad (q1, "sink");
|
||||
gst_element_add_pad (pbin, gst_ghost_pad_new ("sink", sinkpad));
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
if (!gst_element_link (replay_bin, pbin))
|
||||
goto fail;
|
||||
|
||||
srcpad = gst_element_get_static_pad (q2, "src");
|
||||
gst_element_add_pad (pbin, gst_ghost_pad_new ("src", srcpad));
|
||||
gst_object_unref (srcpad);
|
||||
|
||||
g_object_set (onvifts, "set-t-bit", TRUE, "set-e-bit", TRUE, "ntp-offset",
|
||||
G_GUINT64_CONSTANT (0), "drop-out-of-segment", FALSE, NULL);
|
||||
|
||||
gst_element_set_clock (onvifts, gst_system_clock_obtain ());
|
||||
|
||||
done:
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
gst_object_unref (ret);
|
||||
ret = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
static void
|
||||
onvif_factory_class_init (OnvifFactoryClass * klass)
|
||||
{
|
||||
GstRTSPMediaFactoryClass *mf_class = GST_RTSP_MEDIA_FACTORY_CLASS (klass);
|
||||
|
||||
mf_class->create_element = onvif_factory_create_element;
|
||||
}
|
||||
|
||||
static GstRTSPMediaFactory *
|
||||
onvif_factory_new (void)
|
||||
{
|
||||
GstRTSPMediaFactory *result;
|
||||
|
||||
result =
|
||||
GST_RTSP_MEDIA_FACTORY (g_object_new (onvif_factory_get_type (), NULL));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
gchar *service;
|
||||
|
||||
optctx = g_option_context_new ("<filename.mp4> - ONVIF RTSP Server, MP4");
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
filename = argv[1];
|
||||
g_option_context_free (optctx);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (onvif_server_debug, "onvif-server", 0,
|
||||
"ONVIF server");
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
server = gst_rtsp_onvif_server_new ();
|
||||
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
factory = onvif_factory_new ();
|
||||
gst_rtsp_media_factory_set_media_gtype (factory, GST_TYPE_RTSP_ONVIF_MEDIA);
|
||||
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
g_object_unref (mounts);
|
||||
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
service = gst_rtsp_server_get_service (server);
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", service);
|
||||
g_free (service);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
32
examples/test-onvif-server.h
Normal file
32
examples/test-onvif-server.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE (ReplayBin, replay_bin, REPLAY, BIN, GstBin);
|
||||
|
||||
G_DECLARE_FINAL_TYPE (OnvifFactory, onvif_factory, ONVIF, FACTORY,
|
||||
GstRTSPOnvifMediaFactory);
|
||||
|
||||
G_END_DECLS
|
67
examples/test-readme.c
Normal file
67
examples/test-readme.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( videotestsrc is-live=1 ! x264enc ! rtph264pay name=pay0 pt=96 )");
|
||||
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
179
examples/test-record-auth.c
Normal file
179
examples/test-record-auth.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
/* define this if you want the server to use TLS */
|
||||
//#define WITH_TLS
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
gchar *basic;
|
||||
#ifdef WITH_TLS
|
||||
GTlsCertificate *cert;
|
||||
#endif
|
||||
|
||||
optctx = g_option_context_new ("<launch line> - Test RTSP Server, Launch\n\n"
|
||||
"Example: \"( decodebin name=depay0 ! autovideosink )\"");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named depay%d. Each
|
||||
* element with depay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_transport_mode (factory,
|
||||
GST_RTSP_TRANSPORT_MODE_RECORD);
|
||||
gst_rtsp_media_factory_set_launch (factory, argv[1]);
|
||||
gst_rtsp_media_factory_set_latency (factory, 2000);
|
||||
#ifdef WITH_TLS
|
||||
gst_rtsp_media_factory_set_profiles (factory,
|
||||
GST_RTSP_PROFILE_SAVP | GST_RTSP_PROFILE_SAVPF);
|
||||
#else
|
||||
gst_rtsp_media_factory_set_profiles (factory,
|
||||
GST_RTSP_PROFILE_AVP | GST_RTSP_PROFILE_AVPF);
|
||||
#endif
|
||||
|
||||
/* allow user to access this resource */
|
||||
gst_rtsp_media_factory_add_role (factory, "user",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
/* Anonymous users can see but not construct, so get UNAUTHORIZED */
|
||||
gst_rtsp_media_factory_add_role (factory, "anonymous",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* Set up the auth for user account */
|
||||
/* make a new authentication manager */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
#ifdef WITH_TLS
|
||||
cert = g_tls_certificate_new_from_pem ("-----BEGIN CERTIFICATE-----"
|
||||
"MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk"
|
||||
"ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp"
|
||||
"ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq"
|
||||
"hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx"
|
||||
"NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW"
|
||||
"B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3"
|
||||
"DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf"
|
||||
"hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC"
|
||||
"MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1"
|
||||
"Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk"
|
||||
"PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH"
|
||||
"QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4="
|
||||
"-----END CERTIFICATE-----"
|
||||
"-----BEGIN PRIVATE KEY-----"
|
||||
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA2EnE8ZOeVwZmwzPc"
|
||||
"88DvoK1ckhOK7nVrsx9j6TmyKJ6m34UaAGpSlZbhvY72xyPNXl8QnUjm79SgT9bG"
|
||||
"zeUc6QIDAQABAkBRFJZ32VbqWMP9OVwDJLiwC01AlYLnka0mIQZbT/2xq9dUc9GW"
|
||||
"U3kiVw4lL8v/+sPjtTPCYYdzHHOyDen6znVhAiEA9qJT7BtQvRxCvGrAhr9MS022"
|
||||
"tTdPbW829BoUtIeH64cCIQDggG5i48v7HPacPBIH1RaSVhXl8qHCpQD3qrIw3FMw"
|
||||
"DwIga8PqH5Sf5sHedy2+CiK0V4MRfoU4c3zQ6kArI+bEgSkCIQCLA1vXBiE31B5s"
|
||||
"bdHoYa1BXebfZVd+1Hd95IfEM5mbRwIgSkDuQwV55BBlvWph3U8wVIMIb4GStaH8"
|
||||
"W535W8UBbEg=" "-----END PRIVATE KEY-----", -1, &error);
|
||||
if (cert == NULL) {
|
||||
g_printerr ("failed to parse PEM: %s\n", error->message);
|
||||
return -1;
|
||||
}
|
||||
gst_rtsp_auth_set_tls_certificate (auth, cert);
|
||||
g_object_unref (cert);
|
||||
#endif
|
||||
|
||||
/* make default token - anonymous unauthenticated access */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"anonymous", NULL);
|
||||
gst_rtsp_auth_set_default_token (auth, token);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* make user token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("user", "password");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* set as the server authentication manager */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
g_object_unref (auth);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
#ifdef WITH_TLS
|
||||
g_print ("stream ready at rtsps://127.0.0.1:%s/test\n", port);
|
||||
#else
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
#endif
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
101
examples/test-record.c
Normal file
101
examples/test-record.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
|
||||
optctx = g_option_context_new ("<launch line> - Test RTSP Server, Launch\n\n"
|
||||
"Example: \"( decodebin name=depay0 ! autovideosink )\"");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named depay%d. Each
|
||||
* element with depay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_transport_mode (factory,
|
||||
GST_RTSP_TRANSPORT_MODE_RECORD);
|
||||
gst_rtsp_media_factory_set_launch (factory, argv[1]);
|
||||
gst_rtsp_media_factory_set_latency (factory, 2000);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
g_print ("On the sender, send a stream with rtspclientsink:\n"
|
||||
" gst-launch-1.0 videotestsrc ! x264enc ! rtspclientsink location=rtsp://127.0.0.1:%s/test\n",
|
||||
port);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
931
examples/test-replay-server.c
Normal file
931
examples/test-replay-server.c
Normal file
|
@ -0,0 +1,931 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
#include "test-replay-server.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (replay_server_debug);
|
||||
#define GST_CAT_DEFAULT (replay_server_debug)
|
||||
|
||||
static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw");
|
||||
static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw");
|
||||
|
||||
static GList
|
||||
* gst_rtsp_media_factory_replay_get_demuxers (GstRTSPMediaFactoryReplay *
|
||||
factory);
|
||||
static GList
|
||||
* gst_rtsp_media_factory_replay_get_payloaders (GstRTSPMediaFactoryReplay *
|
||||
factory);
|
||||
static GList
|
||||
* gst_rtsp_media_factory_replay_get_decoders (GstRTSPMediaFactoryReplay *
|
||||
factory);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstPad *srcpad;
|
||||
gulong block_id;
|
||||
} GstReplayBinPad;
|
||||
|
||||
static void
|
||||
gst_replay_bin_pad_unblock_and_free (GstReplayBinPad * pad)
|
||||
{
|
||||
if (pad->srcpad && pad->block_id) {
|
||||
GST_DEBUG_OBJECT (pad->srcpad, "Unblock");
|
||||
gst_pad_remove_probe (pad->srcpad, pad->block_id);
|
||||
pad->block_id = 0;
|
||||
}
|
||||
|
||||
gst_clear_object (&pad->srcpad);
|
||||
g_free (pad);
|
||||
}
|
||||
|
||||
/* NOTE: this bin implementation is almost completely taken from rtsp-media-factory-uri
|
||||
* but this example doesn't use the GstRTSPMediaFactoryURI object so that
|
||||
* we can handle events and messages ourselves.
|
||||
* Specifically,
|
||||
* - Handle segment-done message for looping given source
|
||||
* - Drop all incoming seek event because client seek is not implemented
|
||||
* and do initial segment seeking on no-more-pads signal
|
||||
*/
|
||||
struct _GstReplayBin
|
||||
{
|
||||
GstBin parent;
|
||||
|
||||
gint64 num_loops;
|
||||
|
||||
GstCaps *raw_vcaps;
|
||||
GstCaps *raw_acaps;
|
||||
|
||||
guint pt;
|
||||
|
||||
/* without ref */
|
||||
GstElement *uridecodebin;
|
||||
GstElement *inner_bin;
|
||||
|
||||
/* holds ref */
|
||||
GstRTSPMediaFactoryReplay *factory;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GList *srcpads;
|
||||
};
|
||||
|
||||
static void gst_replay_bin_dispose (GObject * object);
|
||||
static void gst_replay_bin_finalize (GObject * object);
|
||||
static void gst_replay_bin_handle_message (GstBin * bin, GstMessage * message);
|
||||
|
||||
static gboolean autoplug_continue_cb (GstElement * dbin, GstPad * pad,
|
||||
GstCaps * caps, GstReplayBin * self);
|
||||
static void pad_added_cb (GstElement * dbin, GstPad * pad, GstReplayBin * self);
|
||||
static void no_more_pads_cb (GstElement * uribin, GstReplayBin * self);
|
||||
|
||||
#define gst_replay_bin_parent_class bin_parent_class
|
||||
G_DEFINE_TYPE (GstReplayBin, gst_replay_bin, GST_TYPE_BIN);
|
||||
|
||||
static void
|
||||
gst_replay_bin_class_init (GstReplayBinClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstBinClass *bin_class = GST_BIN_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gst_replay_bin_dispose;
|
||||
gobject_class->finalize = gst_replay_bin_finalize;
|
||||
|
||||
bin_class->handle_message = GST_DEBUG_FUNCPTR (gst_replay_bin_handle_message);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_init (GstReplayBin * self)
|
||||
{
|
||||
self->raw_vcaps = gst_static_caps_get (&raw_video_caps);
|
||||
self->raw_acaps = gst_static_caps_get (&raw_audio_caps);
|
||||
|
||||
self->uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
|
||||
if (!self->uridecodebin) {
|
||||
GST_ERROR_OBJECT (self, "uridecodebin is unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
/* our bin will dynamically expose payloaded pads */
|
||||
self->inner_bin = gst_bin_new ("dynpay0");
|
||||
gst_bin_add (GST_BIN_CAST (self), self->inner_bin);
|
||||
gst_bin_add (GST_BIN_CAST (self->inner_bin), self->uridecodebin);
|
||||
|
||||
g_signal_connect (self->uridecodebin, "autoplug-continue",
|
||||
G_CALLBACK (autoplug_continue_cb), self);
|
||||
g_signal_connect (self->uridecodebin, "pad-added",
|
||||
G_CALLBACK (pad_added_cb), self);
|
||||
g_signal_connect (self->uridecodebin, "no-more-pads",
|
||||
G_CALLBACK (no_more_pads_cb), self);
|
||||
|
||||
self->pt = 96;
|
||||
|
||||
g_mutex_init (&self->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_dispose (GObject * object)
|
||||
{
|
||||
GstReplayBin *self = GST_REPLAY_BIN (object);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "dispose");
|
||||
|
||||
gst_clear_caps (&self->raw_vcaps);
|
||||
gst_clear_caps (&self->raw_acaps);
|
||||
gst_clear_object (&self->factory);
|
||||
|
||||
if (self->srcpads) {
|
||||
g_list_free_full (self->srcpads,
|
||||
(GDestroyNotify) gst_replay_bin_pad_unblock_and_free);
|
||||
self->srcpads = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (bin_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_finalize (GObject * object)
|
||||
{
|
||||
GstReplayBin *self = GST_REPLAY_BIN (object);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
G_OBJECT_CLASS (bin_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
send_eos_foreach_srcpad (GstElement * element, GstPad * pad, gpointer user_data)
|
||||
{
|
||||
GST_DEBUG_OBJECT (pad, "Sending EOS to downstream");
|
||||
gst_pad_push_event (pad, gst_event_new_eos ());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_do_segment_seek (GstElement * element, GstReplayBin * self)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
ret = gst_element_seek (element, 1.0, GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT,
|
||||
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
|
||||
|
||||
if (!ret) {
|
||||
GST_WARNING_OBJECT (self, "segment seeking failed");
|
||||
gst_element_foreach_src_pad (element,
|
||||
(GstElementForeachPadFunc) send_eos_foreach_srcpad, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_handle_message (GstBin * bin, GstMessage * message)
|
||||
{
|
||||
GstReplayBin *self = GST_REPLAY_BIN (bin);
|
||||
|
||||
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_SEGMENT_DONE) {
|
||||
gboolean next_loop = TRUE;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Have segment done message");
|
||||
|
||||
g_mutex_lock (&self->lock);
|
||||
if (self->num_loops != -1) {
|
||||
self->num_loops--;
|
||||
|
||||
if (self->num_loops < 1)
|
||||
next_loop = FALSE;
|
||||
}
|
||||
|
||||
if (next_loop) {
|
||||
/* Send seek event from non-streaming thread */
|
||||
gst_element_call_async (GST_ELEMENT_CAST (self->uridecodebin),
|
||||
(GstElementCallAsyncFunc) gst_replay_bin_do_segment_seek, self, NULL);
|
||||
} else {
|
||||
gst_element_foreach_src_pad (GST_ELEMENT_CAST (self->uridecodebin),
|
||||
(GstElementForeachPadFunc) send_eos_foreach_srcpad, NULL);
|
||||
}
|
||||
|
||||
g_mutex_unlock (&self->lock);
|
||||
}
|
||||
|
||||
GST_BIN_CLASS (bin_parent_class)->handle_message (bin, message);
|
||||
}
|
||||
|
||||
static GstElementFactory *
|
||||
find_payloader (GstReplayBin * self, GstCaps * caps)
|
||||
{
|
||||
GList *list;
|
||||
GstElementFactory *factory = NULL;
|
||||
gboolean autoplug_more = FALSE;
|
||||
GList *demuxers = NULL;
|
||||
GList *payloaders = NULL;
|
||||
|
||||
demuxers = gst_rtsp_media_factory_replay_get_demuxers (self->factory);
|
||||
|
||||
/* first find a demuxer that can link */
|
||||
list = gst_element_factory_list_filter (demuxers, caps, GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list) {
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
gboolean parsed = FALSE;
|
||||
gint mpegversion = 0;
|
||||
|
||||
if (!gst_structure_get_boolean (structure, "parsed", &parsed) &&
|
||||
gst_structure_has_name (structure, "audio/mpeg") &&
|
||||
gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
|
||||
(mpegversion == 2 || mpegversion == 4)) {
|
||||
/* for AAC it's framed=true instead of parsed=true */
|
||||
gst_structure_get_boolean (structure, "framed", &parsed);
|
||||
}
|
||||
|
||||
/* Avoid plugging parsers in a loop. This is not 100% correct, as some
|
||||
* parsers don't set parsed=true in caps. We should do something like
|
||||
* decodebin does and track decode chains and elements plugged in those
|
||||
* chains...
|
||||
*/
|
||||
if (parsed) {
|
||||
GList *walk;
|
||||
const gchar *klass;
|
||||
|
||||
for (walk = list; walk; walk = walk->next) {
|
||||
factory = GST_ELEMENT_FACTORY (walk->data);
|
||||
klass = gst_element_factory_get_metadata (factory,
|
||||
GST_ELEMENT_METADATA_KLASS);
|
||||
if (strstr (klass, "Parser"))
|
||||
/* caps have parsed=true, so skip this parser to avoid loops */
|
||||
continue;
|
||||
|
||||
autoplug_more = TRUE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* caps don't have parsed=true set and we have a demuxer/parser */
|
||||
autoplug_more = TRUE;
|
||||
}
|
||||
|
||||
gst_plugin_feature_list_free (list);
|
||||
}
|
||||
|
||||
if (autoplug_more)
|
||||
/* we have a demuxer, try that one first */
|
||||
return NULL;
|
||||
|
||||
payloaders = gst_rtsp_media_factory_replay_get_payloaders (self->factory);
|
||||
|
||||
/* no demuxer try a depayloader */
|
||||
list = gst_element_factory_list_filter (payloaders,
|
||||
caps, GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list == NULL) {
|
||||
GList *decoders =
|
||||
gst_rtsp_media_factory_replay_get_decoders (self->factory);
|
||||
/* no depayloader, try a decoder, we'll get to a payloader for a decoded
|
||||
* video or audio format, worst case. */
|
||||
list = gst_element_factory_list_filter (decoders,
|
||||
caps, GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list != NULL) {
|
||||
/* we have a decoder, try that one first */
|
||||
gst_plugin_feature_list_free (list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (list != NULL) {
|
||||
factory = GST_ELEMENT_FACTORY_CAST (list->data);
|
||||
g_object_ref (factory);
|
||||
gst_plugin_feature_list_free (list);
|
||||
}
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
autoplug_continue_cb (GstElement * dbin, GstPad * pad, GstCaps * caps,
|
||||
GstReplayBin * self)
|
||||
{
|
||||
GstElementFactory *factory;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "found pad %s:%s of caps %" GST_PTR_FORMAT,
|
||||
GST_DEBUG_PAD_NAME (pad), caps);
|
||||
|
||||
if (!(factory = find_payloader (self, caps)))
|
||||
goto no_factory;
|
||||
|
||||
/* we found a payloader, stop autoplugging so we can plug the
|
||||
* payloader. */
|
||||
GST_DEBUG_OBJECT (self, "found factory %s",
|
||||
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
||||
gst_object_unref (factory);
|
||||
|
||||
return FALSE;
|
||||
|
||||
no_factory:
|
||||
{
|
||||
/* no payloader, continue autoplugging */
|
||||
GST_DEBUG_OBJECT (self, "no payloader found for caps %" GST_PTR_FORMAT,
|
||||
caps);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
replay_bin_sink_probe (GstPad * pad, GstPadProbeInfo * info,
|
||||
GstReplayBin * self)
|
||||
{
|
||||
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
|
||||
|
||||
if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
|
||||
GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEEK:
|
||||
/* Ideally this shouldn't happen because we are responding
|
||||
* seeking query with non-seekable */
|
||||
GST_DEBUG_OBJECT (pad, "Drop seek event");
|
||||
ret = GST_PAD_PROBE_DROP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) {
|
||||
GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_SEEKING:
|
||||
{
|
||||
/* FIXME: client seek is not implemented */
|
||||
gst_query_set_seeking (query, GST_FORMAT_TIME, FALSE, 0,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
ret = GST_PAD_PROBE_HANDLED;
|
||||
break;
|
||||
}
|
||||
case GST_QUERY_SEGMENT:
|
||||
/* client seeking is not considered in here */
|
||||
gst_query_set_segment (query,
|
||||
1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE);
|
||||
ret = GST_PAD_PROBE_HANDLED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
replay_bin_src_block (GstPad * pad, GstPadProbeInfo * info, GstReplayBin * self)
|
||||
{
|
||||
GST_DEBUG_OBJECT (pad, "Block pad");
|
||||
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * dbin, GstPad * pad, GstReplayBin * self)
|
||||
{
|
||||
GstElementFactory *factory;
|
||||
GstElement *payloader;
|
||||
GstCaps *caps;
|
||||
GstPad *sinkpad, *srcpad, *ghostpad;
|
||||
GstPad *dpad = pad;
|
||||
GstElement *convert;
|
||||
gchar *padname, *payloader_name;
|
||||
GstElement *inner_bin = self->inner_bin;
|
||||
GstReplayBinPad *bin_pad;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "added pad %s:%s", GST_DEBUG_PAD_NAME (pad));
|
||||
|
||||
/* ref to make refcounting easier later */
|
||||
gst_object_ref (pad);
|
||||
padname = gst_pad_get_name (pad);
|
||||
|
||||
/* get pad caps first, then call get_caps, then fail */
|
||||
if ((caps = gst_pad_get_current_caps (pad)) == NULL)
|
||||
if ((caps = gst_pad_query_caps (pad, NULL)) == NULL)
|
||||
goto no_caps;
|
||||
|
||||
/* check for raw caps */
|
||||
if (gst_caps_can_intersect (caps, self->raw_vcaps)) {
|
||||
/* we have raw video caps, insert converter */
|
||||
convert = gst_element_factory_make ("videoconvert", NULL);
|
||||
} else if (gst_caps_can_intersect (caps, self->raw_acaps)) {
|
||||
/* we have raw audio caps, insert converter */
|
||||
convert = gst_element_factory_make ("audioconvert", NULL);
|
||||
} else {
|
||||
convert = NULL;
|
||||
}
|
||||
|
||||
if (convert) {
|
||||
gst_bin_add (GST_BIN_CAST (inner_bin), convert);
|
||||
gst_element_sync_state_with_parent (convert);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (convert, "sink");
|
||||
gst_pad_link (pad, sinkpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
/* unref old pad, we reffed before */
|
||||
gst_object_unref (pad);
|
||||
|
||||
/* continue with new pad and caps */
|
||||
pad = gst_element_get_static_pad (convert, "src");
|
||||
if ((caps = gst_pad_get_current_caps (pad)) == NULL)
|
||||
if ((caps = gst_pad_query_caps (pad, NULL)) == NULL)
|
||||
goto no_caps;
|
||||
}
|
||||
|
||||
if (!(factory = find_payloader (self, caps)))
|
||||
goto no_factory;
|
||||
|
||||
gst_caps_unref (caps);
|
||||
|
||||
/* we have a payloader now */
|
||||
GST_DEBUG_OBJECT (self, "found payloader factory %s",
|
||||
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
||||
|
||||
payloader_name = g_strdup_printf ("pay_%s", padname);
|
||||
payloader = gst_element_factory_create (factory, payloader_name);
|
||||
g_free (payloader_name);
|
||||
if (payloader == NULL)
|
||||
goto no_payloader;
|
||||
|
||||
g_object_set (payloader, "pt", self->pt, NULL);
|
||||
self->pt++;
|
||||
|
||||
if (g_object_class_find_property (G_OBJECT_GET_CLASS (payloader),
|
||||
"buffer-list"))
|
||||
g_object_set (payloader, "buffer-list", TRUE, NULL);
|
||||
|
||||
/* add the payloader to the pipeline */
|
||||
gst_bin_add (GST_BIN_CAST (inner_bin), payloader);
|
||||
gst_element_sync_state_with_parent (payloader);
|
||||
|
||||
/* link the pad to the sinkpad of the payloader */
|
||||
sinkpad = gst_element_get_static_pad (payloader, "sink");
|
||||
gst_pad_link (pad, sinkpad);
|
||||
gst_object_unref (pad);
|
||||
|
||||
/* Add pad probe to handle events */
|
||||
gst_pad_add_probe (sinkpad,
|
||||
GST_PAD_PROBE_TYPE_EVENT_UPSTREAM | GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
|
||||
(GstPadProbeCallback) replay_bin_sink_probe, self, NULL);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
/* block data for initial segment seeking */
|
||||
bin_pad = g_new0 (GstReplayBinPad, 1);
|
||||
|
||||
/* Move ownership of pad to this struct */
|
||||
bin_pad->srcpad = gst_object_ref (dpad);
|
||||
bin_pad->block_id =
|
||||
gst_pad_add_probe (dpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
|
||||
(GstPadProbeCallback) replay_bin_src_block, self, NULL);
|
||||
g_mutex_lock (&self->lock);
|
||||
self->srcpads = g_list_append (self->srcpads, bin_pad);
|
||||
g_mutex_unlock (&self->lock);
|
||||
|
||||
/* now expose the srcpad of the payloader as a ghostpad with the same name
|
||||
* as the uridecodebin pad name. */
|
||||
srcpad = gst_element_get_static_pad (payloader, "src");
|
||||
ghostpad = gst_ghost_pad_new (padname, srcpad);
|
||||
gst_object_unref (srcpad);
|
||||
g_free (padname);
|
||||
|
||||
gst_pad_set_active (ghostpad, TRUE);
|
||||
gst_element_add_pad (inner_bin, ghostpad);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
no_caps:
|
||||
{
|
||||
GST_WARNING ("could not get caps from pad");
|
||||
g_free (padname);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
no_factory:
|
||||
{
|
||||
GST_DEBUG ("no payloader found");
|
||||
g_free (padname);
|
||||
gst_caps_unref (caps);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
no_payloader:
|
||||
{
|
||||
GST_ERROR ("could not create payloader from factory");
|
||||
g_free (padname);
|
||||
gst_caps_unref (caps);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_replay_bin_do_initial_segment_seek (GstElement * element,
|
||||
GstReplayBin * self)
|
||||
{
|
||||
gboolean ret;
|
||||
GstQuery *query;
|
||||
gboolean seekable;
|
||||
|
||||
query = gst_query_new_seeking (GST_FORMAT_TIME);
|
||||
ret = gst_element_query (element, query);
|
||||
|
||||
if (!ret) {
|
||||
GST_WARNING_OBJECT (self, "Cannot query seeking");
|
||||
gst_query_unref (query);
|
||||
goto done;
|
||||
}
|
||||
|
||||
gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
|
||||
gst_query_unref (query);
|
||||
|
||||
if (!seekable) {
|
||||
GST_WARNING_OBJECT (self, "Source is not seekable");
|
||||
ret = FALSE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = gst_element_seek (element, 1.0, GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
|
||||
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
|
||||
|
||||
if (!ret)
|
||||
GST_WARNING_OBJECT (self, "segment seeking failed");
|
||||
|
||||
done:
|
||||
/* Unblock all pads then */
|
||||
g_mutex_lock (&self->lock);
|
||||
if (self->srcpads) {
|
||||
g_list_free_full (self->srcpads,
|
||||
(GDestroyNotify) gst_replay_bin_pad_unblock_and_free);
|
||||
self->srcpads = NULL;
|
||||
}
|
||||
g_mutex_unlock (&self->lock);
|
||||
|
||||
if (!ret) {
|
||||
GST_WARNING_OBJECT (self, "Sending eos to all pads");
|
||||
gst_element_foreach_src_pad (element,
|
||||
(GstElementForeachPadFunc) send_eos_foreach_srcpad, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
no_more_pads_cb (GstElement * uribin, GstReplayBin * self)
|
||||
{
|
||||
GST_DEBUG_OBJECT (self, "no-more-pads");
|
||||
gst_element_no_more_pads (GST_ELEMENT_CAST (self->inner_bin));
|
||||
|
||||
/* Flush seeking from streaming thread might not be good idea.
|
||||
* Do this from another (non-streaming) thread */
|
||||
gst_element_call_async (GST_ELEMENT_CAST (self->uridecodebin),
|
||||
(GstElementCallAsyncFunc) gst_replay_bin_do_initial_segment_seek,
|
||||
self, NULL);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_replay_bin_new (const gchar * uri, gint64 num_loops,
|
||||
GstRTSPMediaFactoryReplay * factory, const gchar * name)
|
||||
{
|
||||
GstReplayBin *self;
|
||||
|
||||
g_return_val_if_fail (uri != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
|
||||
|
||||
if (!name)
|
||||
name = "GstRelayBin";
|
||||
|
||||
self = GST_REPLAY_BIN (g_object_new (GST_TYPE_REPLAY_BIN,
|
||||
"name", name, NULL));
|
||||
|
||||
if (!self->uridecodebin) {
|
||||
gst_object_unref (self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_object_set (self->uridecodebin, "uri", uri, NULL);
|
||||
self->factory = g_object_ref (factory);
|
||||
self->num_loops = num_loops;
|
||||
|
||||
return GST_ELEMENT_CAST (self);
|
||||
}
|
||||
|
||||
struct _GstRTSPMediaFactoryReplay
|
||||
{
|
||||
GstRTSPMediaFactory parent;
|
||||
|
||||
gchar *uri;
|
||||
|
||||
GList *demuxers;
|
||||
GList *payloaders;
|
||||
GList *decoders;
|
||||
|
||||
gint64 num_loops;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_URI,
|
||||
PROP_NUM_LOOPS,
|
||||
};
|
||||
|
||||
#define DEFAULT_NUM_LOOPS (-1)
|
||||
|
||||
static void gst_rtsp_media_factory_replay_get_property (GObject * object,
|
||||
guint propid, GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_media_factory_replay_set_property (GObject * object,
|
||||
guint propid, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_media_factory_replay_finalize (GObject * object);
|
||||
|
||||
static GstElement
|
||||
* gst_rtsp_media_factory_replay_create_element (GstRTSPMediaFactory *
|
||||
factory, const GstRTSPUrl * url);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GList *demux;
|
||||
GList *payload;
|
||||
GList *decode;
|
||||
} FilterData;
|
||||
|
||||
static gboolean
|
||||
payloader_filter (GstPluginFeature * feature, FilterData * self);
|
||||
|
||||
#define gst_rtsp_media_factory_replay_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstRTSPMediaFactoryReplay,
|
||||
gst_rtsp_media_factory_replay, GST_TYPE_RTSP_MEDIA_FACTORY);
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_replay_class_init (GstRTSPMediaFactoryReplayClass
|
||||
* klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstRTSPMediaFactoryClass *mf_class = GST_RTSP_MEDIA_FACTORY_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gst_rtsp_media_factory_replay_get_property;
|
||||
gobject_class->set_property = gst_rtsp_media_factory_replay_set_property;
|
||||
gobject_class->finalize = gst_rtsp_media_factory_replay_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_URI,
|
||||
g_param_spec_string ("uri", "URI",
|
||||
"The URI of the resource to stream", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_NUM_LOOPS,
|
||||
g_param_spec_int64 ("num-loops", "Num Loops",
|
||||
"The number of loops (-1 = infinite)", -1, G_MAXINT64,
|
||||
DEFAULT_NUM_LOOPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
mf_class->create_element =
|
||||
GST_DEBUG_FUNCPTR (gst_rtsp_media_factory_replay_create_element);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_replay_init (GstRTSPMediaFactoryReplay * self)
|
||||
{
|
||||
FilterData data = { NULL, };
|
||||
|
||||
/* get the feature list using the filter */
|
||||
gst_registry_feature_filter (gst_registry_get (), (GstPluginFeatureFilter)
|
||||
payloader_filter, FALSE, &data);
|
||||
|
||||
/* sort */
|
||||
self->demuxers =
|
||||
g_list_sort (data.demux, gst_plugin_feature_rank_compare_func);
|
||||
self->payloaders =
|
||||
g_list_sort (data.payload, gst_plugin_feature_rank_compare_func);
|
||||
self->decoders =
|
||||
g_list_sort (data.decode, gst_plugin_feature_rank_compare_func);
|
||||
|
||||
self->num_loops = DEFAULT_NUM_LOOPS;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_replay_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPMediaFactoryReplay *self = GST_RTSP_MEDIA_FACTORY_REPLAY (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_URI:
|
||||
g_value_take_string (value, self->uri);
|
||||
break;
|
||||
case PROP_NUM_LOOPS:
|
||||
g_value_set_int64 (value, self->num_loops);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_replay_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPMediaFactoryReplay *self = GST_RTSP_MEDIA_FACTORY_REPLAY (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_URI:
|
||||
g_free (self->uri);
|
||||
self->uri = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_NUM_LOOPS:
|
||||
self->num_loops = g_value_get_int64 (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_replay_finalize (GObject * object)
|
||||
{
|
||||
GstRTSPMediaFactoryReplay *self = GST_RTSP_MEDIA_FACTORY_REPLAY (object);
|
||||
|
||||
g_free (self->uri);
|
||||
|
||||
gst_plugin_feature_list_free (self->demuxers);
|
||||
gst_plugin_feature_list_free (self->payloaders);
|
||||
gst_plugin_feature_list_free (self->decoders);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_rtsp_media_factory_replay_create_element (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPMediaFactoryReplay *self = GST_RTSP_MEDIA_FACTORY_REPLAY (factory);
|
||||
|
||||
return gst_replay_bin_new (self->uri, self->num_loops, self,
|
||||
"GstRTSPMediaFactoryReplay");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
payloader_filter (GstPluginFeature * feature, FilterData * data)
|
||||
{
|
||||
const gchar *klass;
|
||||
GstElementFactory *fact;
|
||||
GList **list = NULL;
|
||||
|
||||
/* we only care about element factories */
|
||||
if (G_UNLIKELY (!GST_IS_ELEMENT_FACTORY (feature)))
|
||||
return FALSE;
|
||||
|
||||
if (gst_plugin_feature_get_rank (feature) < GST_RANK_MARGINAL)
|
||||
return FALSE;
|
||||
|
||||
fact = GST_ELEMENT_FACTORY_CAST (feature);
|
||||
|
||||
klass = gst_element_factory_get_metadata (fact, GST_ELEMENT_METADATA_KLASS);
|
||||
|
||||
if (strstr (klass, "Decoder"))
|
||||
list = &data->decode;
|
||||
else if (strstr (klass, "Demux"))
|
||||
list = &data->demux;
|
||||
else if (strstr (klass, "Parser") && strstr (klass, "Codec"))
|
||||
list = &data->demux;
|
||||
else if (strstr (klass, "Payloader") && strstr (klass, "RTP"))
|
||||
list = &data->payload;
|
||||
|
||||
if (list) {
|
||||
GST_LOG ("adding %s", GST_OBJECT_NAME (fact));
|
||||
*list = g_list_prepend (*list, gst_object_ref (fact));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_rtsp_media_factory_replay_get_demuxers (GstRTSPMediaFactoryReplay * factory)
|
||||
{
|
||||
return factory->demuxers;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_rtsp_media_factory_replay_get_payloaders (GstRTSPMediaFactoryReplay *
|
||||
factory)
|
||||
{
|
||||
return factory->payloaders;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_rtsp_media_factory_replay_get_decoders (GstRTSPMediaFactoryReplay * factory)
|
||||
{
|
||||
return factory->decoders;
|
||||
}
|
||||
|
||||
static GstRTSPMediaFactory *
|
||||
gst_rtsp_media_factory_replay_new (const gchar * uri, gint64 num_loops)
|
||||
{
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
factory =
|
||||
GST_RTSP_MEDIA_FACTORY (g_object_new
|
||||
(GST_TYPE_RTSP_MEDIA_FACTORY_REPLAY, "uri", uri, "num-loops", num_loops,
|
||||
NULL));
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
gchar *service;
|
||||
gchar *uri = NULL;
|
||||
gint64 num_loops = -1;
|
||||
GOptionEntry options[] = {
|
||||
{"num-loops", 0, 0, G_OPTION_ARG_INT64, &num_loops,
|
||||
"The number of loops (default = -1, infinite)", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
optctx = g_option_context_new ("RTSP Replay Server");
|
||||
g_option_context_add_main_entries (optctx, options, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
if (argc < 2) {
|
||||
g_print ("%s\n", g_option_context_get_help (optctx, TRUE, NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_option_context_free (optctx);
|
||||
|
||||
/* check if URI is valid, otherwise convert filename to URI if it's a file */
|
||||
if (gst_uri_is_valid (argv[1])) {
|
||||
uri = g_strdup (argv[1]);
|
||||
} else if (g_file_test (argv[1], G_FILE_TEST_EXISTS)) {
|
||||
uri = gst_filename_to_uri (argv[1], NULL);
|
||||
} else {
|
||||
g_printerr ("Unrecognised command line argument '%s'.\n"
|
||||
"Please pass an URI or file as argument!\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (num_loops < -1 || num_loops == 0) {
|
||||
g_printerr ("num-loop should be non-zero or -1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (replay_server_debug, "replay-server", 0,
|
||||
"RTSP replay server");
|
||||
|
||||
if (num_loops != -1)
|
||||
g_print ("Run loop %" G_GINT64_FORMAT " times\n", num_loops);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
factory = gst_rtsp_media_factory_replay_new (uri, num_loops);
|
||||
g_free (uri);
|
||||
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
g_object_unref (mounts);
|
||||
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
service = gst_rtsp_server_get_service (server);
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", service);
|
||||
g_free (service);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
36
examples/test-replay-server.h
Normal file
36
examples/test-replay-server.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_REPLAY_BIN (gst_replay_bin_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GstReplayBin, gst_replay_bin, GST, REPLAY_BIN, GstBin);
|
||||
|
||||
#define GST_TYPE_RTSP_MEDIA_FACTORY_REPLAY (gst_rtsp_media_factory_replay_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GstRTSPMediaFactoryReplay,
|
||||
gst_rtsp_media_factory_replay, GST, RTSP_MEDIA_FACTORY_REPLAY,
|
||||
GstRTSPMediaFactory);
|
||||
|
||||
G_END_DECLS
|
98
examples/test-sdp.c
Normal file
98
examples/test-sdp.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2009 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
media_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
||||
{
|
||||
gst_rtsp_media_set_reusable (media, TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
gchar *str;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
if (argc < 2) {
|
||||
g_message ("usage: %s <filename.sdp>", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
|
||||
str =
|
||||
g_strdup_printf ("( filesrc location=%s ! sdpdemux name=dynpay0 )",
|
||||
argv[1]);
|
||||
gst_rtsp_media_factory_set_launch (factory, str);
|
||||
gst_rtsp_media_factory_set_shared (factory, TRUE);
|
||||
g_signal_connect (factory, "media-configure", (GCallback) media_configure,
|
||||
NULL);
|
||||
g_free (str);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving */
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
157
examples/test-uri.c
Normal file
157
examples/test-uri.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
#include <gst/rtsp-server/rtsp-media-factory-uri.h>
|
||||
|
||||
#define DEFAULT_RTSP_PORT "8554"
|
||||
|
||||
static char *port = (char *) DEFAULT_RTSP_PORT;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,
|
||||
"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static gboolean
|
||||
remove_map (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPMountPoints *mounts;
|
||||
|
||||
g_print ("removing /test mount point\n");
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
gst_rtsp_mount_points_remove_factory (mounts, "/test");
|
||||
g_object_unref (mounts);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main (int argc, gchar * argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactoryURI *factory;
|
||||
GOptionContext *optctx;
|
||||
GError *error = NULL;
|
||||
gchar *uri;
|
||||
|
||||
optctx = g_option_context_new ("<uri> - Test RTSP Server, URI");
|
||||
g_option_context_add_main_entries (optctx, entries, NULL);
|
||||
g_option_context_add_group (optctx, gst_init_get_option_group ());
|
||||
if (!g_option_context_parse (optctx, &argc, &argv, &error)) {
|
||||
g_printerr ("Error parsing options: %s\n", error->message);
|
||||
g_option_context_free (optctx);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
g_option_context_free (optctx);
|
||||
|
||||
if (argc < 2) {
|
||||
g_printerr ("Please pass an URI or file as argument!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
g_object_set (server, "service", port, NULL);
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a URI media factory for a test stream. */
|
||||
factory = gst_rtsp_media_factory_uri_new ();
|
||||
|
||||
/* when using GStreamer as a client, one can use the gst payloader, which is
|
||||
* more efficient when there is no payloader for the compressed format */
|
||||
/* g_object_set (factory, "use-gstpay", TRUE, NULL); */
|
||||
|
||||
/* check if URI is valid, otherwise convert filename to URI if it's a file */
|
||||
if (gst_uri_is_valid (argv[1])) {
|
||||
uri = g_strdup (argv[1]);
|
||||
} else if (g_file_test (argv[1], G_FILE_TEST_EXISTS)) {
|
||||
uri = gst_filename_to_uri (argv[1], NULL);
|
||||
} else {
|
||||
g_printerr ("Unrecognised command line argument '%s'.\n"
|
||||
"Please pass an URI or file as argument!\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gst_rtsp_media_factory_uri_set_uri (factory, uri);
|
||||
g_free (uri);
|
||||
|
||||
/* if you want multiple clients to see the same video, set the shared property
|
||||
* to TRUE */
|
||||
/* gst_rtsp_media_factory_set_shared ( GST_RTSP_MEDIA_FACTORY (factory), TRUE); */
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test",
|
||||
GST_RTSP_MEDIA_FACTORY (factory));
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
/* do session cleanup every 2 seconds */
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
#if 0
|
||||
/* remove the mount point after 10 seconds, new clients won't be able to use
|
||||
* the /test url anymore */
|
||||
g_timeout_add_seconds (10, (GSourceFunc) remove_map, server);
|
||||
#endif
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:%s/test\n", port);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
222
examples/test-video-disconnect.c
Normal file
222
examples/test-video-disconnect.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2018 Jan Schmidt <jan at centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/* This example disconnects any clients and exits 10 seconds
|
||||
* after the first client connects */
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
guint exit_timeout_id = 0;
|
||||
|
||||
/* define this if you want the resource to only be available when using
|
||||
* user/password as the password */
|
||||
#undef WITH_AUTH
|
||||
|
||||
/* define this if you want the server to use TLS (it will also need WITH_AUTH
|
||||
* to be defined) */
|
||||
#undef WITH_TLS
|
||||
|
||||
/* this timeout is periodically run to clean up the expired sessions from the
|
||||
* pool. This needs to be run explicitly currently but might be done
|
||||
* automatically as part of the mainloop. */
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstRTSPFilterResult
|
||||
client_filter (GstRTSPServer * server, GstRTSPClient * client,
|
||||
gpointer user_data)
|
||||
{
|
||||
/* Simple filter that shuts down all clients. */
|
||||
return GST_RTSP_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
/* Timeout that runs 10 seconds after the first client connects and triggers
|
||||
* the shutdown of the server */
|
||||
static gboolean
|
||||
shutdown_timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPMountPoints *mounts;
|
||||
g_print ("Time for everyone to go. Removing mount point\n");
|
||||
/* Remove the mount point to prevent new clients connecting */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
gst_rtsp_mount_points_remove_factory (mounts, "/test");
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* Filter existing clients and remove them */
|
||||
g_print ("Disconnecting existing clients\n");
|
||||
gst_rtsp_server_client_filter (server, client_filter, NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
client_connected (GstRTSPServer * server, GstRTSPClient * client)
|
||||
{
|
||||
if (exit_timeout_id == 0) {
|
||||
g_print ("First Client connected. Disconnecting everyone in 10 seconds\n");
|
||||
exit_timeout_id =
|
||||
g_timeout_add_seconds (10, (GSourceFunc) shutdown_timeout, server);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
#ifdef WITH_AUTH
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
gchar *basic;
|
||||
GstRTSPPermissions *permissions;
|
||||
#endif
|
||||
#ifdef WITH_TLS
|
||||
GTlsCertificate *cert;
|
||||
GError *error = NULL;
|
||||
#endif
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
#ifdef WITH_AUTH
|
||||
/* make a new authentication manager. it can be added to control access to all
|
||||
* the factories on the server or on individual factories. */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
#ifdef WITH_TLS
|
||||
cert = g_tls_certificate_new_from_pem ("-----BEGIN CERTIFICATE-----"
|
||||
"MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk"
|
||||
"ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp"
|
||||
"ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq"
|
||||
"hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx"
|
||||
"NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW"
|
||||
"B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3"
|
||||
"DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf"
|
||||
"hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC"
|
||||
"MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1"
|
||||
"Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk"
|
||||
"PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH"
|
||||
"QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4="
|
||||
"-----END CERTIFICATE-----"
|
||||
"-----BEGIN PRIVATE KEY-----"
|
||||
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA2EnE8ZOeVwZmwzPc"
|
||||
"88DvoK1ckhOK7nVrsx9j6TmyKJ6m34UaAGpSlZbhvY72xyPNXl8QnUjm79SgT9bG"
|
||||
"zeUc6QIDAQABAkBRFJZ32VbqWMP9OVwDJLiwC01AlYLnka0mIQZbT/2xq9dUc9GW"
|
||||
"U3kiVw4lL8v/+sPjtTPCYYdzHHOyDen6znVhAiEA9qJT7BtQvRxCvGrAhr9MS022"
|
||||
"tTdPbW829BoUtIeH64cCIQDggG5i48v7HPacPBIH1RaSVhXl8qHCpQD3qrIw3FMw"
|
||||
"DwIga8PqH5Sf5sHedy2+CiK0V4MRfoU4c3zQ6kArI+bEgSkCIQCLA1vXBiE31B5s"
|
||||
"bdHoYa1BXebfZVd+1Hd95IfEM5mbRwIgSkDuQwV55BBlvWph3U8wVIMIb4GStaH8"
|
||||
"W535W8UBbEg=" "-----END PRIVATE KEY-----", -1, &error);
|
||||
if (cert == NULL) {
|
||||
g_printerr ("failed to parse PEM: %s\n", error->message);
|
||||
return -1;
|
||||
}
|
||||
gst_rtsp_auth_set_tls_certificate (auth, cert);
|
||||
g_object_unref (cert);
|
||||
#endif
|
||||
|
||||
/* make user token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("user", "password");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* configure in the server */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
#endif
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
#ifdef WITH_AUTH
|
||||
/* add permissions for the user media role */
|
||||
permissions = gst_rtsp_permissions_new ();
|
||||
gst_rtsp_permissions_add_role (permissions, "user",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
gst_rtsp_media_factory_set_permissions (factory, permissions);
|
||||
gst_rtsp_permissions_unref (permissions);
|
||||
#ifdef WITH_TLS
|
||||
gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_SAVP);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
g_signal_connect (server, "client-connected", (GCallback) client_connected,
|
||||
NULL);
|
||||
|
||||
/* add a timeout for the session cleanup */
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving, this never stops */
|
||||
#ifdef WITH_TLS
|
||||
g_print ("stream ready at rtsps://127.0.0.1:8554/test\n");
|
||||
#else
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
#endif
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
100
examples/test-video-rtx.c
Normal file
100
examples/test-video-rtx.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
/* this timeout is periodically run to clean up the expired sessions from the
|
||||
* pool. This needs to be run explicitly currently but might be done
|
||||
* automatically as part of the mainloop. */
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=8 " ")");
|
||||
|
||||
gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_AVPF);
|
||||
|
||||
/* store up to 0.4 seconds of retransmission data */
|
||||
gst_rtsp_media_factory_set_retransmission_time (factory, 400 * GST_MSECOND);
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
/* add a timeout for the session cleanup */
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving, this never stops */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
177
examples/test-video.c
Normal file
177
examples/test-video.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
/* define this if you want the resource to only be available when using
|
||||
* user/password as the password */
|
||||
#undef WITH_AUTH
|
||||
|
||||
/* define this if you want the server to use TLS (it will also need WITH_AUTH
|
||||
* to be defined) */
|
||||
#undef WITH_TLS
|
||||
|
||||
/* this timeout is periodically run to clean up the expired sessions from the
|
||||
* pool. This needs to be run explicitly currently but might be done
|
||||
* automatically as part of the mainloop. */
|
||||
static gboolean
|
||||
timeout (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_session_pool_cleanup (pool);
|
||||
g_object_unref (pool);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
#ifdef WITH_AUTH
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
gchar *basic;
|
||||
GstRTSPPermissions *permissions;
|
||||
#endif
|
||||
#ifdef WITH_TLS
|
||||
GTlsCertificate *cert;
|
||||
GError *error = NULL;
|
||||
#endif
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
#ifdef WITH_AUTH
|
||||
/* make a new authentication manager. it can be added to control access to all
|
||||
* the factories on the server or on individual factories. */
|
||||
auth = gst_rtsp_auth_new ();
|
||||
#ifdef WITH_TLS
|
||||
cert = g_tls_certificate_new_from_pem ("-----BEGIN CERTIFICATE-----"
|
||||
"MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk"
|
||||
"ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp"
|
||||
"ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq"
|
||||
"hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx"
|
||||
"NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW"
|
||||
"B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3"
|
||||
"DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf"
|
||||
"hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC"
|
||||
"MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1"
|
||||
"Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk"
|
||||
"PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH"
|
||||
"QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4="
|
||||
"-----END CERTIFICATE-----"
|
||||
"-----BEGIN PRIVATE KEY-----"
|
||||
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA2EnE8ZOeVwZmwzPc"
|
||||
"88DvoK1ckhOK7nVrsx9j6TmyKJ6m34UaAGpSlZbhvY72xyPNXl8QnUjm79SgT9bG"
|
||||
"zeUc6QIDAQABAkBRFJZ32VbqWMP9OVwDJLiwC01AlYLnka0mIQZbT/2xq9dUc9GW"
|
||||
"U3kiVw4lL8v/+sPjtTPCYYdzHHOyDen6znVhAiEA9qJT7BtQvRxCvGrAhr9MS022"
|
||||
"tTdPbW829BoUtIeH64cCIQDggG5i48v7HPacPBIH1RaSVhXl8qHCpQD3qrIw3FMw"
|
||||
"DwIga8PqH5Sf5sHedy2+CiK0V4MRfoU4c3zQ6kArI+bEgSkCIQCLA1vXBiE31B5s"
|
||||
"bdHoYa1BXebfZVd+1Hd95IfEM5mbRwIgSkDuQwV55BBlvWph3U8wVIMIb4GStaH8"
|
||||
"W535W8UBbEg=" "-----END PRIVATE KEY-----", -1, &error);
|
||||
if (cert == NULL) {
|
||||
g_printerr ("failed to parse PEM: %s\n", error->message);
|
||||
return -1;
|
||||
}
|
||||
gst_rtsp_auth_set_tls_certificate (auth, cert);
|
||||
g_object_unref (cert);
|
||||
#endif
|
||||
|
||||
/* make user token */
|
||||
token =
|
||||
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||
"user", NULL);
|
||||
basic = gst_rtsp_auth_make_basic ("user", "password");
|
||||
gst_rtsp_auth_add_basic (auth, basic, token);
|
||||
g_free (basic);
|
||||
gst_rtsp_token_unref (token);
|
||||
|
||||
/* configure in the server */
|
||||
gst_rtsp_server_set_auth (server, auth);
|
||||
#endif
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named pay%d. Each
|
||||
* element with pay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||
#ifdef WITH_AUTH
|
||||
/* add permissions for the user media role */
|
||||
permissions = gst_rtsp_permissions_new ();
|
||||
gst_rtsp_permissions_add_role (permissions, "user",
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
gst_rtsp_media_factory_set_permissions (factory, permissions);
|
||||
gst_rtsp_permissions_unref (permissions);
|
||||
#ifdef WITH_TLS
|
||||
gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_SAVP);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||
goto failed;
|
||||
|
||||
/* add a timeout for the session cleanup */
|
||||
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||
|
||||
/* start serving, this never stops */
|
||||
#ifdef WITH_TLS
|
||||
g_print ("stream ready at rtsps://127.0.0.1:8554/test\n");
|
||||
#else
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
#endif
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
failed:
|
||||
{
|
||||
g_print ("failed to attach the server\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
500
gst-rtsp-server.doap
Normal file
500
gst-rtsp-server.doap
Normal file
|
@ -0,0 +1,500 @@
|
|||
<Project
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns="http://usefulinc.com/ns/doap#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:admin="http://webns.net/mvcb/">
|
||||
|
||||
<name>GStreamer RTSP Server</name>
|
||||
<shortname>gst-rtsp-server</shortname>
|
||||
<homepage rdf:resource="http://gstreamer.freedesktop.org/modules/gst-rtsp-server.html" />
|
||||
<created>1999-10-31</created>
|
||||
<shortdesc xml:lang="en">
|
||||
RTSP server library based on GStreamer
|
||||
</shortdesc>
|
||||
<description xml:lang="en">
|
||||
RTSP server library based on GStreamer
|
||||
</description>
|
||||
<category></category>
|
||||
<bug-database rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/issues/" />
|
||||
<screenshots></screenshots>
|
||||
<mailing-list rdf:resource="http://lists.freedesktop.org/mailman/listinfo/gstreamer-devel" />
|
||||
<programming-language>C</programming-language>
|
||||
<license rdf:resource="http://usefulinc.com/doap/licenses/lgpl" />
|
||||
<download-page rdf:resource="http://gstreamer.freedesktop.org/download/" />
|
||||
|
||||
<repository>
|
||||
<GitRepository>
|
||||
<location rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server"/>
|
||||
<browse rdf:resource="https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server"/>
|
||||
</GitRepository>
|
||||
</repository>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.19.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2021-09-23</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.19.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.19.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2021-06-01</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.19.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.18.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2020-09-08</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.18.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.17.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2020-08-20</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.17.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.17.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2020-07-03</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.17.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.17.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2020-06-19</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.17.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.16.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2019-04-19</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.16.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.15.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2019-04-11</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.15.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.15.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2019-02-26</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.15.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.15.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2019-01-17</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.15.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.14.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2018-03-19</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.14.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.13.91</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2018-03-13</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.13.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.13.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2018-03-03</created>
|
||||
<file-release rdf:resource="https://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.13.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.13.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2018-02-15</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.13.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.4</revision>
|
||||
<branch>1.12</branch>
|
||||
<name></name>
|
||||
<created>2017-12-07</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.12.4.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.3</revision>
|
||||
<branch>1.12</branch>
|
||||
<name></name>
|
||||
<created>2017-09-18</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.12.3.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.2</revision>
|
||||
<branch>1.12</branch>
|
||||
<name></name>
|
||||
<created>2017-07-14</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.12.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.1</revision>
|
||||
<branch>1.12</branch>
|
||||
<name></name>
|
||||
<created>2017-06-20</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.12.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.12.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2017-05-04</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.12.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.11.91</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2017-04-27</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.11.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.11.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2017-04-07</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.11.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.11.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2017-02-24</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.11.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.11.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2017-01-12</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.11.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.10.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-11-01</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.10.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.9.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-09-30</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.9.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.9.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-09-01</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.9.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.9.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-06-06</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.9.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.8.0</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-03-24</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.8.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.7.91</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-03-15</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.7.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.7.90</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-03-01</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.7.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.7.2</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2016-02-19</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.7.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.7.1</revision>
|
||||
<branch>master</branch>
|
||||
<name></name>
|
||||
<created>2015-12-24</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.7.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.6.2</revision>
|
||||
<branch>1.6</branch>
|
||||
<name></name>
|
||||
<created>2015-12-14</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.6.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.6.1</revision>
|
||||
<branch>1.6</branch>
|
||||
<name></name>
|
||||
<created>2015-10-30</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.6.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.6.0</revision>
|
||||
<branch>1.6</branch>
|
||||
<name></name>
|
||||
<created>2015-09-25</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.6.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.5.91</revision>
|
||||
<branch>1.5</branch>
|
||||
<name></name>
|
||||
<created>2015-09-18</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.5.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.5.90</revision>
|
||||
<branch>1.5</branch>
|
||||
<name></name>
|
||||
<created>2015-08-19</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.5.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.5.2</revision>
|
||||
<branch>1.5</branch>
|
||||
<name></name>
|
||||
<created>2015-06-24</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.5.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.5.1</revision>
|
||||
<branch>1.5</branch>
|
||||
<name></name>
|
||||
<created>2015-06-07</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.5.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.4.0</revision>
|
||||
<branch>1.4</branch>
|
||||
<name></name>
|
||||
<created>2014-07-19</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.4.0.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.3.91</revision>
|
||||
<branch>1.3</branch>
|
||||
<name></name>
|
||||
<created>2014-07-11</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.3.91.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.3.90</revision>
|
||||
<branch>1.3</branch>
|
||||
<name></name>
|
||||
<created>2014-06-28</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.3.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.3.3</revision>
|
||||
<branch>1.3</branch>
|
||||
<name></name>
|
||||
<created>2014-06-22</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.3.3.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.3.2</revision>
|
||||
<branch>1.3</branch>
|
||||
<name></name>
|
||||
<created>2014-05-21</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.3.2.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.3.1</revision>
|
||||
<branch>1.3</branch>
|
||||
<name></name>
|
||||
<created>2014-05-03</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.3.1.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<release>
|
||||
<Version>
|
||||
<revision>1.1.90</revision>
|
||||
<branch>1.1</branch>
|
||||
<name></name>
|
||||
<created>2014-02-09</created>
|
||||
<file-release rdf:resource="http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-1.1.90.tar.xz" />
|
||||
</Version>
|
||||
</release>
|
||||
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Wim Taymans</foaf:name>
|
||||
<foaf:mbox_sha1sum>0d93fde052812d51a05fd86de9bdbf674423daa2</foaf:mbox_sha1sum>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
|
||||
</Project>
|
5
gst/meson.build
Normal file
5
gst/meson.build
Normal file
|
@ -0,0 +1,5 @@
|
|||
subdir('rtsp-server')
|
||||
|
||||
if not get_option('rtspclientsink').disabled()
|
||||
subdir('rtsp-sink')
|
||||
endif
|
99
gst/rtsp-server/meson.build
Normal file
99
gst/rtsp-server/meson.build
Normal file
|
@ -0,0 +1,99 @@
|
|||
rtsp_server_sources = [
|
||||
'rtsp-address-pool.c',
|
||||
'rtsp-auth.c',
|
||||
'rtsp-client.c',
|
||||
'rtsp-context.c',
|
||||
'rtsp-latency-bin.c',
|
||||
'rtsp-media.c',
|
||||
'rtsp-media-factory.c',
|
||||
'rtsp-media-factory-uri.c',
|
||||
'rtsp-mount-points.c',
|
||||
'rtsp-params.c',
|
||||
'rtsp-permissions.c',
|
||||
'rtsp-sdp.c',
|
||||
'rtsp-server.c',
|
||||
'rtsp-session.c',
|
||||
'rtsp-session-media.c',
|
||||
'rtsp-session-pool.c',
|
||||
'rtsp-stream.c',
|
||||
'rtsp-stream-transport.c',
|
||||
'rtsp-thread-pool.c',
|
||||
'rtsp-token.c',
|
||||
'rtsp-onvif-server.c',
|
||||
'rtsp-onvif-client.c',
|
||||
'rtsp-onvif-media-factory.c',
|
||||
'rtsp-onvif-media.c',
|
||||
]
|
||||
|
||||
rtsp_server_headers = [
|
||||
'rtsp-auth.h',
|
||||
'rtsp-address-pool.h',
|
||||
'rtsp-context.h',
|
||||
'rtsp-params.h',
|
||||
'rtsp-sdp.h',
|
||||
'rtsp-thread-pool.h',
|
||||
'rtsp-media.h',
|
||||
'rtsp-media-factory.h',
|
||||
'rtsp-media-factory-uri.h',
|
||||
'rtsp-mount-points.h',
|
||||
'rtsp-permissions.h',
|
||||
'rtsp-stream.h',
|
||||
'rtsp-stream-transport.h',
|
||||
'rtsp-session.h',
|
||||
'rtsp-session-media.h',
|
||||
'rtsp-session-pool.h',
|
||||
'rtsp-token.h',
|
||||
'rtsp-client.h',
|
||||
'rtsp-server.h',
|
||||
'rtsp-server-object.h',
|
||||
'rtsp-server-prelude.h',
|
||||
'rtsp-onvif-server.h',
|
||||
'rtsp-onvif-client.h',
|
||||
'rtsp-onvif-media-factory.h',
|
||||
'rtsp-onvif-media.h',
|
||||
]
|
||||
|
||||
install_headers(rtsp_server_headers, subdir : 'gstreamer-1.0/gst/rtsp-server')
|
||||
|
||||
gst_rtsp_server_deps = [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep]
|
||||
gst_rtsp_server = library('gstrtspserver-@0@'.format(api_version),
|
||||
rtsp_server_sources,
|
||||
include_directories : rtspserver_incs,
|
||||
c_args: rtspserver_args + ['-DBUILDING_GST_RTSP_SERVER'],
|
||||
version : libversion,
|
||||
soversion : soversion,
|
||||
darwin_versions : osxversion,
|
||||
install : true,
|
||||
dependencies : gst_rtsp_server_deps)
|
||||
|
||||
pkgconfig.generate(gst_rtsp_server,
|
||||
libraries : [gst_dep],
|
||||
subdirs : pkgconfig_subdirs,
|
||||
name : 'gstreamer-rtsp-server-1.0',
|
||||
description : 'GStreamer based RTSP server',
|
||||
)
|
||||
|
||||
rtsp_server_gen_sources = []
|
||||
if build_gir
|
||||
gst_gir_extra_args = gir_init_section + ['--c-include=gst/rtsp-server/rtsp-server.h']
|
||||
rtsp_server_gir = gnome.generate_gir(gst_rtsp_server,
|
||||
sources : rtsp_server_headers + rtsp_server_sources,
|
||||
namespace : 'GstRtspServer',
|
||||
nsversion : api_version,
|
||||
identifier_prefix : 'Gst',
|
||||
symbol_prefix : 'gst',
|
||||
export_packages : 'gstreamer-rtsp-server-' + api_version,
|
||||
install : true,
|
||||
extra_args : gst_gir_extra_args,
|
||||
includes : ['Gst-1.0', 'GstRtsp-1.0', 'GstNet-1.0'],
|
||||
dependencies : gst_rtsp_server_deps,
|
||||
)
|
||||
rtsp_server_gen_sources += [rtsp_server_gir]
|
||||
endif
|
||||
|
||||
gst_rtsp_server_dep = declare_dependency(link_with : gst_rtsp_server,
|
||||
include_directories : rtspserver_incs,
|
||||
sources : rtsp_server_gen_sources,
|
||||
dependencies : [gstrtsp_dep, gstrtp_dep, gstsdp_dep, gstnet_dep, gstapp_dep])
|
||||
|
||||
meson.override_dependency('gstreamer-rtsp-server-1.0', gst_rtsp_server_dep)
|
753
gst/rtsp-server/rtsp-address-pool.c
Normal file
753
gst/rtsp-server/rtsp-address-pool.c
Normal file
|
@ -0,0 +1,753 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-address-pool
|
||||
* @short_description: A pool of network addresses
|
||||
* @see_also: #GstRTSPStream, #GstRTSPStreamTransport
|
||||
*
|
||||
* The #GstRTSPAddressPool is an object that maintains a collection of network
|
||||
* addresses. It is used to allocate server ports and server multicast addresses
|
||||
* but also to reserve client provided destination addresses.
|
||||
*
|
||||
* A range of addresses can be added with gst_rtsp_address_pool_add_range().
|
||||
* Both multicast and unicast addresses can be added.
|
||||
*
|
||||
* With gst_rtsp_address_pool_acquire_address() an unused address and port range
|
||||
* can be acquired from the pool. With gst_rtsp_address_pool_reserve_address() a
|
||||
* specific address can be retrieved. Both methods return a boxed
|
||||
* #GstRTSPAddress that should be freed with gst_rtsp_address_free() after
|
||||
* usage, which brings the address back into the pool.
|
||||
*
|
||||
* Last reviewed on 2013-07-16 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "rtsp-address-pool.h"
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_copy:
|
||||
* @addr: a #GstRTSPAddress
|
||||
*
|
||||
* Make a copy of @addr.
|
||||
*
|
||||
* Returns: a copy of @addr.
|
||||
*/
|
||||
GstRTSPAddress *
|
||||
gst_rtsp_address_copy (GstRTSPAddress * addr)
|
||||
{
|
||||
GstRTSPAddress *copy;
|
||||
|
||||
g_return_val_if_fail (addr != NULL, NULL);
|
||||
|
||||
copy = g_slice_dup (GstRTSPAddress, addr);
|
||||
/* only release to the pool when the original is freed. It's a bit
|
||||
* weird but this will do for now as it avoid us to use refcounting. */
|
||||
copy->pool = NULL;
|
||||
copy->address = g_strdup (copy->address);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
|
||||
GstRTSPAddress * addr);
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_free:
|
||||
* @addr: a #GstRTSPAddress
|
||||
*
|
||||
* Free @addr and releasing it back into the pool when owned by a
|
||||
* pool.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_address_free (GstRTSPAddress * addr)
|
||||
{
|
||||
g_return_if_fail (addr != NULL);
|
||||
|
||||
if (addr->pool) {
|
||||
/* unrefs the pool and sets it to NULL */
|
||||
gst_rtsp_address_pool_release_address (addr->pool, addr);
|
||||
}
|
||||
g_free (addr->address);
|
||||
g_slice_free (GstRTSPAddress, addr);
|
||||
}
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GstRTSPAddress, gst_rtsp_address,
|
||||
(GBoxedCopyFunc) gst_rtsp_address_copy,
|
||||
(GBoxedFreeFunc) gst_rtsp_address_free);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_address_pool_debug
|
||||
|
||||
#define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate))
|
||||
|
||||
struct _GstRTSPAddressPoolPrivate
|
||||
{
|
||||
GMutex lock; /* protects everything in this struct */
|
||||
GList *addresses;
|
||||
GList *allocated;
|
||||
|
||||
gboolean has_unicast_addresses;
|
||||
};
|
||||
|
||||
#define ADDR_IS_IPV4(a) ((a)->size == 4)
|
||||
#define ADDR_IS_IPV6(a) ((a)->size == 16)
|
||||
#define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint8 bytes[16];
|
||||
gsize size;
|
||||
guint16 port;
|
||||
} Addr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Addr min;
|
||||
Addr max;
|
||||
guint8 ttl;
|
||||
} AddrRange;
|
||||
|
||||
#define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0)
|
||||
|
||||
#define gst_rtsp_address_pool_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPAddressPool, gst_rtsp_address_pool,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
static void gst_rtsp_address_pool_finalize (GObject * obj);
|
||||
|
||||
static void
|
||||
gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_rtsp_address_pool_finalize;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0,
|
||||
"GstRTSPAddressPool");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_address_pool_init (GstRTSPAddressPool * pool)
|
||||
{
|
||||
pool->priv = gst_rtsp_address_pool_get_instance_private (pool);
|
||||
|
||||
g_mutex_init (&pool->priv->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
free_range (AddrRange * range)
|
||||
{
|
||||
g_slice_free (AddrRange, range);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_address_pool_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPAddressPool *pool;
|
||||
|
||||
pool = GST_RTSP_ADDRESS_POOL (obj);
|
||||
|
||||
g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range);
|
||||
g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range);
|
||||
g_mutex_clear (&pool->priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_new:
|
||||
*
|
||||
* Make a new #GstRTSPAddressPool.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPAddressPool
|
||||
*/
|
||||
GstRTSPAddressPool *
|
||||
gst_rtsp_address_pool_new (void)
|
||||
{
|
||||
GstRTSPAddressPool *pool;
|
||||
|
||||
pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_clear:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
*
|
||||
* Clear all addresses in @pool. There should be no outstanding
|
||||
* allocations.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
||||
g_return_if_fail (pool->priv->allocated == NULL);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_list_free_full (priv->addresses, (GDestroyNotify) free_range);
|
||||
priv->addresses = NULL;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fill_address (const gchar * address, guint16 port, Addr * addr,
|
||||
gboolean is_multicast)
|
||||
{
|
||||
GInetAddress *inet;
|
||||
|
||||
inet = g_inet_address_new_from_string (address);
|
||||
if (inet == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (is_multicast != g_inet_address_get_is_multicast (inet)) {
|
||||
g_object_unref (inet);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
addr->size = g_inet_address_get_native_size (inet);
|
||||
memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size);
|
||||
g_object_unref (inet);
|
||||
addr->port = port;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
get_address_string (Addr * addr)
|
||||
{
|
||||
gchar *res;
|
||||
GInetAddress *inet;
|
||||
|
||||
inet = g_inet_address_new_from_bytes (addr->bytes,
|
||||
addr->size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
|
||||
res = g_inet_address_to_string (inet);
|
||||
g_object_unref (inet);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_add_range:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
* @min_address: a minimum address to add
|
||||
* @max_address: a maximum address to add
|
||||
* @min_port: the minimum port
|
||||
* @max_port: the maximum port
|
||||
* @ttl: a TTL or 0 for unicast addresses
|
||||
*
|
||||
* Adds the addresses from @min_addess to @max_address (inclusive)
|
||||
* to @pool. The valid port range for the addresses will be from @min_port to
|
||||
* @max_port inclusive.
|
||||
*
|
||||
* When @ttl is 0, @min_address and @max_address should be unicast addresses.
|
||||
* @min_address and @max_address can be set to
|
||||
* #GST_RTSP_ADDRESS_POOL_ANY_IPV4 or #GST_RTSP_ADDRESS_POOL_ANY_IPV6 to bind
|
||||
* to all available IPv4 or IPv6 addresses.
|
||||
*
|
||||
* When @ttl > 0, @min_address and @max_address should be multicast addresses.
|
||||
*
|
||||
* Returns: %TRUE if the addresses could be added.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
|
||||
const gchar * min_address, const gchar * max_address,
|
||||
guint16 min_port, guint16 max_port, guint8 ttl)
|
||||
{
|
||||
AddrRange *range;
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
gboolean is_multicast;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
|
||||
g_return_val_if_fail (min_port <= max_port, FALSE);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
is_multicast = ttl != 0;
|
||||
|
||||
range = g_slice_new0 (AddrRange);
|
||||
|
||||
if (!fill_address (min_address, min_port, &range->min, is_multicast))
|
||||
goto invalid;
|
||||
if (!fill_address (max_address, max_port, &range->max, is_multicast))
|
||||
goto invalid;
|
||||
|
||||
if (range->min.size != range->max.size)
|
||||
goto invalid;
|
||||
if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0)
|
||||
goto invalid;
|
||||
|
||||
range->ttl = ttl;
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address,
|
||||
min_port, max_port, ttl);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->addresses = g_list_prepend (priv->addresses, range);
|
||||
|
||||
if (!is_multicast)
|
||||
priv->has_unicast_addresses = TRUE;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
invalid:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address,
|
||||
max_address);
|
||||
g_slice_free (AddrRange, range);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
inc_address (Addr * addr, guint count)
|
||||
{
|
||||
gint i;
|
||||
guint carry;
|
||||
|
||||
carry = count;
|
||||
for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
|
||||
carry += addr->bytes[i];
|
||||
addr->bytes[i] = carry & 0xff;
|
||||
carry >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* tells us the number of addresses between min_addr and max_addr */
|
||||
static guint
|
||||
diff_address (Addr * max_addr, Addr * min_addr)
|
||||
{
|
||||
gint i;
|
||||
guint result = 0;
|
||||
|
||||
g_return_val_if_fail (min_addr->size == max_addr->size, 0);
|
||||
|
||||
for (i = 0; i < min_addr->size; i++) {
|
||||
g_return_val_if_fail (result < (1 << 24), result);
|
||||
|
||||
result <<= 8;
|
||||
result += max_addr->bytes[i] - min_addr->bytes[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static AddrRange *
|
||||
split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr,
|
||||
guint skip_port, gint n_ports)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv = pool->priv;
|
||||
AddrRange *temp;
|
||||
|
||||
if (skip_addr) {
|
||||
temp = g_slice_dup (AddrRange, range);
|
||||
memcpy (temp->max.bytes, temp->min.bytes, temp->min.size);
|
||||
inc_address (&temp->max, skip_addr - 1);
|
||||
priv->addresses = g_list_prepend (priv->addresses, temp);
|
||||
|
||||
inc_address (&range->min, skip_addr);
|
||||
}
|
||||
|
||||
if (!RANGE_IS_SINGLE (range)) {
|
||||
/* min and max are not the same, we have more than one address. */
|
||||
temp = g_slice_dup (AddrRange, range);
|
||||
/* increment the range min address */
|
||||
inc_address (&temp->min, 1);
|
||||
/* and store back in pool */
|
||||
priv->addresses = g_list_prepend (priv->addresses, temp);
|
||||
|
||||
/* adjust range with only the first address */
|
||||
memcpy (range->max.bytes, range->min.bytes, range->min.size);
|
||||
}
|
||||
|
||||
/* range now contains only one single address */
|
||||
if (skip_port > 0) {
|
||||
/* make a range with the skipped ports */
|
||||
temp = g_slice_dup (AddrRange, range);
|
||||
temp->max.port = temp->min.port + skip_port - 1;
|
||||
/* and store back in pool */
|
||||
priv->addresses = g_list_prepend (priv->addresses, temp);
|
||||
|
||||
/* increment range port */
|
||||
range->min.port += skip_port;
|
||||
}
|
||||
/* range now contains single address with desired port number */
|
||||
if (range->max.port - range->min.port + 1 > n_ports) {
|
||||
/* make a range with the remaining ports */
|
||||
temp = g_slice_dup (AddrRange, range);
|
||||
temp->min.port += n_ports;
|
||||
/* and store back in pool */
|
||||
priv->addresses = g_list_prepend (priv->addresses, temp);
|
||||
|
||||
/* and truncate port */
|
||||
range->max.port = range->min.port + n_ports - 1;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_acquire_address:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
* @flags: flags
|
||||
* @n_ports: the amount of ports
|
||||
*
|
||||
* Take an address and ports from @pool. @flags can be used to control the
|
||||
* allocation. @n_ports consecutive ports will be allocated of which the first
|
||||
* one can be found in @port.
|
||||
*
|
||||
* Returns: (nullable): a #GstRTSPAddress that should be freed with
|
||||
* gst_rtsp_address_free after use or %NULL when no address could be
|
||||
* acquired.
|
||||
*/
|
||||
GstRTSPAddress *
|
||||
gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
|
||||
GstRTSPAddressFlags flags, gint n_ports)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
GList *walk, *next;
|
||||
AddrRange *result;
|
||||
GstRTSPAddress *addr;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
|
||||
g_return_val_if_fail (n_ports > 0, NULL);
|
||||
|
||||
priv = pool->priv;
|
||||
result = NULL;
|
||||
addr = NULL;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
/* go over available ranges */
|
||||
for (walk = priv->addresses; walk; walk = next) {
|
||||
AddrRange *range;
|
||||
gint ports, skip;
|
||||
|
||||
range = walk->data;
|
||||
next = walk->next;
|
||||
|
||||
/* check address type when given */
|
||||
if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min))
|
||||
continue;
|
||||
if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min))
|
||||
continue;
|
||||
if (flags & GST_RTSP_ADDRESS_FLAG_MULTICAST && range->ttl == 0)
|
||||
continue;
|
||||
if (flags & GST_RTSP_ADDRESS_FLAG_UNICAST && range->ttl != 0)
|
||||
continue;
|
||||
|
||||
/* check for enough ports */
|
||||
ports = range->max.port - range->min.port + 1;
|
||||
if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT
|
||||
&& !ADDR_IS_EVEN_PORT (&range->min))
|
||||
skip = 1;
|
||||
else
|
||||
skip = 0;
|
||||
if (ports - skip < n_ports)
|
||||
continue;
|
||||
|
||||
/* we found a range, remove from the list */
|
||||
priv->addresses = g_list_delete_link (priv->addresses, walk);
|
||||
/* now split and exit our loop */
|
||||
result = split_range (pool, range, 0, skip, n_ports);
|
||||
priv->allocated = g_list_prepend (priv->allocated, result);
|
||||
break;
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (result) {
|
||||
addr = g_slice_new0 (GstRTSPAddress);
|
||||
addr->pool = g_object_ref (pool);
|
||||
addr->address = get_address_string (&result->min);
|
||||
addr->n_ports = n_ports;
|
||||
addr->port = result->min.port;
|
||||
addr->ttl = result->ttl;
|
||||
addr->priv = result;
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", addr->address,
|
||||
addr->port, addr->ttl);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_release_address:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
* @id: an address id
|
||||
*
|
||||
* Release a previously acquired address (with
|
||||
* gst_rtsp_address_pool_acquire_address()) back into @pool.
|
||||
*/
|
||||
static void
|
||||
gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
|
||||
GstRTSPAddress * addr)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
GList *find;
|
||||
AddrRange *range;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
||||
g_return_if_fail (addr != NULL);
|
||||
g_return_if_fail (addr->pool == pool);
|
||||
|
||||
priv = pool->priv;
|
||||
range = addr->priv;
|
||||
|
||||
/* we don't want to free twice */
|
||||
addr->priv = NULL;
|
||||
addr->pool = NULL;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
find = g_list_find (priv->allocated, range);
|
||||
if (find == NULL)
|
||||
goto not_found;
|
||||
|
||||
priv->allocated = g_list_delete_link (priv->allocated, find);
|
||||
|
||||
/* FIXME, merge and do something clever */
|
||||
priv->addresses = g_list_prepend (priv->addresses, range);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
g_object_unref (pool);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
not_found:
|
||||
{
|
||||
g_warning ("Released unknown address %p", addr);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dump_range (AddrRange * range, GstRTSPAddressPool * pool)
|
||||
{
|
||||
gchar *addr1, *addr2;
|
||||
|
||||
addr1 = get_address_string (&range->min);
|
||||
addr2 = get_address_string (&range->max);
|
||||
g_print (" address %s-%s, port %u-%u, ttl %u\n", addr1, addr2,
|
||||
range->min.port, range->max.port, range->ttl);
|
||||
g_free (addr1);
|
||||
g_free (addr2);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_dump:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
*
|
||||
* Dump the free and allocated addresses to stdout.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_print ("free:\n");
|
||||
g_list_foreach (priv->addresses, (GFunc) dump_range, pool);
|
||||
g_print ("allocated:\n");
|
||||
g_list_foreach (priv->allocated, (GFunc) dump_range, pool);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
static GList *
|
||||
find_address_in_ranges (GList * addresses, Addr * addr, guint port,
|
||||
guint n_ports, guint ttl)
|
||||
{
|
||||
GList *walk, *next;
|
||||
|
||||
/* go over available ranges */
|
||||
for (walk = addresses; walk; walk = next) {
|
||||
AddrRange *range;
|
||||
|
||||
range = walk->data;
|
||||
next = walk->next;
|
||||
|
||||
/* Not the right type of address */
|
||||
if (range->min.size != addr->size)
|
||||
continue;
|
||||
|
||||
/* Check that the address is in the interval */
|
||||
if (memcmp (range->min.bytes, addr->bytes, addr->size) > 0 ||
|
||||
memcmp (range->max.bytes, addr->bytes, addr->size) < 0)
|
||||
continue;
|
||||
|
||||
/* Make sure the requested ports are inside the range */
|
||||
if (port < range->min.port || port + n_ports - 1 > range->max.port)
|
||||
continue;
|
||||
|
||||
if (ttl != range->ttl)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return walk;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_reserve_address:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
* @ip_address: The IP address to reserve
|
||||
* @port: The first port to reserve
|
||||
* @n_ports: The number of ports
|
||||
* @ttl: The requested ttl
|
||||
* @address: (out): storage for a #GstRTSPAddress
|
||||
*
|
||||
* Take a specific address and ports from @pool. @n_ports consecutive
|
||||
* ports will be allocated of which the first one can be found in
|
||||
* @port.
|
||||
*
|
||||
* If @ttl is 0, @address should be a unicast address. If @ttl > 0, @address
|
||||
* should be a valid multicast address.
|
||||
*
|
||||
* Returns: #GST_RTSP_ADDRESS_POOL_OK if an address was reserved. The address
|
||||
* is returned in @address and should be freed with gst_rtsp_address_free
|
||||
* after use.
|
||||
*/
|
||||
GstRTSPAddressPoolResult
|
||||
gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
|
||||
const gchar * ip_address, guint port, guint n_ports, guint ttl,
|
||||
GstRTSPAddress ** address)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
Addr input_addr;
|
||||
GList *list;
|
||||
AddrRange *addr_range;
|
||||
GstRTSPAddress *addr;
|
||||
gboolean is_multicast;
|
||||
GstRTSPAddressPoolResult result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool),
|
||||
GST_RTSP_ADDRESS_POOL_EINVAL);
|
||||
g_return_val_if_fail (ip_address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
|
||||
g_return_val_if_fail (port > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
|
||||
g_return_val_if_fail (n_ports > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
|
||||
g_return_val_if_fail (address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
|
||||
|
||||
priv = pool->priv;
|
||||
addr_range = NULL;
|
||||
addr = NULL;
|
||||
is_multicast = ttl != 0;
|
||||
|
||||
if (!fill_address (ip_address, port, &input_addr, is_multicast))
|
||||
goto invalid;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
list = find_address_in_ranges (priv->addresses, &input_addr, port, n_ports,
|
||||
ttl);
|
||||
if (list != NULL) {
|
||||
AddrRange *range = list->data;
|
||||
guint skip_port, skip_addr;
|
||||
|
||||
skip_addr = diff_address (&input_addr, &range->min);
|
||||
skip_port = port - range->min.port;
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "diff 0x%08x/%u", skip_addr, skip_port);
|
||||
|
||||
/* we found a range, remove from the list */
|
||||
priv->addresses = g_list_delete_link (priv->addresses, list);
|
||||
/* now split and exit our loop */
|
||||
addr_range = split_range (pool, range, skip_addr, skip_port, n_ports);
|
||||
priv->allocated = g_list_prepend (priv->allocated, addr_range);
|
||||
}
|
||||
|
||||
if (addr_range) {
|
||||
addr = g_slice_new0 (GstRTSPAddress);
|
||||
addr->pool = g_object_ref (pool);
|
||||
addr->address = get_address_string (&addr_range->min);
|
||||
addr->n_ports = n_ports;
|
||||
addr->port = addr_range->min.port;
|
||||
addr->ttl = addr_range->ttl;
|
||||
addr->priv = addr_range;
|
||||
|
||||
result = GST_RTSP_ADDRESS_POOL_OK;
|
||||
GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address,
|
||||
addr->port, addr->ttl);
|
||||
} else {
|
||||
/* We failed to reserve the address. Check if it was because the address
|
||||
* was already in use or if it wasn't in the pool to begin with */
|
||||
list = find_address_in_ranges (priv->allocated, &input_addr, port, n_ports,
|
||||
ttl);
|
||||
if (list != NULL) {
|
||||
result = GST_RTSP_ADDRESS_POOL_ERESERVED;
|
||||
} else {
|
||||
result = GST_RTSP_ADDRESS_POOL_ERANGE;
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
*address = addr;
|
||||
return result;
|
||||
|
||||
/* ERRORS */
|
||||
invalid:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool, "invalid address %s:%u/%u/%u", ip_address,
|
||||
port, n_ports, ttl);
|
||||
*address = NULL;
|
||||
return GST_RTSP_ADDRESS_POOL_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_address_pool_has_unicast_addresses:
|
||||
* @pool: a #GstRTSPAddressPool
|
||||
*
|
||||
* Used to know if the pool includes any unicast addresses.
|
||||
*
|
||||
* Returns: %TRUE if the pool includes any unicast addresses, %FALSE otherwise
|
||||
*/
|
||||
|
||||
gboolean
|
||||
gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool)
|
||||
{
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
gboolean has_unicast_addresses;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
has_unicast_addresses = priv->has_unicast_addresses;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return has_unicast_addresses;
|
||||
}
|
205
gst/rtsp-server/rtsp-address-pool.h
Normal file
205
gst/rtsp-server/rtsp-address-pool.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_ADDRESS_POOL_H__
|
||||
#define __GST_RTSP_ADDRESS_POOL_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_ADDRESS_POOL (gst_rtsp_address_pool_get_type ())
|
||||
#define GST_IS_RTSP_ADDRESS_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ADDRESS_POOL))
|
||||
#define GST_IS_RTSP_ADDRESS_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ADDRESS_POOL))
|
||||
#define GST_RTSP_ADDRESS_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolClass))
|
||||
#define GST_RTSP_ADDRESS_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPool))
|
||||
#define GST_RTSP_ADDRESS_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolClass))
|
||||
#define GST_RTSP_ADDRESS_POOL_CAST(obj) ((GstRTSPAddressPool*)(obj))
|
||||
#define GST_RTSP_ADDRESS_POOL_CLASS_CAST(klass) ((GstRTSPAddressPoolClass*)(klass))
|
||||
|
||||
/**
|
||||
* GstRTSPAddressPoolResult:
|
||||
* @GST_RTSP_ADDRESS_POOL_OK: no error
|
||||
* @GST_RTSP_ADDRESS_POOL_EINVAL:invalid arguments were provided to a function
|
||||
* @GST_RTSP_ADDRESS_POOL_ERESERVED: the addres has already been reserved
|
||||
* @GST_RTSP_ADDRESS_POOL_ERANGE: the address is not in the pool
|
||||
* @GST_RTSP_ADDRESS_POOL_ELAST: last error
|
||||
*
|
||||
* Result codes from RTSP address pool functions.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_ADDRESS_POOL_OK = 0,
|
||||
/* errors */
|
||||
GST_RTSP_ADDRESS_POOL_EINVAL = -1,
|
||||
GST_RTSP_ADDRESS_POOL_ERESERVED = -2,
|
||||
GST_RTSP_ADDRESS_POOL_ERANGE = -3,
|
||||
|
||||
GST_RTSP_ADDRESS_POOL_ELAST = -4,
|
||||
} GstRTSPAddressPoolResult;
|
||||
|
||||
|
||||
typedef struct _GstRTSPAddress GstRTSPAddress;
|
||||
|
||||
typedef struct _GstRTSPAddressPool GstRTSPAddressPool;
|
||||
typedef struct _GstRTSPAddressPoolClass GstRTSPAddressPoolClass;
|
||||
typedef struct _GstRTSPAddressPoolPrivate GstRTSPAddressPoolPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPAddress:
|
||||
* @pool: the #GstRTSPAddressPool owner of this address
|
||||
* @address: the address
|
||||
* @port: the port number
|
||||
* @n_ports: number of ports
|
||||
* @ttl: TTL or 0 for unicast addresses
|
||||
*
|
||||
* An address
|
||||
*/
|
||||
struct _GstRTSPAddress {
|
||||
GstRTSPAddressPool *pool;
|
||||
|
||||
gchar *address;
|
||||
guint16 port;
|
||||
gint n_ports;
|
||||
guint8 ttl;
|
||||
|
||||
/*<private >*/
|
||||
gpointer priv;
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_address_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddress * gst_rtsp_address_copy (GstRTSPAddress *addr);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_address_free (GstRTSPAddress *addr);
|
||||
|
||||
/**
|
||||
* GstRTSPAddressFlags:
|
||||
* @GST_RTSP_ADDRESS_FLAG_NONE: no flags
|
||||
* @GST_RTSP_ADDRESS_FLAG_IPV4: an IPv4 address
|
||||
* @GST_RTSP_ADDRESS_FLAG_IPV6: and IPv6 address
|
||||
* @GST_RTSP_ADDRESS_FLAG_EVEN_PORT: address with an even port
|
||||
* @GST_RTSP_ADDRESS_FLAG_MULTICAST: a multicast address
|
||||
* @GST_RTSP_ADDRESS_FLAG_UNICAST: a unicast address
|
||||
*
|
||||
* Flags used to control allocation of addresses
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_ADDRESS_FLAG_NONE = 0,
|
||||
GST_RTSP_ADDRESS_FLAG_IPV4 = (1 << 0),
|
||||
GST_RTSP_ADDRESS_FLAG_IPV6 = (1 << 1),
|
||||
GST_RTSP_ADDRESS_FLAG_EVEN_PORT = (1 << 2),
|
||||
GST_RTSP_ADDRESS_FLAG_MULTICAST = (1 << 3),
|
||||
GST_RTSP_ADDRESS_FLAG_UNICAST = (1 << 4),
|
||||
} GstRTSPAddressFlags;
|
||||
|
||||
/**
|
||||
* GST_RTSP_ADDRESS_POOL_ANY_IPV4:
|
||||
*
|
||||
* Used with gst_rtsp_address_pool_add_range() to bind to all
|
||||
* IPv4 addresses
|
||||
*/
|
||||
#define GST_RTSP_ADDRESS_POOL_ANY_IPV4 "0.0.0.0"
|
||||
|
||||
/**
|
||||
* GST_RTSP_ADDRESS_POOL_ANY_IPV6:
|
||||
*
|
||||
* Used with gst_rtsp_address_pool_add_range() to bind to all
|
||||
* IPv6 addresses
|
||||
*/
|
||||
#define GST_RTSP_ADDRESS_POOL_ANY_IPV6 "::"
|
||||
|
||||
/**
|
||||
* GstRTSPAddressPool:
|
||||
* @parent: the parent GObject
|
||||
*
|
||||
* An address pool, all member are private
|
||||
*/
|
||||
struct _GstRTSPAddressPool {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPAddressPoolPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPAddressPoolClass:
|
||||
*
|
||||
* Opaque Address pool class.
|
||||
*/
|
||||
struct _GstRTSPAddressPoolClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_address_pool_get_type (void);
|
||||
|
||||
/* create a new address pool */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddressPool * gst_rtsp_address_pool_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
|
||||
const gchar *min_address,
|
||||
const gchar *max_address,
|
||||
guint16 min_port,
|
||||
guint16 max_port,
|
||||
guint8 ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddress * gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
|
||||
GstRTSPAddressFlags flags,
|
||||
gint n_ports);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddressPoolResult gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
|
||||
const gchar *ip_address,
|
||||
guint port,
|
||||
guint n_ports,
|
||||
guint ttl,
|
||||
GstRTSPAddress ** address);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPAddress, gst_rtsp_address_free)
|
||||
#endif
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPAddressPool, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_ADDRESS_POOL_H__ */
|
1264
gst/rtsp-server/rtsp-auth.c
Normal file
1264
gst/rtsp-server/rtsp-auth.c
Normal file
File diff suppressed because it is too large
Load diff
230
gst/rtsp-server/rtsp-auth.h
Normal file
230
gst/rtsp-server/rtsp-auth.h
Normal file
|
@ -0,0 +1,230 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef __GST_RTSP_AUTH_H__
|
||||
#define __GST_RTSP_AUTH_H__
|
||||
|
||||
typedef struct _GstRTSPAuth GstRTSPAuth;
|
||||
typedef struct _GstRTSPAuthClass GstRTSPAuthClass;
|
||||
typedef struct _GstRTSPAuthPrivate GstRTSPAuthPrivate;
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
#include "rtsp-client.h"
|
||||
#include "rtsp-token.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_AUTH (gst_rtsp_auth_get_type ())
|
||||
#define GST_IS_RTSP_AUTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_AUTH))
|
||||
#define GST_IS_RTSP_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_AUTH))
|
||||
#define GST_RTSP_AUTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_AUTH, GstRTSPAuthClass))
|
||||
#define GST_RTSP_AUTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_AUTH, GstRTSPAuth))
|
||||
#define GST_RTSP_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_AUTH, GstRTSPAuthClass))
|
||||
#define GST_RTSP_AUTH_CAST(obj) ((GstRTSPAuth*)(obj))
|
||||
#define GST_RTSP_AUTH_CLASS_CAST(klass) ((GstRTSPAuthClass*)(klass))
|
||||
|
||||
/**
|
||||
* GstRTSPAuth:
|
||||
*
|
||||
* The authentication structure.
|
||||
*/
|
||||
struct _GstRTSPAuth {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPAuthPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPAuthClass:
|
||||
* @authenticate: check the authentication of a client. The default implementation
|
||||
* checks if the authentication in the header matches one of the basic
|
||||
* authentication tokens. This function should set the authgroup field
|
||||
* in the context.
|
||||
* @check: check if a resource can be accessed. this function should
|
||||
* call authenticate to authenticate the client when needed. The method
|
||||
* should also construct and send an appropriate response message on
|
||||
* error.
|
||||
*
|
||||
* The authentication class.
|
||||
*/
|
||||
struct _GstRTSPAuthClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
gboolean (*authenticate) (GstRTSPAuth *auth, GstRTSPContext *ctx);
|
||||
gboolean (*check) (GstRTSPAuth *auth, GstRTSPContext *ctx,
|
||||
const gchar *check);
|
||||
void (*generate_authenticate_header) (GstRTSPAuth *auth, GstRTSPContext *ctx);
|
||||
gboolean (*accept_certificate) (GstRTSPAuth *auth,
|
||||
GTlsConnection *connection,
|
||||
GTlsCertificate *peer_cert,
|
||||
GTlsCertificateFlags errors);
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING - 1];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_auth_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAuth * gst_rtsp_auth_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_tls_certificate (GstRTSPAuth *auth, GTlsCertificate *cert);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GTlsCertificate * gst_rtsp_auth_get_tls_certificate (GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_tls_database (GstRTSPAuth *auth, GTlsDatabase *database);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GTlsDatabase * gst_rtsp_auth_get_tls_database (GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_tls_authentication_mode (GstRTSPAuth *auth, GTlsAuthenticationMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GTlsAuthenticationMode gst_rtsp_auth_get_tls_authentication_mode (GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_default_token (GstRTSPAuth *auth, GstRTSPToken *token);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPToken * gst_rtsp_auth_get_default_token (GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_add_basic (GstRTSPAuth *auth, const gchar * basic,
|
||||
GstRTSPToken *token);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_remove_basic (GstRTSPAuth *auth, const gchar * basic);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_add_digest (GstRTSPAuth *auth, const gchar *user,
|
||||
const gchar *pass, GstRTSPToken *token);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_remove_digest (GstRTSPAuth *auth, const gchar *user);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_supported_methods (GstRTSPAuth *auth, GstRTSPAuthMethod methods);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAuthMethod gst_rtsp_auth_get_supported_methods (GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_auth_check (const gchar *check);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_auth_parse_htdigest (GstRTSPAuth *auth, const gchar *path, GstRTSPToken *token);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_auth_set_realm (GstRTSPAuth *auth, const gchar *realm);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_auth_get_realm (GstRTSPAuth *auth);
|
||||
|
||||
/* helpers */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass);
|
||||
|
||||
/* checks */
|
||||
/**
|
||||
* GST_RTSP_AUTH_CHECK_CONNECT:
|
||||
*
|
||||
* Check a new connection
|
||||
*/
|
||||
#define GST_RTSP_AUTH_CHECK_CONNECT "auth.check.connect"
|
||||
/**
|
||||
* GST_RTSP_AUTH_CHECK_URL:
|
||||
*
|
||||
* Check the URL and methods
|
||||
*/
|
||||
#define GST_RTSP_AUTH_CHECK_URL "auth.check.url"
|
||||
/**
|
||||
* GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS:
|
||||
*
|
||||
* Check if access is allowed to a factory.
|
||||
* When access is not allowed an 404 Not Found is sent in the response.
|
||||
*/
|
||||
#define GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_ACCESS "auth.check.media.factory.access"
|
||||
/**
|
||||
* GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT:
|
||||
*
|
||||
* Check if media can be constructed from a media factory
|
||||
* A response should be sent on error.
|
||||
*/
|
||||
#define GST_RTSP_AUTH_CHECK_MEDIA_FACTORY_CONSTRUCT "auth.check.media.factory.construct"
|
||||
/**
|
||||
* GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS:
|
||||
*
|
||||
* Check if the client can specify TTL, destination and
|
||||
* port pair in multicast. No response is sent when the check returns
|
||||
* %FALSE.
|
||||
*/
|
||||
#define GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS "auth.check.transport.client-settings"
|
||||
|
||||
|
||||
/* tokens */
|
||||
/**
|
||||
* GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE:
|
||||
*
|
||||
* G_TYPE_STRING, the role to use when dealing with media factories
|
||||
*
|
||||
* The default #GstRTSPAuth object uses this string in the token to find the
|
||||
* role of the media factory. It will then retrieve the #GstRTSPPermissions of
|
||||
* the media factory and retrieve the role with the same name.
|
||||
*/
|
||||
#define GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE "media.factory.role"
|
||||
/**
|
||||
* GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS:
|
||||
*
|
||||
* G_TYPE_BOOLEAN, %TRUE if the client can specify TTL, destination and
|
||||
* port pair in multicast.
|
||||
*/
|
||||
#define GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS "transport.client-settings"
|
||||
|
||||
/* permissions */
|
||||
/**
|
||||
* GST_RTSP_PERM_MEDIA_FACTORY_ACCESS:
|
||||
*
|
||||
* G_TYPE_BOOLEAN, %TRUE if the media can be accessed, %FALSE will
|
||||
* return a 404 Not Found error when trying to access the media.
|
||||
*/
|
||||
#define GST_RTSP_PERM_MEDIA_FACTORY_ACCESS "media.factory.access"
|
||||
/**
|
||||
* GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT:
|
||||
*
|
||||
* G_TYPE_BOOLEAN, %TRUE if the media can be constructed, %FALSE will
|
||||
* return a 404 Not Found error when trying to access the media.
|
||||
*/
|
||||
#define GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT "media.factory.construct"
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPAuth, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_AUTH_H__ */
|
5404
gst/rtsp-server/rtsp-client.c
Normal file
5404
gst/rtsp-server/rtsp-client.c
Normal file
File diff suppressed because it is too large
Load diff
294
gst/rtsp-server/rtsp-client.h
Normal file
294
gst/rtsp-server/rtsp-client.h
Normal file
|
@ -0,0 +1,294 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/gstrtspconnection.h>
|
||||
|
||||
#ifndef __GST_RTSP_CLIENT_H__
|
||||
#define __GST_RTSP_CLIENT_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstRTSPClient GstRTSPClient;
|
||||
typedef struct _GstRTSPClientClass GstRTSPClientClass;
|
||||
typedef struct _GstRTSPClientPrivate GstRTSPClientPrivate;
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
#include "rtsp-context.h"
|
||||
#include "rtsp-mount-points.h"
|
||||
#include "rtsp-sdp.h"
|
||||
#include "rtsp-auth.h"
|
||||
|
||||
#define GST_TYPE_RTSP_CLIENT (gst_rtsp_client_get_type ())
|
||||
#define GST_IS_RTSP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_CLIENT))
|
||||
#define GST_IS_RTSP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_CLIENT))
|
||||
#define GST_RTSP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClientClass))
|
||||
#define GST_RTSP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_CLIENT, GstRTSPClient))
|
||||
#define GST_RTSP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_CLIENT, GstRTSPClientClass))
|
||||
#define GST_RTSP_CLIENT_CAST(obj) ((GstRTSPClient*)(obj))
|
||||
#define GST_RTSP_CLIENT_CLASS_CAST(klass) ((GstRTSPClientClass*)(klass))
|
||||
|
||||
/**
|
||||
* GstRTSPClientSendFunc:
|
||||
* @client: a #GstRTSPClient
|
||||
* @message: a #GstRTSPMessage
|
||||
* @close: close the connection
|
||||
* @user_data: user data when registering the callback
|
||||
*
|
||||
* This callback is called when @client wants to send @message. When @close is
|
||||
* %TRUE, the connection should be closed when the message has been sent.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
typedef gboolean (*GstRTSPClientSendFunc) (GstRTSPClient *client,
|
||||
GstRTSPMessage *message,
|
||||
gboolean close,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPClientSendMessagesFunc:
|
||||
* @client: a #GstRTSPClient
|
||||
* @messages: #GstRTSPMessage
|
||||
* @n_messages: number of messages
|
||||
* @close: close the connection
|
||||
* @user_data: user data when registering the callback
|
||||
*
|
||||
* This callback is called when @client wants to send @messages. When @close is
|
||||
* %TRUE, the connection should be closed when the message has been sent.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
typedef gboolean (*GstRTSPClientSendMessagesFunc) (GstRTSPClient *client,
|
||||
GstRTSPMessage *messages,
|
||||
guint n_messages,
|
||||
gboolean close,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPClient:
|
||||
*
|
||||
* The client object represents the connection and its state with a client.
|
||||
*/
|
||||
struct _GstRTSPClient {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPClientPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPClientClass:
|
||||
* @create_sdp: called when the SDP needs to be created for media.
|
||||
* @configure_client_media: called when the stream in media needs to be configured.
|
||||
* The default implementation will configure the blocksize on the payloader when
|
||||
* spcified in the request headers.
|
||||
* @configure_client_transport: called when the client transport needs to be
|
||||
* configured.
|
||||
* @params_set: set parameters. This function should also initialize the
|
||||
* RTSP response(ctx->response) via a call to gst_rtsp_message_init_response()
|
||||
* @params_get: get parameters. This function should also initialize the
|
||||
* RTSP response(ctx->response) via a call to gst_rtsp_message_init_response()
|
||||
* @make_path_from_uri: called to create path from uri.
|
||||
* @adjust_play_mode: called to give the application the possibility to adjust
|
||||
* the range, seek flags, rate and rate-control. Since 1.18
|
||||
* @adjust_play_response: called to give the implementation the possibility to
|
||||
* adjust the response to a play request, for example if extra headers were
|
||||
* parsed when #GstRTSPClientClass.adjust_play_mode was called. Since 1.18
|
||||
* @tunnel_http_response: called when a response to the GET request is about to
|
||||
* be sent for a tunneled connection. The response can be modified. Since: 1.4
|
||||
*
|
||||
* The client class structure.
|
||||
*/
|
||||
struct _GstRTSPClientClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
GstSDPMessage * (*create_sdp) (GstRTSPClient *client, GstRTSPMedia *media);
|
||||
gboolean (*configure_client_media) (GstRTSPClient * client,
|
||||
GstRTSPMedia * media, GstRTSPStream * stream,
|
||||
GstRTSPContext * ctx);
|
||||
gboolean (*configure_client_transport) (GstRTSPClient * client,
|
||||
GstRTSPContext * ctx,
|
||||
GstRTSPTransport * ct);
|
||||
GstRTSPResult (*params_set) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPResult (*params_get) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
gchar * (*make_path_from_uri) (GstRTSPClient *client, const GstRTSPUrl *uri);
|
||||
GstRTSPStatusCode (*adjust_play_mode) (GstRTSPClient * client,
|
||||
GstRTSPContext * context,
|
||||
GstRTSPTimeRange ** range,
|
||||
GstSeekFlags * flags,
|
||||
gdouble * rate,
|
||||
GstClockTime * trickmode_interval,
|
||||
gboolean * enable_rate_control);
|
||||
GstRTSPStatusCode (*adjust_play_response) (GstRTSPClient * client,
|
||||
GstRTSPContext * context);
|
||||
|
||||
/* signals */
|
||||
void (*closed) (GstRTSPClient *client);
|
||||
void (*new_session) (GstRTSPClient *client, GstRTSPSession *session);
|
||||
void (*options_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*describe_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*setup_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*play_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*pause_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*teardown_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*set_parameter_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*get_parameter_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*handle_response) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
|
||||
void (*tunnel_http_response) (GstRTSPClient * client, GstRTSPMessage * request,
|
||||
GstRTSPMessage * response);
|
||||
void (*send_message) (GstRTSPClient * client, GstRTSPContext *ctx,
|
||||
GstRTSPMessage * response);
|
||||
|
||||
gboolean (*handle_sdp) (GstRTSPClient *client, GstRTSPContext *ctx, GstRTSPMedia *media, GstSDPMessage *sdp);
|
||||
|
||||
void (*announce_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*record_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
gchar* (*check_requirements) (GstRTSPClient *client, GstRTSPContext *ctx, gchar ** arr);
|
||||
|
||||
GstRTSPStatusCode (*pre_options_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_describe_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_setup_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_play_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_pause_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_teardown_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_set_parameter_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_get_parameter_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_announce_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
GstRTSPStatusCode (*pre_record_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-18];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_client_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPClient * gst_rtsp_client_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_session_pool (GstRTSPClient *client,
|
||||
GstRTSPSessionPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionPool * gst_rtsp_client_get_session_pool (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_mount_points (GstRTSPClient *client,
|
||||
GstRTSPMountPoints *mounts);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMountPoints * gst_rtsp_client_get_mount_points (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_content_length_limit (GstRTSPClient *client, guint limit);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_client_get_content_length_limit (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_auth (GstRTSPClient *client, GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAuth * gst_rtsp_client_get_auth (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_thread_pool (GstRTSPClient *client, GstRTSPThreadPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPThreadPool * gst_rtsp_client_get_thread_pool (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_client_set_connection (GstRTSPClient *client, GstRTSPConnection *conn);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPConnection * gst_rtsp_client_get_connection (GstRTSPClient *client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_client_attach (GstRTSPClient *client,
|
||||
GMainContext *context);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_close (GstRTSPClient * client);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_send_func (GstRTSPClient *client,
|
||||
GstRTSPClientSendFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_client_set_send_messages_func (GstRTSPClient *client,
|
||||
GstRTSPClientSendMessagesFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPResult gst_rtsp_client_handle_message (GstRTSPClient *client,
|
||||
GstRTSPMessage *message);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPResult gst_rtsp_client_send_message (GstRTSPClient * client,
|
||||
GstRTSPSession *session,
|
||||
GstRTSPMessage *message);
|
||||
/**
|
||||
* GstRTSPClientSessionFilterFunc:
|
||||
* @client: a #GstRTSPClient object
|
||||
* @sess: a #GstRTSPSession in @client
|
||||
* @user_data: user data that has been given to gst_rtsp_client_session_filter()
|
||||
*
|
||||
* This function will be called by the gst_rtsp_client_session_filter(). An
|
||||
* implementation should return a value of #GstRTSPFilterResult.
|
||||
*
|
||||
* When this function returns #GST_RTSP_FILTER_REMOVE, @sess will be removed
|
||||
* from @client.
|
||||
*
|
||||
* A return value of #GST_RTSP_FILTER_KEEP will leave @sess untouched in
|
||||
* @client.
|
||||
*
|
||||
* A value of #GST_RTSP_FILTER_REF will add @sess to the result #GList of
|
||||
* gst_rtsp_client_session_filter().
|
||||
*
|
||||
* Returns: a #GstRTSPFilterResult.
|
||||
*/
|
||||
typedef GstRTSPFilterResult (*GstRTSPClientSessionFilterFunc) (GstRTSPClient *client,
|
||||
GstRTSPSession *sess,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GList * gst_rtsp_client_session_filter (GstRTSPClient *client,
|
||||
GstRTSPClientSessionFilterFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStreamTransport * gst_rtsp_client_get_stream_transport (GstRTSPClient *client,
|
||||
guint8 channel);
|
||||
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPClient, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_CLIENT_H__ */
|
95
gst/rtsp-server/rtsp-context.c
Normal file
95
gst/rtsp-server/rtsp-context.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2013 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-context
|
||||
* @short_description: A client request context
|
||||
* @see_also: #GstRTSPServer, #GstRTSPClient
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "rtsp-context.h"
|
||||
|
||||
G_DEFINE_POINTER_TYPE (GstRTSPContext, gst_rtsp_context);
|
||||
|
||||
static GPrivate current_context;
|
||||
|
||||
/**
|
||||
* gst_rtsp_context_get_current: (skip):
|
||||
*
|
||||
* Get the current #GstRTSPContext. This object is retrieved from the
|
||||
* current thread that is handling the request for a client.
|
||||
*
|
||||
* Returns: a #GstRTSPContext
|
||||
*/
|
||||
GstRTSPContext *
|
||||
gst_rtsp_context_get_current (void)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
l = g_private_get (¤t_context);
|
||||
if (l == NULL)
|
||||
return NULL;
|
||||
|
||||
return (GstRTSPContext *) (l->data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_context_push_current:
|
||||
* @ctx: a #GstRTSPContext
|
||||
*
|
||||
* Pushes @ctx onto the context stack. The current
|
||||
* context can then be received using gst_rtsp_context_get_current().
|
||||
**/
|
||||
void
|
||||
gst_rtsp_context_push_current (GstRTSPContext * ctx)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_return_if_fail (ctx != NULL);
|
||||
|
||||
l = g_private_get (¤t_context);
|
||||
l = g_slist_prepend (l, ctx);
|
||||
g_private_set (¤t_context, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_context_pop_current:
|
||||
* @ctx: a #GstRTSPContext
|
||||
*
|
||||
* Pops @ctx off the context stack (verifying that @ctx
|
||||
* is on the top of the stack).
|
||||
**/
|
||||
void
|
||||
gst_rtsp_context_pop_current (GstRTSPContext * ctx)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
l = g_private_get (¤t_context);
|
||||
|
||||
g_return_if_fail (l != NULL);
|
||||
g_return_if_fail (l->data == ctx);
|
||||
|
||||
l = g_slist_delete_link (l, l);
|
||||
g_private_set (¤t_context, l);
|
||||
}
|
97
gst/rtsp-server/rtsp-context.h
Normal file
97
gst/rtsp-server/rtsp-context.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/gstrtspconnection.h>
|
||||
|
||||
#ifndef __GST_RTSP_CONTEXT_H__
|
||||
#define __GST_RTSP_CONTEXT_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_CONTEXT (gst_rtsp_context_get_type ())
|
||||
|
||||
typedef struct _GstRTSPContext GstRTSPContext;
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
#include "rtsp-server-object.h"
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-media-factory.h"
|
||||
#include "rtsp-session-media.h"
|
||||
#include "rtsp-auth.h"
|
||||
#include "rtsp-thread-pool.h"
|
||||
#include "rtsp-token.h"
|
||||
|
||||
/**
|
||||
* GstRTSPContext:
|
||||
* @server: the server
|
||||
* @conn: the connection
|
||||
* @client: the client
|
||||
* @request: the complete request
|
||||
* @uri: the complete url parsed from @request
|
||||
* @method: the parsed method of @uri
|
||||
* @auth: the current auth object or %NULL
|
||||
* @token: authorisation token
|
||||
* @session: the session, can be %NULL
|
||||
* @sessmedia: the session media for the url can be %NULL
|
||||
* @factory: the media factory for the url, can be %NULL
|
||||
* @media: the media for the url can be %NULL
|
||||
* @stream: the stream for the url can be %NULL
|
||||
* @response: the response
|
||||
* @trans: the stream transport, can be %NULL
|
||||
*
|
||||
* Information passed around containing the context of a request.
|
||||
*/
|
||||
struct _GstRTSPContext {
|
||||
GstRTSPServer *server;
|
||||
GstRTSPConnection *conn;
|
||||
GstRTSPClient *client;
|
||||
GstRTSPMessage *request;
|
||||
GstRTSPUrl *uri;
|
||||
GstRTSPMethod method;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPToken *token;
|
||||
GstRTSPSession *session;
|
||||
GstRTSPSessionMedia *sessmedia;
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPMedia *media;
|
||||
GstRTSPStream *stream;
|
||||
GstRTSPMessage *response;
|
||||
GstRTSPStreamTransport *trans;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING - 1];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_context_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPContext * gst_rtsp_context_get_current (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_context_push_current (GstRTSPContext * ctx);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_context_pop_current (GstRTSPContext * ctx);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_CONTEXT_H__ */
|
352
gst/rtsp-server/rtsp-latency-bin.c
Normal file
352
gst/rtsp-server/rtsp-latency-bin.c
Normal file
|
@ -0,0 +1,352 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2018 Ognyan Tonchev <ognyan@axis.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-latency-bin.h"
|
||||
|
||||
struct _GstRTSPLatencyBinPrivate
|
||||
{
|
||||
GstPad *sinkpad;
|
||||
GstElement *element;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ELEMENT,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_latency_bin_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_latency_bin_debug
|
||||
|
||||
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static void gst_rtsp_latency_bin_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_latency_bin_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static gboolean gst_rtsp_latency_bin_element_query (GstElement * element,
|
||||
GstQuery * query);
|
||||
static gboolean gst_rtsp_latency_bin_element_event (GstElement * element,
|
||||
GstEvent * event);
|
||||
static void gst_rtsp_latency_bin_message_handler (GstBin * bin,
|
||||
GstMessage * message);
|
||||
static gboolean gst_rtsp_latency_bin_add_element (GstRTSPLatencyBin *
|
||||
latency_bin, GstElement * element);
|
||||
static GstStateChangeReturn gst_rtsp_latency_bin_change_state (GstElement *
|
||||
element, GstStateChange transition);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPLatencyBin, gst_rtsp_latency_bin,
|
||||
GST_TYPE_BIN);
|
||||
|
||||
static void
|
||||
gst_rtsp_latency_bin_class_init (GstRTSPLatencyBinClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_klass = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (klass);
|
||||
GstBinClass *gstbin_klass = GST_BIN_CLASS (klass);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_latency_bin_debug,
|
||||
"rtsplatencybin", 0, "GstRTSPLatencyBin");
|
||||
|
||||
gobject_klass->get_property = gst_rtsp_latency_bin_get_property;
|
||||
gobject_klass->set_property = gst_rtsp_latency_bin_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_klass, PROP_ELEMENT,
|
||||
g_param_spec_object ("element", "The Element",
|
||||
"The GstElement to prevent from affecting piplines latency",
|
||||
GST_TYPE_ELEMENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
||||
|
||||
gstelement_klass->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_change_state);
|
||||
gstelement_klass->query =
|
||||
GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_element_query);
|
||||
gstelement_klass->send_event =
|
||||
GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_element_event);
|
||||
|
||||
gstbin_klass->handle_message =
|
||||
GST_DEBUG_FUNCPTR (gst_rtsp_latency_bin_message_handler);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_latency_bin_init (GstRTSPLatencyBin * latency_bin)
|
||||
{
|
||||
GST_OBJECT_FLAG_SET (latency_bin, GST_ELEMENT_FLAG_SINK);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_latency_bin_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (object);
|
||||
GstRTSPLatencyBinPrivate *priv =
|
||||
gst_rtsp_latency_bin_get_instance_private (latency_bin);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_ELEMENT:
|
||||
g_value_set_object (value, priv->element);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_latency_bin_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_ELEMENT:
|
||||
if (!gst_rtsp_latency_bin_add_element (latency_bin,
|
||||
g_value_get_object (value))) {
|
||||
GST_WARNING_OBJECT (latency_bin, "Could not add the element");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_latency_bin_add_element (GstRTSPLatencyBin * latency_bin,
|
||||
GstElement * element)
|
||||
{
|
||||
GstRTSPLatencyBinPrivate *priv =
|
||||
gst_rtsp_latency_bin_get_instance_private (latency_bin);
|
||||
GstPad *pad;
|
||||
GstPadTemplate *templ;
|
||||
|
||||
GST_DEBUG_OBJECT (latency_bin, "Adding element to latencybin : %s",
|
||||
GST_ELEMENT_NAME (element));
|
||||
|
||||
if (!element) {
|
||||
goto no_element;
|
||||
}
|
||||
|
||||
/* add the element to ourself */
|
||||
gst_object_ref (element);
|
||||
gst_bin_add (GST_BIN (latency_bin), element);
|
||||
priv->element = element;
|
||||
|
||||
/* add ghost pad first */
|
||||
templ = gst_static_pad_template_get (&sinktemplate);
|
||||
priv->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
|
||||
gst_object_unref (templ);
|
||||
g_assert (priv->sinkpad);
|
||||
|
||||
gst_element_add_pad (GST_ELEMENT (latency_bin), priv->sinkpad);
|
||||
|
||||
/* and link it to our element */
|
||||
pad = gst_element_get_static_pad (element, "sink");
|
||||
if (!pad) {
|
||||
goto no_sink_pad;
|
||||
}
|
||||
|
||||
if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (priv->sinkpad), pad)) {
|
||||
goto set_target_failed;
|
||||
}
|
||||
|
||||
gst_object_unref (pad);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORs */
|
||||
no_element:
|
||||
{
|
||||
GST_WARNING_OBJECT (latency_bin, "No element, not adding");
|
||||
return FALSE;
|
||||
}
|
||||
no_sink_pad:
|
||||
{
|
||||
GST_WARNING_OBJECT (latency_bin, "The element has no sink pad");
|
||||
return FALSE;
|
||||
}
|
||||
set_target_failed:
|
||||
{
|
||||
GST_WARNING_OBJECT (latency_bin, "Could not set target pad");
|
||||
gst_object_unref (pad);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_latency_bin_element_query (GstElement * element, GstQuery * query)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
GST_LOG_OBJECT (element, "got query %s", GST_QUERY_TYPE_NAME (query));
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_LATENCY:
|
||||
/* ignoring latency query, we do not want our element to affect latency on
|
||||
* the rest of the pipeline */
|
||||
GST_DEBUG_OBJECT (element, "ignoring latency query");
|
||||
gst_query_set_latency (query, FALSE, 0, -1);
|
||||
break;
|
||||
default:
|
||||
ret =
|
||||
GST_ELEMENT_CLASS (gst_rtsp_latency_bin_parent_class)->query
|
||||
(GST_ELEMENT (element), query);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_latency_bin_element_event (GstElement * element, GstEvent * event)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
GST_LOG_OBJECT (element, "got event %s", GST_EVENT_TYPE_NAME (event));
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_LATENCY:
|
||||
/* ignoring latency event, we will configure latency on our element when
|
||||
* going to PLAYING */
|
||||
GST_DEBUG_OBJECT (element, "ignoring latency event");
|
||||
gst_event_unref (event);
|
||||
break;
|
||||
default:
|
||||
ret =
|
||||
GST_ELEMENT_CLASS (gst_rtsp_latency_bin_parent_class)->send_event
|
||||
(GST_ELEMENT (element), event);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_latency_bin_recalculate_latency (GstRTSPLatencyBin * latency_bin)
|
||||
{
|
||||
GstRTSPLatencyBinPrivate *priv =
|
||||
gst_rtsp_latency_bin_get_instance_private (latency_bin);
|
||||
GstEvent *latency;
|
||||
GstQuery *query;
|
||||
GstClockTime min_latency;
|
||||
|
||||
GST_DEBUG_OBJECT (latency_bin, "Recalculating latency");
|
||||
|
||||
if (!priv->element) {
|
||||
GST_WARNING_OBJECT (latency_bin, "We do not have an element");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
query = gst_query_new_latency ();
|
||||
|
||||
if (!gst_element_query (priv->element, query)) {
|
||||
GST_WARNING_OBJECT (latency_bin, "Latency query failed");
|
||||
gst_query_unref (query);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_query_parse_latency (query, NULL, &min_latency, NULL);
|
||||
gst_query_unref (query);
|
||||
|
||||
GST_LOG_OBJECT (latency_bin, "Got min_latency from stream: %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (min_latency));
|
||||
|
||||
latency = gst_event_new_latency (min_latency);
|
||||
if (!gst_element_send_event (priv->element, latency)) {
|
||||
GST_WARNING_OBJECT (latency_bin, "Sending latency event to stream failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_latency_bin_message_handler (GstBin * bin, GstMessage * message)
|
||||
{
|
||||
GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (bin);
|
||||
|
||||
GST_LOG_OBJECT (bin, "Got message %s", GST_MESSAGE_TYPE_NAME (message));
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_LATENCY:{
|
||||
if (!gst_rtsp_latency_bin_recalculate_latency (latency_bin)) {
|
||||
GST_WARNING_OBJECT (latency_bin, "Could not recalculate latency");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GST_BIN_CLASS (gst_rtsp_latency_bin_parent_class)->handle_message (bin,
|
||||
message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_rtsp_latency_bin_change_state (GstElement * element, GstStateChange
|
||||
transition)
|
||||
{
|
||||
GstRTSPLatencyBin *latency_bin = GST_RTSP_LATENCY_BIN (element);
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
GST_LOG_OBJECT (latency_bin, "Changing state %s",
|
||||
gst_state_change_get_name (transition));
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PLAYING:
|
||||
if (!gst_rtsp_latency_bin_recalculate_latency (latency_bin)) {
|
||||
GST_WARNING_OBJECT (latency_bin, "Could not recalculate latency");
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (gst_rtsp_latency_bin_parent_class)->change_state
|
||||
(element, transition);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_latency_bin_new:
|
||||
* @element: (transfer full): a #GstElement
|
||||
*
|
||||
* Create a bin that encapsulates an @element and prevents it from affecting
|
||||
* latency on the whole pipeline.
|
||||
*
|
||||
* Returns: A newly created #GstRTSPLatencyBin element, or %NULL on failure
|
||||
*/
|
||||
GstElement *
|
||||
gst_rtsp_latency_bin_new (GstElement * element)
|
||||
{
|
||||
GstElement *gst_rtsp_latency_bin;
|
||||
|
||||
g_return_val_if_fail (element, NULL);
|
||||
|
||||
gst_rtsp_latency_bin = g_object_new (GST_RTSP_LATENCY_BIN_TYPE, "element",
|
||||
element, NULL);
|
||||
gst_object_unref (element);
|
||||
return gst_rtsp_latency_bin;
|
||||
}
|
59
gst/rtsp-server/rtsp-latency-bin.h
Normal file
59
gst/rtsp-server/rtsp-latency-bin.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2018 Ognyan Tonchev <ognyan@axis.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_LATENCY_BIN_H__
|
||||
#define __GST_RTSP_LATENCY_BIN_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstRTSPLatencyBin GstRTSPLatencyBin;
|
||||
typedef struct _GstRTSPLatencyBinClass GstRTSPLatencyBinClass;
|
||||
typedef struct _GstRTSPLatencyBinPrivate GstRTSPLatencyBinPrivate;
|
||||
|
||||
#define GST_RTSP_LATENCY_BIN_TYPE (gst_rtsp_latency_bin_get_type ())
|
||||
#define IS_GST_RTSP_LATENCY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_RTSP_LATENCY_BIN_TYPE))
|
||||
#define IS_GST_RTSP_LATENCY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_RTSP_LATENCY_BIN_TYPE))
|
||||
#define GST_RTSP_LATENCY_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBinClass))
|
||||
#define GST_RTSP_LATENCY_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBin))
|
||||
#define GST_RTSP_LATENCY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_RTSP_LATENCY_BIN_TYPE, GstRTSPLatencyBinClass))
|
||||
#define GST_RTSP_LATENCY_BIN_CAST(obj) ((GstRTSPLatencyBin*)(obj))
|
||||
#define GST_RTSP_LATENCY_BIN_CLASS_CAST(klass) ((GstRTSPLatencyBinClass*)(klass))
|
||||
|
||||
struct _GstRTSPLatencyBin {
|
||||
GstBin parent;
|
||||
|
||||
GstRTSPLatencyBinPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GstRTSPLatencyBinClass {
|
||||
GstBinClass parent_class;
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_latency_bin_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_latency_bin_new (GstElement * element);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_LATENCY_BIN_H__ */
|
646
gst/rtsp-server/rtsp-media-factory-uri.c
Normal file
646
gst/rtsp-server/rtsp-media-factory-uri.c
Normal file
|
@ -0,0 +1,646 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-media-factory-uri
|
||||
* @short_description: A factory for URI sources
|
||||
* @see_also: #GstRTSPMediaFactory, #GstRTSPMedia
|
||||
*
|
||||
* This specialized #GstRTSPMediaFactory constructs media pipelines from a URI,
|
||||
* given with gst_rtsp_media_factory_uri_set_uri().
|
||||
*
|
||||
* It will automatically demux and payload the different streams found in the
|
||||
* media at URL.
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-media-factory-uri.h"
|
||||
|
||||
struct _GstRTSPMediaFactoryURIPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
gchar *uri; /* protected by lock */
|
||||
gboolean use_gstpay;
|
||||
|
||||
GstCaps *raw_vcaps;
|
||||
GstCaps *raw_acaps;
|
||||
GList *demuxers;
|
||||
GList *payloaders;
|
||||
GList *decoders;
|
||||
};
|
||||
|
||||
#define DEFAULT_URI NULL
|
||||
#define DEFAULT_USE_GSTPAY FALSE
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_URI,
|
||||
PROP_USE_GSTPAY,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
|
||||
#define RAW_VIDEO_CAPS \
|
||||
"video/x-raw"
|
||||
|
||||
#define RAW_AUDIO_CAPS \
|
||||
"audio/x-raw"
|
||||
|
||||
static GstStaticCaps raw_video_caps = GST_STATIC_CAPS (RAW_VIDEO_CAPS);
|
||||
static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS (RAW_AUDIO_CAPS);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstRTSPMediaFactoryURI *factory;
|
||||
guint pt;
|
||||
} FactoryData;
|
||||
|
||||
static void
|
||||
free_data (FactoryData * data)
|
||||
{
|
||||
g_object_unref (data->factory);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static const gchar *factory_key = "GstRTSPMediaFactoryURI";
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_media_factory_uri_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_media_factory_uri_debug
|
||||
|
||||
static void gst_rtsp_media_factory_uri_get_property (GObject * object,
|
||||
guint propid, GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_media_factory_uri_set_property (GObject * object,
|
||||
guint propid, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_media_factory_uri_finalize (GObject * obj);
|
||||
|
||||
static GstElement *rtsp_media_factory_uri_create_element (GstRTSPMediaFactory *
|
||||
factory, const GstRTSPUrl * url);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPMediaFactoryURI, gst_rtsp_media_factory_uri,
|
||||
GST_TYPE_RTSP_MEDIA_FACTORY);
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_uri_class_init (GstRTSPMediaFactoryURIClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstRTSPMediaFactoryClass *mediafactory_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
mediafactory_class = GST_RTSP_MEDIA_FACTORY_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gst_rtsp_media_factory_uri_get_property;
|
||||
gobject_class->set_property = gst_rtsp_media_factory_uri_set_property;
|
||||
gobject_class->finalize = gst_rtsp_media_factory_uri_finalize;
|
||||
|
||||
/**
|
||||
* GstRTSPMediaFactoryURI::uri:
|
||||
*
|
||||
* The uri of the resource that will be served by this factory.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_URI,
|
||||
g_param_spec_string ("uri", "URI",
|
||||
"The URI of the resource to stream", DEFAULT_URI,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* GstRTSPMediaFactoryURI::use-gstpay:
|
||||
*
|
||||
* Allow the usage of gstpay in order to avoid decoding of compressed formats
|
||||
* without a payloader.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_USE_GSTPAY,
|
||||
g_param_spec_boolean ("use-gstpay", "Use gstpay",
|
||||
"Use the gstpay payloader to avoid decoding", DEFAULT_USE_GSTPAY,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
mediafactory_class->create_element = rtsp_media_factory_uri_create_element;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_media_factory_uri_debug, "rtspmediafactoryuri",
|
||||
0, "GstRTSPMediaFactoryUri");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GList *demux;
|
||||
GList *payload;
|
||||
GList *decode;
|
||||
} FilterData;
|
||||
|
||||
static gboolean
|
||||
payloader_filter (GstPluginFeature * feature, FilterData * data)
|
||||
{
|
||||
const gchar *klass;
|
||||
GstElementFactory *fact;
|
||||
GList **list = NULL;
|
||||
|
||||
/* we only care about element factories */
|
||||
if (G_UNLIKELY (!GST_IS_ELEMENT_FACTORY (feature)))
|
||||
return FALSE;
|
||||
|
||||
if (gst_plugin_feature_get_rank (feature) < GST_RANK_MARGINAL)
|
||||
return FALSE;
|
||||
|
||||
fact = GST_ELEMENT_FACTORY_CAST (feature);
|
||||
|
||||
klass = gst_element_factory_get_metadata (fact, GST_ELEMENT_METADATA_KLASS);
|
||||
|
||||
if (strstr (klass, "Decoder"))
|
||||
list = &data->decode;
|
||||
else if (strstr (klass, "Demux"))
|
||||
list = &data->demux;
|
||||
else if (strstr (klass, "Parser") && strstr (klass, "Codec"))
|
||||
list = &data->demux;
|
||||
else if (strstr (klass, "Payloader") && strstr (klass, "RTP"))
|
||||
list = &data->payload;
|
||||
|
||||
if (list) {
|
||||
GST_DEBUG ("adding %s", GST_OBJECT_NAME (fact));
|
||||
*list = g_list_prepend (*list, gst_object_ref (fact));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_uri_init (GstRTSPMediaFactoryURI * factory)
|
||||
{
|
||||
GstRTSPMediaFactoryURIPrivate *priv =
|
||||
gst_rtsp_media_factory_uri_get_instance_private (factory);
|
||||
FilterData data = { NULL, NULL, NULL };
|
||||
|
||||
GST_DEBUG_OBJECT (factory, "new");
|
||||
|
||||
factory->priv = priv;
|
||||
|
||||
priv->uri = g_strdup (DEFAULT_URI);
|
||||
priv->use_gstpay = DEFAULT_USE_GSTPAY;
|
||||
g_mutex_init (&priv->lock);
|
||||
|
||||
/* get the feature list using the filter */
|
||||
gst_registry_feature_filter (gst_registry_get (), (GstPluginFeatureFilter)
|
||||
payloader_filter, FALSE, &data);
|
||||
/* sort */
|
||||
priv->demuxers =
|
||||
g_list_sort (data.demux, gst_plugin_feature_rank_compare_func);
|
||||
priv->payloaders =
|
||||
g_list_sort (data.payload, gst_plugin_feature_rank_compare_func);
|
||||
priv->decoders =
|
||||
g_list_sort (data.decode, gst_plugin_feature_rank_compare_func);
|
||||
|
||||
priv->raw_vcaps = gst_static_caps_get (&raw_video_caps);
|
||||
priv->raw_acaps = gst_static_caps_get (&raw_audio_caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_uri_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPMediaFactoryURI *factory = GST_RTSP_MEDIA_FACTORY_URI (obj);
|
||||
GstRTSPMediaFactoryURIPrivate *priv = factory->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (factory, "finalize");
|
||||
|
||||
g_free (priv->uri);
|
||||
gst_plugin_feature_list_free (priv->demuxers);
|
||||
gst_plugin_feature_list_free (priv->payloaders);
|
||||
gst_plugin_feature_list_free (priv->decoders);
|
||||
gst_caps_unref (priv->raw_vcaps);
|
||||
gst_caps_unref (priv->raw_acaps);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_media_factory_uri_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_uri_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPMediaFactoryURI *factory = GST_RTSP_MEDIA_FACTORY_URI (object);
|
||||
GstRTSPMediaFactoryURIPrivate *priv = factory->priv;
|
||||
|
||||
switch (propid) {
|
||||
case PROP_URI:
|
||||
g_value_take_string (value, gst_rtsp_media_factory_uri_get_uri (factory));
|
||||
break;
|
||||
case PROP_USE_GSTPAY:
|
||||
g_value_set_boolean (value, priv->use_gstpay);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_media_factory_uri_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPMediaFactoryURI *factory = GST_RTSP_MEDIA_FACTORY_URI (object);
|
||||
GstRTSPMediaFactoryURIPrivate *priv = factory->priv;
|
||||
|
||||
switch (propid) {
|
||||
case PROP_URI:
|
||||
gst_rtsp_media_factory_uri_set_uri (factory, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_USE_GSTPAY:
|
||||
priv->use_gstpay = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_factory_uri_new:
|
||||
*
|
||||
* Create a new #GstRTSPMediaFactoryURI instance.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPMediaFactoryURI object.
|
||||
*/
|
||||
GstRTSPMediaFactoryURI *
|
||||
gst_rtsp_media_factory_uri_new (void)
|
||||
{
|
||||
GstRTSPMediaFactoryURI *result;
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_MEDIA_FACTORY_URI, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_factory_uri_set_uri:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
* @uri: the uri the stream
|
||||
*
|
||||
* Set the URI of the resource that will be streamed by this factory.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_media_factory_uri_set_uri (GstRTSPMediaFactoryURI * factory,
|
||||
const gchar * uri)
|
||||
{
|
||||
GstRTSPMediaFactoryURIPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY_URI (factory));
|
||||
g_return_if_fail (uri != NULL);
|
||||
|
||||
priv = factory->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_free (priv->uri);
|
||||
priv->uri = g_strdup (uri);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_factory_uri_get_uri:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Get the URI that will provide media for this factory.
|
||||
*
|
||||
* Returns: (transfer full): the configured URI. g_free() after usage.
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_media_factory_uri_get_uri (GstRTSPMediaFactoryURI * factory)
|
||||
{
|
||||
GstRTSPMediaFactoryURIPrivate *priv;
|
||||
gchar *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY_URI (factory), NULL);
|
||||
|
||||
priv = factory->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_strdup (priv->uri);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GstElementFactory *
|
||||
find_payloader (GstRTSPMediaFactoryURI * urifact, GstCaps * caps)
|
||||
{
|
||||
GstRTSPMediaFactoryURIPrivate *priv = urifact->priv;
|
||||
GList *list;
|
||||
GstElementFactory *factory = NULL;
|
||||
gboolean autoplug_more = FALSE;
|
||||
|
||||
/* first find a demuxer that can link */
|
||||
list = gst_element_factory_list_filter (priv->demuxers, caps,
|
||||
GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list) {
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
gboolean parsed = FALSE;
|
||||
gint mpegversion = 0;
|
||||
|
||||
if (!gst_structure_get_boolean (structure, "parsed", &parsed) &&
|
||||
gst_structure_has_name (structure, "audio/mpeg") &&
|
||||
gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
|
||||
(mpegversion == 2 || mpegversion == 4)) {
|
||||
/* for AAC it's framed=true instead of parsed=true */
|
||||
gst_structure_get_boolean (structure, "framed", &parsed);
|
||||
}
|
||||
|
||||
/* Avoid plugging parsers in a loop. This is not 100% correct, as some
|
||||
* parsers don't set parsed=true in caps. We should do something like
|
||||
* decodebin does and track decode chains and elements plugged in those
|
||||
* chains...
|
||||
*/
|
||||
if (parsed) {
|
||||
GList *walk;
|
||||
const gchar *klass;
|
||||
|
||||
for (walk = list; walk; walk = walk->next) {
|
||||
factory = GST_ELEMENT_FACTORY (walk->data);
|
||||
klass = gst_element_factory_get_metadata (factory,
|
||||
GST_ELEMENT_METADATA_KLASS);
|
||||
if (strstr (klass, "Parser"))
|
||||
/* caps have parsed=true, so skip this parser to avoid loops */
|
||||
continue;
|
||||
|
||||
autoplug_more = TRUE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* caps don't have parsed=true set and we have a demuxer/parser */
|
||||
autoplug_more = TRUE;
|
||||
}
|
||||
|
||||
gst_plugin_feature_list_free (list);
|
||||
}
|
||||
|
||||
if (autoplug_more)
|
||||
/* we have a demuxer, try that one first */
|
||||
return NULL;
|
||||
|
||||
/* no demuxer try a depayloader */
|
||||
list = gst_element_factory_list_filter (priv->payloaders, caps,
|
||||
GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list == NULL) {
|
||||
if (priv->use_gstpay) {
|
||||
/* no depayloader or parser/demuxer, use gstpay when allowed */
|
||||
factory = gst_element_factory_find ("rtpgstpay");
|
||||
} else {
|
||||
/* no depayloader, try a decoder, we'll get to a payloader for a decoded
|
||||
* video or audio format, worst case. */
|
||||
list = gst_element_factory_list_filter (priv->decoders, caps,
|
||||
GST_PAD_SINK, FALSE);
|
||||
|
||||
if (list != NULL) {
|
||||
/* we have a decoder, try that one first */
|
||||
gst_plugin_feature_list_free (list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list != NULL) {
|
||||
factory = GST_ELEMENT_FACTORY_CAST (list->data);
|
||||
g_object_ref (factory);
|
||||
gst_plugin_feature_list_free (list);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
autoplug_continue_cb (GstElement * uribin, GstPad * pad, GstCaps * caps,
|
||||
GstElement * element)
|
||||
{
|
||||
FactoryData *data;
|
||||
GstElementFactory *factory;
|
||||
|
||||
GST_DEBUG ("found pad %s:%s of caps %" GST_PTR_FORMAT,
|
||||
GST_DEBUG_PAD_NAME (pad), caps);
|
||||
|
||||
data = g_object_get_data (G_OBJECT (element), factory_key);
|
||||
|
||||
if (!(factory = find_payloader (data->factory, caps)))
|
||||
goto no_factory;
|
||||
|
||||
/* we found a payloader, stop autoplugging so we can plug the
|
||||
* payloader. */
|
||||
GST_DEBUG ("found factory %s",
|
||||
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
||||
gst_object_unref (factory);
|
||||
|
||||
return FALSE;
|
||||
|
||||
/* ERRORS */
|
||||
no_factory:
|
||||
{
|
||||
/* no payloader, continue autoplugging */
|
||||
GST_DEBUG ("no payloader found");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * uribin, GstPad * pad, GstElement * element)
|
||||
{
|
||||
GstRTSPMediaFactoryURI *urifact;
|
||||
GstRTSPMediaFactoryURIPrivate *priv;
|
||||
FactoryData *data;
|
||||
GstElementFactory *factory;
|
||||
GstElement *payloader;
|
||||
GstCaps *caps;
|
||||
GstPad *sinkpad, *srcpad, *ghostpad;
|
||||
GstElement *convert;
|
||||
gchar *padname, *payloader_name;
|
||||
|
||||
GST_DEBUG ("added pad %s:%s", GST_DEBUG_PAD_NAME (pad));
|
||||
|
||||
/* link the element now and expose the pad */
|
||||
data = g_object_get_data (G_OBJECT (element), factory_key);
|
||||
urifact = data->factory;
|
||||
priv = urifact->priv;
|
||||
|
||||
/* ref to make refcounting easier later */
|
||||
gst_object_ref (pad);
|
||||
padname = gst_pad_get_name (pad);
|
||||
|
||||
/* get pad caps first, then call get_caps, then fail */
|
||||
if ((caps = gst_pad_get_current_caps (pad)) == NULL)
|
||||
if ((caps = gst_pad_query_caps (pad, NULL)) == NULL)
|
||||
goto no_caps;
|
||||
|
||||
/* check for raw caps */
|
||||
if (gst_caps_can_intersect (caps, priv->raw_vcaps)) {
|
||||
/* we have raw video caps, insert converter */
|
||||
convert = gst_element_factory_make ("videoconvert", NULL);
|
||||
} else if (gst_caps_can_intersect (caps, priv->raw_acaps)) {
|
||||
/* we have raw audio caps, insert converter */
|
||||
convert = gst_element_factory_make ("audioconvert", NULL);
|
||||
} else {
|
||||
convert = NULL;
|
||||
}
|
||||
|
||||
if (convert) {
|
||||
gst_bin_add (GST_BIN_CAST (element), convert);
|
||||
gst_element_set_state (convert, GST_STATE_PLAYING);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (convert, "sink");
|
||||
gst_pad_link (pad, sinkpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
/* unref old pad, we reffed before */
|
||||
gst_object_unref (pad);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
/* continue with new pad and caps */
|
||||
pad = gst_element_get_static_pad (convert, "src");
|
||||
if ((caps = gst_pad_get_current_caps (pad)) == NULL)
|
||||
if ((caps = gst_pad_query_caps (pad, NULL)) == NULL)
|
||||
goto no_caps;
|
||||
}
|
||||
|
||||
if (!(factory = find_payloader (urifact, caps)))
|
||||
goto no_factory;
|
||||
|
||||
gst_caps_unref (caps);
|
||||
|
||||
/* we have a payloader now */
|
||||
GST_DEBUG ("found payloader factory %s",
|
||||
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
|
||||
|
||||
payloader_name = g_strdup_printf ("pay_%s", padname);
|
||||
payloader = gst_element_factory_create (factory, payloader_name);
|
||||
g_free (payloader_name);
|
||||
if (payloader == NULL)
|
||||
goto no_payloader;
|
||||
|
||||
g_object_set (payloader, "pt", data->pt, NULL);
|
||||
data->pt++;
|
||||
|
||||
if (g_object_class_find_property (G_OBJECT_GET_CLASS (payloader),
|
||||
"buffer-list"))
|
||||
g_object_set (payloader, "buffer-list", TRUE, NULL);
|
||||
|
||||
/* add the payloader to the pipeline */
|
||||
gst_bin_add (GST_BIN_CAST (element), payloader);
|
||||
gst_element_set_state (payloader, GST_STATE_PLAYING);
|
||||
|
||||
/* link the pad to the sinkpad of the payloader */
|
||||
sinkpad = gst_element_get_static_pad (payloader, "sink");
|
||||
gst_pad_link (pad, sinkpad);
|
||||
gst_object_unref (sinkpad);
|
||||
gst_object_unref (pad);
|
||||
|
||||
/* now expose the srcpad of the payloader as a ghostpad with the same name
|
||||
* as the uridecodebin pad name. */
|
||||
srcpad = gst_element_get_static_pad (payloader, "src");
|
||||
ghostpad = gst_ghost_pad_new (padname, srcpad);
|
||||
gst_object_unref (srcpad);
|
||||
g_free (padname);
|
||||
|
||||
gst_pad_set_active (ghostpad, TRUE);
|
||||
gst_element_add_pad (element, ghostpad);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
no_caps:
|
||||
{
|
||||
GST_WARNING ("could not get caps from pad");
|
||||
g_free (padname);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
no_factory:
|
||||
{
|
||||
GST_DEBUG ("no payloader found");
|
||||
g_free (padname);
|
||||
gst_caps_unref (caps);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
no_payloader:
|
||||
{
|
||||
GST_ERROR ("could not create payloader from factory");
|
||||
g_free (padname);
|
||||
gst_caps_unref (caps);
|
||||
gst_object_unref (pad);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
no_more_pads_cb (GstElement * uribin, GstElement * element)
|
||||
{
|
||||
GST_DEBUG ("no-more-pads");
|
||||
gst_element_no_more_pads (element);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
rtsp_media_factory_uri_create_element (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPMediaFactoryURIPrivate *priv;
|
||||
GstElement *topbin, *element, *uribin;
|
||||
GstRTSPMediaFactoryURI *urifact;
|
||||
FactoryData *data;
|
||||
|
||||
urifact = GST_RTSP_MEDIA_FACTORY_URI_CAST (factory);
|
||||
priv = urifact->priv;
|
||||
|
||||
GST_LOG ("creating element");
|
||||
|
||||
topbin = gst_bin_new ("GstRTSPMediaFactoryURI");
|
||||
g_assert (topbin != NULL);
|
||||
|
||||
/* our bin will dynamically expose payloaded pads */
|
||||
element = gst_bin_new ("dynpay0");
|
||||
g_assert (element != NULL);
|
||||
|
||||
uribin = gst_element_factory_make ("uridecodebin", "uribin");
|
||||
if (uribin == NULL)
|
||||
goto no_uridecodebin;
|
||||
|
||||
g_object_set (uribin, "uri", priv->uri, NULL);
|
||||
|
||||
/* keep factory data around */
|
||||
data = g_new0 (FactoryData, 1);
|
||||
data->factory = g_object_ref (urifact);
|
||||
data->pt = 96;
|
||||
|
||||
g_object_set_data_full (G_OBJECT (element), factory_key,
|
||||
data, (GDestroyNotify) free_data);
|
||||
|
||||
/* connect to the signals */
|
||||
g_signal_connect (uribin, "autoplug-continue",
|
||||
(GCallback) autoplug_continue_cb, element);
|
||||
g_signal_connect (uribin, "pad-added", (GCallback) pad_added_cb, element);
|
||||
g_signal_connect (uribin, "no-more-pads", (GCallback) no_more_pads_cb,
|
||||
element);
|
||||
|
||||
gst_bin_add (GST_BIN_CAST (element), uribin);
|
||||
gst_bin_add (GST_BIN_CAST (topbin), element);
|
||||
|
||||
return topbin;
|
||||
|
||||
no_uridecodebin:
|
||||
{
|
||||
g_critical ("can't create uridecodebin element");
|
||||
gst_object_unref (element);
|
||||
return NULL;
|
||||
}
|
||||
}
|
91
gst/rtsp-server/rtsp-media-factory-uri.h
Normal file
91
gst/rtsp-server/rtsp-media-factory-uri.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "rtsp-media-factory.h"
|
||||
|
||||
#ifndef __GST_RTSP_MEDIA_FACTORY_URI_H__
|
||||
#define __GST_RTSP_MEDIA_FACTORY_URI_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* types for the media factory */
|
||||
#define GST_TYPE_RTSP_MEDIA_FACTORY_URI (gst_rtsp_media_factory_uri_get_type ())
|
||||
#define GST_IS_RTSP_MEDIA_FACTORY_URI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA_FACTORY_URI))
|
||||
#define GST_IS_RTSP_MEDIA_FACTORY_URI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA_FACTORY_URI))
|
||||
#define GST_RTSP_MEDIA_FACTORY_URI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA_FACTORY_URI, GstRTSPMediaFactoryURIClass))
|
||||
#define GST_RTSP_MEDIA_FACTORY_URI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA_FACTORY_URI, GstRTSPMediaFactoryURI))
|
||||
#define GST_RTSP_MEDIA_FACTORY_URI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA_FACTORY_URI, GstRTSPMediaFactoryURIClass))
|
||||
#define GST_RTSP_MEDIA_FACTORY_URI_CAST(obj) ((GstRTSPMediaFactoryURI*)(obj))
|
||||
#define GST_RTSP_MEDIA_FACTORY_URI_CLASS_CAST(klass) ((GstRTSPMediaFactoryURIClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPMediaFactoryURI GstRTSPMediaFactoryURI;
|
||||
typedef struct _GstRTSPMediaFactoryURIClass GstRTSPMediaFactoryURIClass;
|
||||
typedef struct _GstRTSPMediaFactoryURIPrivate GstRTSPMediaFactoryURIPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPMediaFactoryURI:
|
||||
*
|
||||
* A media factory that creates a pipeline to play any uri.
|
||||
*/
|
||||
struct _GstRTSPMediaFactoryURI {
|
||||
GstRTSPMediaFactory parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPMediaFactoryURIPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPMediaFactoryURIClass:
|
||||
*
|
||||
* The #GstRTSPMediaFactoryURI class structure.
|
||||
*/
|
||||
struct _GstRTSPMediaFactoryURIClass {
|
||||
GstRTSPMediaFactoryClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_media_factory_uri_get_type (void);
|
||||
|
||||
/* creating the factory */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMediaFactoryURI * gst_rtsp_media_factory_uri_new (void);
|
||||
|
||||
/* configuring the factory */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_uri_set_uri (GstRTSPMediaFactoryURI *factory,
|
||||
const gchar *uri);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_media_factory_uri_get_uri (GstRTSPMediaFactoryURI *factory);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMediaFactoryURI, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_MEDIA_FACTORY_URI_H__ */
|
2058
gst/rtsp-server/rtsp-media-factory.c
Normal file
2058
gst/rtsp-server/rtsp-media-factory.c
Normal file
File diff suppressed because it is too large
Load diff
284
gst/rtsp-server/rtsp-media-factory.h
Normal file
284
gst/rtsp-server/rtsp-media-factory.h
Normal file
|
@ -0,0 +1,284 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/gstrtspurl.h>
|
||||
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-permissions.h"
|
||||
#include "rtsp-address-pool.h"
|
||||
|
||||
#ifndef __GST_RTSP_MEDIA_FACTORY_H__
|
||||
#define __GST_RTSP_MEDIA_FACTORY_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* types for the media factory */
|
||||
#define GST_TYPE_RTSP_MEDIA_FACTORY (gst_rtsp_media_factory_get_type ())
|
||||
#define GST_IS_RTSP_MEDIA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA_FACTORY))
|
||||
#define GST_IS_RTSP_MEDIA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA_FACTORY))
|
||||
#define GST_RTSP_MEDIA_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA_FACTORY, GstRTSPMediaFactoryClass))
|
||||
#define GST_RTSP_MEDIA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA_FACTORY, GstRTSPMediaFactory))
|
||||
#define GST_RTSP_MEDIA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA_FACTORY, GstRTSPMediaFactoryClass))
|
||||
#define GST_RTSP_MEDIA_FACTORY_CAST(obj) ((GstRTSPMediaFactory*)(obj))
|
||||
#define GST_RTSP_MEDIA_FACTORY_CLASS_CAST(klass) ((GstRTSPMediaFactoryClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPMediaFactory GstRTSPMediaFactory;
|
||||
typedef struct _GstRTSPMediaFactoryClass GstRTSPMediaFactoryClass;
|
||||
typedef struct _GstRTSPMediaFactoryPrivate GstRTSPMediaFactoryPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPMediaFactory:
|
||||
*
|
||||
* The definition and logic for constructing the pipeline for a media. The media
|
||||
* can contain multiple streams like audio and video.
|
||||
*/
|
||||
struct _GstRTSPMediaFactory {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPMediaFactoryPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPMediaFactoryClass:
|
||||
* @gen_key: convert @url to a key for caching shared #GstRTSPMedia objects.
|
||||
* The default implementation of this function will use the complete URL
|
||||
* including the query parameters to return a key.
|
||||
* @create_element: Construct and return a #GstElement that is a #GstBin containing
|
||||
* the elements to use for streaming the media. The bin should contain
|
||||
* payloaders pay\%d for each stream. The default implementation of this
|
||||
* function returns the bin created from the launch parameter.
|
||||
* @construct: the vmethod that will be called when the factory has to create the
|
||||
* #GstRTSPMedia for @url. The default implementation of this
|
||||
* function calls create_element to retrieve an element and then looks for
|
||||
* pay\%d to create the streams.
|
||||
* @create_pipeline: create a new pipeline or re-use an existing one and
|
||||
* add the #GstRTSPMedia's element created by @construct to the pipeline.
|
||||
* @configure: configure the media created with @construct. The default
|
||||
* implementation will configure the 'shared' property of the media.
|
||||
* @media_constructed: signal emitted when a media was constructed
|
||||
* @media_configure: signal emitted when a media should be configured
|
||||
*
|
||||
* The #GstRTSPMediaFactory class structure.
|
||||
*/
|
||||
struct _GstRTSPMediaFactoryClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
gchar * (*gen_key) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
|
||||
|
||||
GstElement * (*create_element) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
|
||||
GstRTSPMedia * (*construct) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
|
||||
GstElement * (*create_pipeline) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
|
||||
void (*configure) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
|
||||
|
||||
/* signals */
|
||||
void (*media_constructed) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
|
||||
void (*media_configure) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_media_factory_get_type (void);
|
||||
|
||||
/* creating the factory */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMediaFactory * gst_rtsp_media_factory_new (void);
|
||||
|
||||
/* configuring the factory */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory *factory,
|
||||
const gchar *launch);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_permissions (GstRTSPMediaFactory *factory,
|
||||
GstRTSPPermissions *permissions);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPermissions * gst_rtsp_media_factory_get_permissions (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_add_role (GstRTSPMediaFactory *factory,
|
||||
const gchar *role,
|
||||
const gchar *fieldname, ...);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_add_role_from_structure (GstRTSPMediaFactory * factory,
|
||||
GstStructure *structure);
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_shared (GstRTSPMediaFactory *factory,
|
||||
gboolean shared);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_is_shared (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_stop_on_disconnect (GstRTSPMediaFactory *factory,
|
||||
gboolean stop_on_disconnect);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_is_stop_on_disonnect (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_suspend_mode (GstRTSPMediaFactory *factory,
|
||||
GstRTSPSuspendMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSuspendMode gst_rtsp_media_factory_get_suspend_mode (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_eos_shutdown (GstRTSPMediaFactory *factory,
|
||||
gboolean eos_shutdown);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_is_eos_shutdown (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_profiles (GstRTSPMediaFactory *factory,
|
||||
GstRTSPProfile profiles);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPProfile gst_rtsp_media_factory_get_profiles (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_protocols (GstRTSPMediaFactory *factory,
|
||||
GstRTSPLowerTrans protocols);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPLowerTrans gst_rtsp_media_factory_get_protocols (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_address_pool (GstRTSPMediaFactory * factory,
|
||||
GstRTSPAddressPool * pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddressPool * gst_rtsp_media_factory_get_address_pool (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_multicast_iface (GstRTSPMediaFactory *factory, const gchar *multicast_iface);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_media_factory_get_multicast_iface (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_buffer_size (GstRTSPMediaFactory * factory,
|
||||
guint size);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_factory_get_buffer_size (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_retransmission_time (GstRTSPMediaFactory * factory,
|
||||
GstClockTime time);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTime gst_rtsp_media_factory_get_retransmission_time (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_do_retransmission (GstRTSPMediaFactory * factory,
|
||||
gboolean do_retransmission);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_get_do_retransmission (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_latency (GstRTSPMediaFactory * factory,
|
||||
guint latency);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_factory_get_latency (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_transport_mode (GstRTSPMediaFactory *factory,
|
||||
GstRTSPTransportMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPTransportMode gst_rtsp_media_factory_get_transport_mode (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_media_gtype (GstRTSPMediaFactory * factory,
|
||||
GType media_gtype);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_media_factory_get_media_gtype (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_clock (GstRTSPMediaFactory *factory,
|
||||
GstClock * clock);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClock * gst_rtsp_media_factory_get_clock (GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_publish_clock_mode (GstRTSPMediaFactory * factory, GstRTSPPublishClockMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPublishClockMode gst_rtsp_media_factory_get_publish_clock_mode (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_set_max_mcast_ttl (GstRTSPMediaFactory * factory,
|
||||
guint ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_factory_get_max_mcast_ttl (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_bind_mcast_address (GstRTSPMediaFactory * factory,
|
||||
gboolean bind_mcast_addr);
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_is_bind_mcast_address (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_dscp_qos (GstRTSPMediaFactory * factory,
|
||||
gint dscp_qos);
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_media_factory_get_dscp_qos (GstRTSPMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_factory_set_enable_rtcp (GstRTSPMediaFactory * factory,
|
||||
gboolean enable);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_factory_is_enable_rtcp (GstRTSPMediaFactory * factory);
|
||||
|
||||
/* creating the media from the factory and a url */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMedia * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory,
|
||||
const GstRTSPUrl *url);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_media_factory_create_element (GstRTSPMediaFactory *factory,
|
||||
const GstRTSPUrl *url);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMediaFactory, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_MEDIA_FACTORY_H__ */
|
5195
gst/rtsp-server/rtsp-media.c
Normal file
5195
gst/rtsp-server/rtsp-media.c
Normal file
File diff suppressed because it is too large
Load diff
449
gst/rtsp-server/rtsp-media.h
Normal file
449
gst/rtsp-server/rtsp-media.h
Normal file
|
@ -0,0 +1,449 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/rtsp.h>
|
||||
#include <gst/net/gstnet.h>
|
||||
|
||||
#ifndef __GST_RTSP_MEDIA_H__
|
||||
#define __GST_RTSP_MEDIA_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* types for the media */
|
||||
#define GST_TYPE_RTSP_MEDIA (gst_rtsp_media_get_type ())
|
||||
#define GST_IS_RTSP_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA))
|
||||
#define GST_IS_RTSP_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA))
|
||||
#define GST_RTSP_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA, GstRTSPMediaClass))
|
||||
#define GST_RTSP_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA, GstRTSPMedia))
|
||||
#define GST_RTSP_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA, GstRTSPMediaClass))
|
||||
#define GST_RTSP_MEDIA_CAST(obj) ((GstRTSPMedia*)(obj))
|
||||
#define GST_RTSP_MEDIA_CLASS_CAST(klass) ((GstRTSPMediaClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPMedia GstRTSPMedia;
|
||||
typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
|
||||
typedef struct _GstRTSPMediaPrivate GstRTSPMediaPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPMediaStatus:
|
||||
* @GST_RTSP_MEDIA_STATUS_UNPREPARED: media pipeline not prerolled
|
||||
* @GST_RTSP_MEDIA_STATUS_UNPREPARING: media pipeline is busy doing a clean
|
||||
* shutdown.
|
||||
* @GST_RTSP_MEDIA_STATUS_PREPARING: media pipeline is prerolling
|
||||
* @GST_RTSP_MEDIA_STATUS_PREPARED: media pipeline is prerolled
|
||||
* @GST_RTSP_MEDIA_STATUS_SUSPENDED: media is suspended
|
||||
* @GST_RTSP_MEDIA_STATUS_ERROR: media pipeline is in error
|
||||
*
|
||||
* The state of the media pipeline.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_MEDIA_STATUS_UNPREPARED = 0,
|
||||
GST_RTSP_MEDIA_STATUS_UNPREPARING = 1,
|
||||
GST_RTSP_MEDIA_STATUS_PREPARING = 2,
|
||||
GST_RTSP_MEDIA_STATUS_PREPARED = 3,
|
||||
GST_RTSP_MEDIA_STATUS_SUSPENDED = 4,
|
||||
GST_RTSP_MEDIA_STATUS_ERROR = 5
|
||||
} GstRTSPMediaStatus;
|
||||
|
||||
/**
|
||||
* GstRTSPSuspendMode:
|
||||
* @GST_RTSP_SUSPEND_MODE_NONE: Media is not suspended
|
||||
* @GST_RTSP_SUSPEND_MODE_PAUSE: Media is PAUSED in suspend
|
||||
* @GST_RTSP_SUSPEND_MODE_RESET: The media is set to NULL when suspended
|
||||
*
|
||||
* The suspend mode of the media pipeline. A media pipeline is suspended right
|
||||
* after creating the SDP and when the client performs a PAUSED request.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_SUSPEND_MODE_NONE = 0,
|
||||
GST_RTSP_SUSPEND_MODE_PAUSE = 1,
|
||||
GST_RTSP_SUSPEND_MODE_RESET = 2
|
||||
} GstRTSPSuspendMode;
|
||||
|
||||
/**
|
||||
* GstRTSPTransportMode:
|
||||
* @GST_RTSP_TRANSPORT_MODE_PLAY: Transport supports PLAY mode
|
||||
* @GST_RTSP_TRANSPORT_MODE_RECORD: Transport supports RECORD mode
|
||||
*
|
||||
* The supported modes of the media.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_TRANSPORT_MODE_PLAY = 1,
|
||||
GST_RTSP_TRANSPORT_MODE_RECORD = 2,
|
||||
} GstRTSPTransportMode;
|
||||
|
||||
/**
|
||||
* GstRTSPPublishClockMode:
|
||||
* @GST_RTSP_PUBLISH_CLOCK_MODE_NONE: Publish nothing
|
||||
* @GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK: Publish the clock but not the offset
|
||||
* @GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET: Publish the clock and offset
|
||||
*
|
||||
* Whether the clock and possibly RTP/clock offset should be published according to RFC7273.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_RTSP_PUBLISH_CLOCK_MODE_NONE,
|
||||
GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK,
|
||||
GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET
|
||||
} GstRTSPPublishClockMode;
|
||||
|
||||
#define GST_TYPE_RTSP_TRANSPORT_MODE (gst_rtsp_transport_mode_get_type())
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_transport_mode_get_type (void);
|
||||
|
||||
#define GST_TYPE_RTSP_SUSPEND_MODE (gst_rtsp_suspend_mode_get_type())
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_suspend_mode_get_type (void);
|
||||
|
||||
#define GST_TYPE_RTSP_PUBLISH_CLOCK_MODE (gst_rtsp_publish_clock_mode_get_type())
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_publish_clock_mode_get_type (void);
|
||||
|
||||
#include "rtsp-stream.h"
|
||||
#include "rtsp-thread-pool.h"
|
||||
#include "rtsp-permissions.h"
|
||||
#include "rtsp-address-pool.h"
|
||||
#include "rtsp-sdp.h"
|
||||
|
||||
/**
|
||||
* GstRTSPMedia:
|
||||
*
|
||||
* A class that contains the GStreamer element along with a list of
|
||||
* #GstRTSPStream objects that can produce data.
|
||||
*
|
||||
* This object is usually created from a #GstRTSPMediaFactory.
|
||||
*/
|
||||
struct _GstRTSPMedia {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPMediaPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPMediaClass:
|
||||
* @handle_message: handle a message
|
||||
* @prepare: the default implementation adds all elements and sets the
|
||||
* pipeline's state to GST_STATE_PAUSED (or GST_STATE_PLAYING
|
||||
* in case of NO_PREROLL elements).
|
||||
* @unprepare: the default implementation sets the pipeline's state
|
||||
* to GST_STATE_NULL and removes all elements.
|
||||
* @suspend: the default implementation sets the pipeline's state to
|
||||
* GST_STATE_NULL GST_STATE_PAUSED depending on the selected
|
||||
* suspend mode.
|
||||
* @unsuspend: the default implementation reverts the suspend operation.
|
||||
* The pipeline will be prerolled again if it's state was
|
||||
* set to GST_STATE_NULL in suspend.
|
||||
* @convert_range: convert a range to the given unit
|
||||
* @query_position: query the current position in the pipeline
|
||||
* @query_stop: query when playback will stop
|
||||
*
|
||||
* The RTSP media class
|
||||
*/
|
||||
struct _GstRTSPMediaClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* vmethods */
|
||||
gboolean (*handle_message) (GstRTSPMedia *media, GstMessage *message);
|
||||
gboolean (*prepare) (GstRTSPMedia *media, GstRTSPThread *thread);
|
||||
gboolean (*unprepare) (GstRTSPMedia *media);
|
||||
gboolean (*suspend) (GstRTSPMedia *media);
|
||||
gboolean (*unsuspend) (GstRTSPMedia *media);
|
||||
gboolean (*convert_range) (GstRTSPMedia *media, GstRTSPTimeRange *range,
|
||||
GstRTSPRangeUnit unit);
|
||||
gboolean (*query_position) (GstRTSPMedia *media, gint64 *position);
|
||||
gboolean (*query_stop) (GstRTSPMedia *media, gint64 *stop);
|
||||
GstElement * (*create_rtpbin) (GstRTSPMedia *media);
|
||||
gboolean (*setup_rtpbin) (GstRTSPMedia *media, GstElement *rtpbin);
|
||||
gboolean (*setup_sdp) (GstRTSPMedia *media, GstSDPMessage *sdp, GstSDPInfo *info);
|
||||
|
||||
/* signals */
|
||||
void (*new_stream) (GstRTSPMedia *media, GstRTSPStream * stream);
|
||||
void (*removed_stream) (GstRTSPMedia *media, GstRTSPStream * stream);
|
||||
|
||||
void (*prepared) (GstRTSPMedia *media);
|
||||
void (*unprepared) (GstRTSPMedia *media);
|
||||
|
||||
void (*target_state) (GstRTSPMedia *media, GstState state);
|
||||
void (*new_state) (GstRTSPMedia *media, GstState state);
|
||||
|
||||
gboolean (*handle_sdp) (GstRTSPMedia *media, GstSDPMessage *sdp);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-1];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_media_get_type (void);
|
||||
|
||||
/* creating the media */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMedia * gst_rtsp_media_new (GstElement *element);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_media_get_element (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_take_pipeline (GstRTSPMedia *media, GstPipeline *pipeline);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMediaStatus gst_rtsp_media_get_status (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_permissions (GstRTSPMedia *media,
|
||||
GstRTSPPermissions *permissions);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPermissions * gst_rtsp_media_get_permissions (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_shared (GstRTSPMedia *media, gboolean shared);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_shared (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_stop_on_disconnect (GstRTSPMedia *media, gboolean stop_on_disconnect);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_stop_on_disconnect (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_transport_mode (GstRTSPMedia *media, GstRTSPTransportMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPTransportMode gst_rtsp_media_get_transport_mode (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_reusable (GstRTSPMedia *media, gboolean reusable);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_reusable (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_profiles (GstRTSPMedia *media, GstRTSPProfile profiles);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPProfile gst_rtsp_media_get_profiles (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_protocols (GstRTSPMedia *media, GstRTSPLowerTrans protocols);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPLowerTrans gst_rtsp_media_get_protocols (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_eos_shutdown (GstRTSPMedia *media, gboolean eos_shutdown);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_eos_shutdown (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_address_pool (GstRTSPMedia *media, GstRTSPAddressPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddressPool * gst_rtsp_media_get_address_pool (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_multicast_iface (GstRTSPMedia *media, const gchar *multicast_iface);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_media_get_multicast_iface (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_buffer_size (GstRTSPMedia *media, guint size);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_get_buffer_size (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_retransmission_time (GstRTSPMedia *media, GstClockTime time);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTime gst_rtsp_media_get_retransmission_time (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_do_retransmission (GstRTSPMedia * media,
|
||||
gboolean do_retransmission);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_get_do_retransmission (GstRTSPMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_latency (GstRTSPMedia *media, guint latency);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_get_latency (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_use_time_provider (GstRTSPMedia *media, gboolean time_provider);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_time_provider (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstNetTimeProvider * gst_rtsp_media_get_time_provider (GstRTSPMedia *media,
|
||||
const gchar *address, guint16 port);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_clock (GstRTSPMedia *media, GstClock * clock);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_publish_clock_mode (GstRTSPMedia * media, GstRTSPPublishClockMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPublishClockMode gst_rtsp_media_get_publish_clock_mode (GstRTSPMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_set_max_mcast_ttl (GstRTSPMedia *media, guint ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_get_max_mcast_ttl (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_bind_mcast_address (GstRTSPMedia *media, gboolean bind_mcast_addr);
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_bind_mcast_address (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_dscp_qos (GstRTSPMedia * media, gint dscp_qos);
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_media_get_dscp_qos (GstRTSPMedia * media);
|
||||
|
||||
/* prepare the media for playback */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_prepare (GstRTSPMedia *media, GstRTSPThread *thread);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_unprepare (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_suspend_mode (GstRTSPMedia *media, GstRTSPSuspendMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSuspendMode gst_rtsp_media_get_suspend_mode (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_suspend (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_unsuspend (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
|
||||
GstSDPInfo * info);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
|
||||
|
||||
/* creating streams */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_collect_streams (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStream * gst_rtsp_media_create_stream (GstRTSPMedia *media,
|
||||
GstElement *payloader,
|
||||
GstPad *pad);
|
||||
|
||||
/* dealing with the media */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_lock (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_unlock (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClock * gst_rtsp_media_get_clock (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTime gst_rtsp_media_get_base_time (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_media_n_streams (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStream * gst_rtsp_media_find_stream (GstRTSPMedia *media, const gchar * control);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_seek (GstRTSPMedia *media, GstRTSPTimeRange *range);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_seek_full (GstRTSPMedia *media,
|
||||
GstRTSPTimeRange *range,
|
||||
GstSeekFlags flags);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_seek_trickmode (GstRTSPMedia *media,
|
||||
GstRTSPTimeRange *range,
|
||||
GstSeekFlags flags,
|
||||
gdouble rate,
|
||||
GstClockTime trickmode_interval);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTimeDiff gst_rtsp_media_seekable (GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_media_get_range_string (GstRTSPMedia *media,
|
||||
gboolean play,
|
||||
GstRTSPRangeUnit unit);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_get_rates (GstRTSPMedia * media,
|
||||
gdouble * rate,
|
||||
gdouble * applied_rate);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_set_state (GstRTSPMedia *media, GstState state,
|
||||
GPtrArray *transports);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_pipeline_state (GstRTSPMedia * media,
|
||||
GstState state);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_complete_pipeline (GstRTSPMedia * media, GPtrArray * transports);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_is_receive_only (GstRTSPMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_has_completed_sender (GstRTSPMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_media_set_rate_control (GstRTSPMedia * media, gboolean enabled);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_media_get_rate_control (GstRTSPMedia * media);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMedia, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_MEDIA_H__ */
|
392
gst/rtsp-server/rtsp-mount-points.c
Normal file
392
gst/rtsp-server/rtsp-mount-points.c
Normal file
|
@ -0,0 +1,392 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-mount-points
|
||||
* @short_description: Map a path to media
|
||||
* @see_also: #GstRTSPMediaFactory, #GstRTSPClient
|
||||
*
|
||||
* A #GstRTSPMountPoints object maintains a relation between paths
|
||||
* and #GstRTSPMediaFactory objects. This object is usually given to
|
||||
* #GstRTSPClient and used to find the media attached to a path.
|
||||
*
|
||||
* With gst_rtsp_mount_points_add_factory () and
|
||||
* gst_rtsp_mount_points_remove_factory(), factories can be added and
|
||||
* removed.
|
||||
*
|
||||
* With gst_rtsp_mount_points_match() you can find the #GstRTSPMediaFactory
|
||||
* object that completely matches the given path.
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-mount-points.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *path;
|
||||
gint len;
|
||||
GstRTSPMediaFactory *factory;
|
||||
} DataItem;
|
||||
|
||||
static DataItem *
|
||||
data_item_new (gchar * path, gint len, GstRTSPMediaFactory * factory)
|
||||
{
|
||||
DataItem *item;
|
||||
|
||||
item = g_slice_alloc (sizeof (DataItem));
|
||||
item->path = path;
|
||||
item->len = len;
|
||||
item->factory = factory;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void
|
||||
data_item_free (gpointer data)
|
||||
{
|
||||
DataItem *item = data;
|
||||
|
||||
g_free (item->path);
|
||||
g_object_unref (item->factory);
|
||||
g_slice_free1 (sizeof (DataItem), item);
|
||||
}
|
||||
|
||||
static void
|
||||
data_item_dump (gconstpointer a, gconstpointer prefix)
|
||||
{
|
||||
const DataItem *item = a;
|
||||
|
||||
GST_DEBUG ("%s%s %p", (gchar *) prefix, item->path, item->factory);
|
||||
}
|
||||
|
||||
static gint
|
||||
data_item_compare (gconstpointer a, gconstpointer b, gpointer user_data)
|
||||
{
|
||||
const DataItem *item1 = a, *item2 = b;
|
||||
gint res;
|
||||
|
||||
res = g_strcmp0 (item1->path, item2->path);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct _GstRTSPMountPointsPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
GSequence *mounts; /* protected by lock */
|
||||
gboolean dirty;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPMountPoints, gst_rtsp_mount_points,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_media_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_media_debug
|
||||
|
||||
static gchar *default_make_path (GstRTSPMountPoints * mounts,
|
||||
const GstRTSPUrl * url);
|
||||
static void gst_rtsp_mount_points_finalize (GObject * obj);
|
||||
|
||||
static void
|
||||
gst_rtsp_mount_points_class_init (GstRTSPMountPointsClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_rtsp_mount_points_finalize;
|
||||
|
||||
klass->make_path = default_make_path;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmountpoints", 0,
|
||||
"GstRTSPMountPoints");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_mount_points_init (GstRTSPMountPoints * mounts)
|
||||
{
|
||||
GstRTSPMountPointsPrivate *priv;
|
||||
|
||||
GST_DEBUG_OBJECT (mounts, "created");
|
||||
|
||||
mounts->priv = priv = gst_rtsp_mount_points_get_instance_private (mounts);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
priv->mounts = g_sequence_new (data_item_free);
|
||||
priv->dirty = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_mount_points_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPMountPoints *mounts = GST_RTSP_MOUNT_POINTS (obj);
|
||||
GstRTSPMountPointsPrivate *priv = mounts->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (mounts, "finalized");
|
||||
|
||||
g_sequence_free (priv->mounts);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_mount_points_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_mount_points_new:
|
||||
*
|
||||
* Make a new mount points object.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPMountPoints
|
||||
*/
|
||||
GstRTSPMountPoints *
|
||||
gst_rtsp_mount_points_new (void)
|
||||
{
|
||||
GstRTSPMountPoints *result;
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_MOUNT_POINTS, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
default_make_path (GstRTSPMountPoints * mounts, const GstRTSPUrl * url)
|
||||
{
|
||||
/* normalize rtsp://<IP>:<PORT> to rtsp://<IP>:<PORT>/ */
|
||||
return g_strdup (url->abspath[0] ? url->abspath : "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_mount_points_make_path:
|
||||
* @mounts: a #GstRTSPMountPoints
|
||||
* @url: a #GstRTSPUrl
|
||||
*
|
||||
* Make a path string from @url.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a path string for @url, g_free() after usage.
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_mount_points_make_path (GstRTSPMountPoints * mounts,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPMountPointsClass *klass;
|
||||
gchar *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL);
|
||||
g_return_val_if_fail (url != NULL, NULL);
|
||||
|
||||
klass = GST_RTSP_MOUNT_POINTS_GET_CLASS (mounts);
|
||||
|
||||
if (klass->make_path)
|
||||
result = klass->make_path (mounts, url);
|
||||
else
|
||||
result = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
has_prefix (DataItem * str, DataItem * prefix)
|
||||
{
|
||||
/* prefix needs to be smaller than str */
|
||||
if (str->len < prefix->len)
|
||||
return FALSE;
|
||||
|
||||
/* special case when "/" is the entire prefix */
|
||||
if (prefix->len == 1 && prefix->path[0] == '/' && str->path[0] == '/')
|
||||
return TRUE;
|
||||
|
||||
/* if str is larger, it there should be a / following the prefix */
|
||||
if (str->len > prefix->len && str->path[prefix->len] != '/')
|
||||
return FALSE;
|
||||
|
||||
return strncmp (str->path, prefix->path, prefix->len) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_mount_points_match:
|
||||
* @mounts: a #GstRTSPMountPoints
|
||||
* @path: a mount point
|
||||
* @matched: (out) (allow-none): the amount of @path matched
|
||||
*
|
||||
* Find the factory in @mounts that has the longest match with @path.
|
||||
*
|
||||
* If @matched is %NULL, @path will match the factory exactly otherwise
|
||||
* the amount of characters that matched is returned in @matched.
|
||||
*
|
||||
* Returns: (transfer full): the #GstRTSPMediaFactory for @path.
|
||||
* g_object_unref() after usage.
|
||||
*/
|
||||
GstRTSPMediaFactory *
|
||||
gst_rtsp_mount_points_match (GstRTSPMountPoints * mounts,
|
||||
const gchar * path, gint * matched)
|
||||
{
|
||||
GstRTSPMountPointsPrivate *priv;
|
||||
GstRTSPMediaFactory *result = NULL;
|
||||
GSequenceIter *iter, *best;
|
||||
DataItem item, *ritem;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
priv = mounts->priv;
|
||||
|
||||
item.path = (gchar *) path;
|
||||
item.len = strlen (path);
|
||||
|
||||
GST_LOG ("Looking for mount point path %s", path);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
if (priv->dirty) {
|
||||
g_sequence_sort (priv->mounts, data_item_compare, mounts);
|
||||
g_sequence_foreach (priv->mounts, (GFunc) data_item_dump,
|
||||
(gpointer) "sort :");
|
||||
priv->dirty = FALSE;
|
||||
}
|
||||
|
||||
/* find the location of the media in the hashtable we only use the absolute
|
||||
* path of the uri to find a media factory. If the factory depends on other
|
||||
* properties found in the url, this method should be overridden. */
|
||||
iter = g_sequence_get_begin_iter (priv->mounts);
|
||||
best = NULL;
|
||||
while (!g_sequence_iter_is_end (iter)) {
|
||||
ritem = g_sequence_get (iter);
|
||||
|
||||
data_item_dump (ritem, "inspect: ");
|
||||
|
||||
/* The sequence is sorted, so any prefix match is an improvement upon
|
||||
* the previous best match, as '/abc' will always be before '/abcd' */
|
||||
if (has_prefix (&item, ritem)) {
|
||||
if (best == NULL) {
|
||||
data_item_dump (ritem, "prefix: ");
|
||||
} else {
|
||||
data_item_dump (ritem, "new best: ");
|
||||
}
|
||||
best = iter;
|
||||
} else {
|
||||
/* if have a match and the current item doesn't prefix match the best we
|
||||
* found so far then we're moving away and can bail out of the loop */
|
||||
if (best != NULL && !has_prefix (ritem, g_sequence_get (best)))
|
||||
break;
|
||||
}
|
||||
|
||||
iter = g_sequence_iter_next (iter);
|
||||
}
|
||||
if (best) {
|
||||
ritem = g_sequence_get (best);
|
||||
data_item_dump (ritem, "result: ");
|
||||
if (matched || ritem->len == item.len) {
|
||||
result = g_object_ref (ritem->factory);
|
||||
if (matched)
|
||||
*matched = ritem->len;
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
GST_INFO ("found media factory %p for path %s", result, path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_mount_points_remove_factory_unlocked (GstRTSPMountPoints * mounts,
|
||||
const gchar * path)
|
||||
{
|
||||
GstRTSPMountPointsPrivate *priv = mounts->priv;
|
||||
DataItem item;
|
||||
GSequenceIter *iter;
|
||||
|
||||
item.path = (gchar *) path;
|
||||
|
||||
if (priv->dirty) {
|
||||
g_sequence_sort (priv->mounts, data_item_compare, mounts);
|
||||
priv->dirty = FALSE;
|
||||
}
|
||||
iter = g_sequence_lookup (priv->mounts, &item, data_item_compare, mounts);
|
||||
if (iter) {
|
||||
g_sequence_remove (iter);
|
||||
priv->dirty = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_mount_points_add_factory:
|
||||
* @mounts: a #GstRTSPMountPoints
|
||||
* @path: a mount point
|
||||
* @factory: (transfer full): a #GstRTSPMediaFactory
|
||||
*
|
||||
* Attach @factory to the mount point @path in @mounts.
|
||||
*
|
||||
* @path is either of the form (/node)+ or the root path '/'. (An empty path is
|
||||
* not allowed.) Any previous mount point will be freed.
|
||||
*
|
||||
* Ownership is taken of the reference on @factory so that @factory should not be
|
||||
* used after calling this function.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_mount_points_add_factory (GstRTSPMountPoints * mounts,
|
||||
const gchar * path, GstRTSPMediaFactory * factory)
|
||||
{
|
||||
GstRTSPMountPointsPrivate *priv;
|
||||
DataItem *item;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts));
|
||||
g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
|
||||
g_return_if_fail (path != NULL && path[0] == '/');
|
||||
|
||||
priv = mounts->priv;
|
||||
|
||||
item = data_item_new (g_strdup (path), strlen (path), factory);
|
||||
|
||||
GST_INFO ("adding media factory %p for path %s", factory, path);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
gst_rtsp_mount_points_remove_factory_unlocked (mounts, path);
|
||||
g_sequence_append (priv->mounts, item);
|
||||
priv->dirty = TRUE;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_mount_points_remove_factory:
|
||||
* @mounts: a #GstRTSPMountPoints
|
||||
* @path: a mount point
|
||||
*
|
||||
* Remove the #GstRTSPMediaFactory associated with @path in @mounts.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints * mounts,
|
||||
const gchar * path)
|
||||
{
|
||||
GstRTSPMountPointsPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_MOUNT_POINTS (mounts));
|
||||
g_return_if_fail (path != NULL && path[0] == '/');
|
||||
|
||||
priv = mounts->priv;
|
||||
|
||||
GST_INFO ("removing media factory for path %s", path);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
gst_rtsp_mount_points_remove_factory_unlocked (mounts, path);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
105
gst/rtsp-server/rtsp-mount-points.h
Normal file
105
gst/rtsp-server/rtsp-mount-points.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "rtsp-media-factory.h"
|
||||
|
||||
#ifndef __GST_RTSP_MOUNT_POINTS_H__
|
||||
#define __GST_RTSP_MOUNT_POINTS_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_MOUNT_POINTS (gst_rtsp_mount_points_get_type ())
|
||||
#define GST_IS_RTSP_MOUNT_POINTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MOUNT_POINTS))
|
||||
#define GST_IS_RTSP_MOUNT_POINTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MOUNT_POINTS))
|
||||
#define GST_RTSP_MOUNT_POINTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MOUNT_POINTS, GstRTSPMountPointsClass))
|
||||
#define GST_RTSP_MOUNT_POINTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MOUNT_POINTS, GstRTSPMountPoints))
|
||||
#define GST_RTSP_MOUNT_POINTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MOUNT_POINTS, GstRTSPMountPointsClass))
|
||||
#define GST_RTSP_MOUNT_POINTS_CAST(obj) ((GstRTSPMountPoints*)(obj))
|
||||
#define GST_RTSP_MOUNT_POINTS_CLASS_CAST(klass) ((GstRTSPMountPointsClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPMountPoints GstRTSPMountPoints;
|
||||
typedef struct _GstRTSPMountPointsClass GstRTSPMountPointsClass;
|
||||
typedef struct _GstRTSPMountPointsPrivate GstRTSPMountPointsPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPMountPoints:
|
||||
*
|
||||
* Creates a #GstRTSPMediaFactory object for a given url.
|
||||
*/
|
||||
struct _GstRTSPMountPoints {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPMountPointsPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPMountPointsClass:
|
||||
* @make_path: make a path from the given url.
|
||||
*
|
||||
* The class for the media mounts object.
|
||||
*/
|
||||
struct _GstRTSPMountPointsClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
gchar * (*make_path) (GstRTSPMountPoints *mounts,
|
||||
const GstRTSPUrl *url);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_mount_points_get_type (void);
|
||||
|
||||
/* creating a mount points */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMountPoints * gst_rtsp_mount_points_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_mount_points_make_path (GstRTSPMountPoints *mounts,
|
||||
const GstRTSPUrl * url);
|
||||
/* finding a media factory */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMediaFactory * gst_rtsp_mount_points_match (GstRTSPMountPoints *mounts,
|
||||
const gchar *path,
|
||||
gint * matched);
|
||||
/* managing media to a mount point */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_mount_points_add_factory (GstRTSPMountPoints *mounts,
|
||||
const gchar *path,
|
||||
GstRTSPMediaFactory *factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_mount_points_remove_factory (GstRTSPMountPoints *mounts,
|
||||
const gchar *path);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMountPoints, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_MOUNT_POINTS_H__ */
|
219
gst/rtsp-server/rtsp-onvif-client.c
Normal file
219
gst/rtsp-server/rtsp-onvif-client.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-onvif-client.h"
|
||||
#include "rtsp-onvif-server.h"
|
||||
#include "rtsp-onvif-media-factory.h"
|
||||
|
||||
G_DEFINE_TYPE (GstRTSPOnvifClient, gst_rtsp_onvif_client, GST_TYPE_RTSP_CLIENT);
|
||||
|
||||
static gchar *
|
||||
gst_rtsp_onvif_client_check_requirements (GstRTSPClient * client,
|
||||
GstRTSPContext * ctx, gchar ** requirements)
|
||||
{
|
||||
GstRTSPMountPoints *mount_points = NULL;
|
||||
GstRTSPMediaFactory *factory = NULL;
|
||||
gchar *path = NULL;
|
||||
gboolean has_backchannel = FALSE;
|
||||
gboolean has_replay = FALSE;
|
||||
GString *unsupported = g_string_new ("");
|
||||
|
||||
while (*requirements) {
|
||||
if (strcmp (*requirements, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT) == 0) {
|
||||
has_backchannel = TRUE;
|
||||
} else if (strcmp (*requirements, GST_RTSP_ONVIF_REPLAY_REQUIREMENT) == 0) {
|
||||
has_replay = TRUE;
|
||||
} else {
|
||||
if (unsupported->len)
|
||||
g_string_append (unsupported, ", ");
|
||||
g_string_append (unsupported, *requirements);
|
||||
}
|
||||
requirements++;
|
||||
}
|
||||
|
||||
if (unsupported->len)
|
||||
goto out;
|
||||
|
||||
mount_points = gst_rtsp_client_get_mount_points (client);
|
||||
if (!(path = gst_rtsp_mount_points_make_path (mount_points, ctx->uri)))
|
||||
goto out;
|
||||
|
||||
if (!(factory = gst_rtsp_mount_points_match (mount_points, path, NULL)))
|
||||
goto out;
|
||||
|
||||
if (has_backchannel && !GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory)) {
|
||||
if (unsupported->len)
|
||||
g_string_append (unsupported, ", ");
|
||||
g_string_append (unsupported, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT);
|
||||
} else if (has_backchannel) {
|
||||
GstRTSPOnvifMediaFactory *onvif_factory =
|
||||
GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
|
||||
|
||||
if (!gst_rtsp_onvif_media_factory_has_backchannel_support (onvif_factory)) {
|
||||
if (unsupported->len)
|
||||
g_string_append (unsupported, ", ");
|
||||
g_string_append (unsupported, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_replay && !GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory)) {
|
||||
if (unsupported->len)
|
||||
g_string_append (unsupported, ", ");
|
||||
g_string_append (unsupported, GST_RTSP_ONVIF_REPLAY_REQUIREMENT);
|
||||
} else if (has_replay) {
|
||||
GstRTSPOnvifMediaFactory *onvif_factory =
|
||||
GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
|
||||
|
||||
if (!gst_rtsp_onvif_media_factory_has_replay_support (onvif_factory)) {
|
||||
if (unsupported->len)
|
||||
g_string_append (unsupported, ", ");
|
||||
g_string_append (unsupported, GST_RTSP_ONVIF_REPLAY_REQUIREMENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
if (path)
|
||||
g_free (path);
|
||||
if (factory)
|
||||
g_object_unref (factory);
|
||||
if (mount_points)
|
||||
g_object_unref (mount_points);
|
||||
|
||||
return g_string_free (unsupported, FALSE);
|
||||
}
|
||||
|
||||
static GstRTSPStatusCode
|
||||
gst_rtsp_onvif_client_adjust_play_mode (GstRTSPClient * client,
|
||||
GstRTSPContext * ctx, GstRTSPTimeRange ** range, GstSeekFlags * flags,
|
||||
gdouble * rate, GstClockTime * trickmode_interval,
|
||||
gboolean * enable_rate_control)
|
||||
{
|
||||
GstRTSPStatusCode ret = GST_RTSP_STS_BAD_REQUEST;
|
||||
gchar **split = NULL;
|
||||
gchar *str;
|
||||
|
||||
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_FRAMES,
|
||||
&str, 0) == GST_RTSP_OK) {
|
||||
|
||||
split = g_strsplit (str, "/", 2);
|
||||
|
||||
if (!g_strcmp0 (split[0], "intra")) {
|
||||
if (split[1]) {
|
||||
guint64 interval;
|
||||
gchar *end;
|
||||
|
||||
interval = g_ascii_strtoull (split[1], &end, 10);
|
||||
|
||||
if (!end || *end != '\0') {
|
||||
GST_ERROR ("Unexpected interval value %s", split[1]);
|
||||
goto done;
|
||||
}
|
||||
|
||||
*trickmode_interval = interval * GST_MSECOND;
|
||||
}
|
||||
*flags |= GST_SEEK_FLAG_TRICKMODE_KEY_UNITS;
|
||||
} else if (!g_strcmp0 (split[0], "predicted")) {
|
||||
if (split[1]) {
|
||||
GST_ERROR ("Predicted frames mode does not allow an interval (%s)",
|
||||
str);
|
||||
goto done;
|
||||
}
|
||||
*flags |= GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED;
|
||||
} else {
|
||||
GST_ERROR ("Invalid frames mode (%s)", str);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RATE_CONTROL,
|
||||
&str, 0) == GST_RTSP_OK) {
|
||||
if (!g_strcmp0 (str, "no")) {
|
||||
*enable_rate_control = FALSE;
|
||||
} else if (!g_strcmp0 (str, "yes")) {
|
||||
*enable_rate_control = TRUE;
|
||||
} else {
|
||||
GST_ERROR ("Invalid rate control header: %s", str);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ret = GST_RTSP_STS_OK;
|
||||
|
||||
done:
|
||||
if (split)
|
||||
g_strfreev (split);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstRTSPStatusCode
|
||||
gst_rtsp_onvif_client_adjust_play_response (GstRTSPClient * client,
|
||||
GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPStatusCode ret = GST_RTSP_STS_OK;
|
||||
gchar *str;
|
||||
|
||||
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RATE_CONTROL,
|
||||
&str, 0) == GST_RTSP_OK) {
|
||||
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_RATE_CONTROL,
|
||||
gst_rtsp_media_get_rate_control (ctx->media) ? "yes" : "no");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_client_class_init (GstRTSPOnvifClientClass * klass)
|
||||
{
|
||||
GstRTSPClientClass *client_klass = (GstRTSPClientClass *) klass;
|
||||
|
||||
client_klass->check_requirements = gst_rtsp_onvif_client_check_requirements;
|
||||
client_klass->adjust_play_mode = gst_rtsp_onvif_client_adjust_play_mode;
|
||||
client_klass->adjust_play_response =
|
||||
gst_rtsp_onvif_client_adjust_play_response;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_client_init (GstRTSPOnvifClient * client)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_client_new:
|
||||
*
|
||||
* Create a new #GstRTSPOnvifClient instance.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPOnvifClient
|
||||
* Since: 1.18
|
||||
*/
|
||||
GstRTSPClient *
|
||||
gst_rtsp_onvif_client_new (void)
|
||||
{
|
||||
GstRTSPClient *result;
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_ONVIF_CLIENT, NULL);
|
||||
|
||||
return result;
|
||||
}
|
65
gst/rtsp-server/rtsp-onvif-client.h
Normal file
65
gst/rtsp-server/rtsp-onvif-client.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_ONVIF_CLIENT_H__
|
||||
#define __GST_RTSP_ONVIF_CLIENT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-client.h"
|
||||
|
||||
#define GST_TYPE_RTSP_ONVIF_CLIENT (gst_rtsp_onvif_client_get_type ())
|
||||
#define GST_IS_RTSP_ONVIF_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_CLIENT))
|
||||
#define GST_IS_RTSP_ONVIF_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_CLIENT))
|
||||
#define GST_RTSP_ONVIF_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClientClass))
|
||||
#define GST_RTSP_ONVIF_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClient))
|
||||
#define GST_RTSP_ONVIF_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_CLIENT, GstRTSPOnvifClientClass))
|
||||
#define GST_RTSP_ONVIF_CLIENT_CAST(obj) ((GstRTSPOnvifClient*)(obj))
|
||||
#define GST_RTSP_ONVIF_CLIENT_CLASS_CAST(klass) ((GstRTSPOnvifClientClass*)(klass))
|
||||
|
||||
typedef struct GstRTSPOnvifClientClass GstRTSPOnvifClientClass;
|
||||
typedef struct GstRTSPOnvifClient GstRTSPOnvifClient;
|
||||
|
||||
/**
|
||||
* GstRTSPOnvifClient:
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct GstRTSPOnvifClientClass
|
||||
{
|
||||
GstRTSPClientClass parent;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
struct GstRTSPOnvifClient
|
||||
{
|
||||
GstRTSPClient parent;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_onvif_client_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPClient * gst_rtsp_onvif_client_new (void);
|
||||
|
||||
#endif /* __GST_RTSP_ONVIF_CLIENT_H__ */
|
545
gst/rtsp-server/rtsp-onvif-media-factory.c
Normal file
545
gst/rtsp-server/rtsp-onvif-media-factory.c
Normal file
|
@ -0,0 +1,545 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:rtsp-onvif-media-factory
|
||||
* @short_description: A factory for ONVIF media pipelines
|
||||
* @see_also: #GstRTSPMediaFactory, #GstRTSPOnvifMedia
|
||||
*
|
||||
* The #GstRTSPOnvifMediaFactory is responsible for creating or recycling
|
||||
* #GstRTSPMedia objects based on the passed URL. Different to
|
||||
* #GstRTSPMediaFactory, this supports special ONVIF features and can create
|
||||
* #GstRTSPOnvifMedia in addition to normal #GstRTSPMedia.
|
||||
*
|
||||
* Special ONVIF features that are currently supported is a backchannel for
|
||||
* the client to send back media to the server in a normal PLAY media, see
|
||||
* gst_rtsp_onvif_media_factory_set_backchannel_launch() and
|
||||
* gst_rtsp_onvif_media_factory_set_backchannel_bandwidth().
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-onvif-media-factory.h"
|
||||
#include "rtsp-onvif-media.h"
|
||||
#include "rtsp-onvif-server.h"
|
||||
|
||||
struct GstRTSPOnvifMediaFactoryPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
gchar *backchannel_launch;
|
||||
guint backchannel_bandwidth;
|
||||
gboolean has_replay_support;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPOnvifMediaFactory,
|
||||
gst_rtsp_onvif_media_factory, GST_TYPE_RTSP_MEDIA_FACTORY);
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_requires_backchannel:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Checks whether the client request requires backchannel.
|
||||
*
|
||||
* Returns: %TRUE if the client request requires backchannel.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_onvif_media_factory_requires_backchannel (GstRTSPMediaFactory *
|
||||
factory, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPMessage *msg = ctx->request;
|
||||
GstRTSPResult res;
|
||||
gint i;
|
||||
gchar *reqs = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), FALSE);
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
res = gst_rtsp_message_get_header (msg, GST_RTSP_HDR_REQUIRE, &reqs, i++);
|
||||
|
||||
if (res == GST_RTSP_ENOTIMPL)
|
||||
break;
|
||||
|
||||
if (strcmp (reqs, GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT) == 0)
|
||||
return TRUE;
|
||||
} while (TRUE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
gst_rtsp_onvif_media_factory_gen_key (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPContext *ctx = gst_rtsp_context_get_current ();
|
||||
|
||||
/* Only medias where no backchannel was requested can be shared */
|
||||
if (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx))
|
||||
return NULL;
|
||||
|
||||
return
|
||||
GST_RTSP_MEDIA_FACTORY_CLASS
|
||||
(gst_rtsp_onvif_media_factory_parent_class)->gen_key (factory, url);
|
||||
}
|
||||
|
||||
static GstRTSPMedia *
|
||||
gst_rtsp_onvif_media_factory_construct (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPMedia *media;
|
||||
GstElement *element, *pipeline;
|
||||
GstRTSPMediaFactoryClass *klass;
|
||||
GType media_gtype;
|
||||
gboolean got_backchannel_stream;
|
||||
GstRTSPContext *ctx = gst_rtsp_context_get_current ();
|
||||
|
||||
/* Mostly a copy of the default implementation but with backchannel support below,
|
||||
* unfortunately we can't re-use the default one because of how the virtual
|
||||
* method is define */
|
||||
|
||||
/* Everything but play is unsupported */
|
||||
if (gst_rtsp_media_factory_get_transport_mode (factory) !=
|
||||
GST_RTSP_TRANSPORT_MODE_PLAY)
|
||||
return NULL;
|
||||
|
||||
/* we only support onvif media here: otherwise a plain GstRTSPMediaFactory
|
||||
* could've been used as well */
|
||||
media_gtype = gst_rtsp_media_factory_get_media_gtype (factory);
|
||||
if (!g_type_is_a (media_gtype, GST_TYPE_RTSP_ONVIF_MEDIA))
|
||||
return NULL;
|
||||
|
||||
klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
|
||||
|
||||
if (!klass->create_pipeline)
|
||||
goto no_create;
|
||||
|
||||
element = gst_rtsp_media_factory_create_element (factory, url);
|
||||
if (element == NULL)
|
||||
goto no_element;
|
||||
|
||||
/* create a new empty media */
|
||||
media =
|
||||
g_object_new (media_gtype, "element", element,
|
||||
"transport-mode", GST_RTSP_TRANSPORT_MODE_PLAY, NULL);
|
||||
|
||||
/* this adds the non-backchannel streams */
|
||||
gst_rtsp_media_collect_streams (media);
|
||||
|
||||
/* this adds the backchannel stream */
|
||||
got_backchannel_stream =
|
||||
gst_rtsp_onvif_media_collect_backchannel (GST_RTSP_ONVIF_MEDIA (media));
|
||||
/* FIXME: This should not happen! We checked for that before */
|
||||
if (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx) &&
|
||||
!got_backchannel_stream) {
|
||||
g_object_unref (media);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pipeline = klass->create_pipeline (factory, media);
|
||||
if (pipeline == NULL)
|
||||
goto no_pipeline;
|
||||
|
||||
gst_rtsp_onvif_media_set_backchannel_bandwidth (GST_RTSP_ONVIF_MEDIA (media),
|
||||
GST_RTSP_ONVIF_MEDIA_FACTORY (factory)->priv->backchannel_bandwidth);
|
||||
|
||||
return media;
|
||||
|
||||
/* ERRORS */
|
||||
no_create:
|
||||
{
|
||||
g_critical ("no create_pipeline function");
|
||||
return NULL;
|
||||
}
|
||||
no_element:
|
||||
{
|
||||
g_critical ("could not create element");
|
||||
return NULL;
|
||||
}
|
||||
no_pipeline:
|
||||
{
|
||||
g_critical ("can't create pipeline");
|
||||
g_object_unref (media);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_rtsp_onvif_media_factory_create_element (GstRTSPMediaFactory * factory,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstElement *element;
|
||||
GError *error = NULL;
|
||||
gchar *launch;
|
||||
GstRTSPContext *ctx = gst_rtsp_context_get_current ();
|
||||
|
||||
/* Mostly a copy of the default implementation but with backchannel support below,
|
||||
* unfortunately we can't re-use the default one because of how the virtual
|
||||
* method is define */
|
||||
|
||||
launch = gst_rtsp_media_factory_get_launch (factory);
|
||||
|
||||
/* we need a parse syntax */
|
||||
if (launch == NULL)
|
||||
goto no_launch;
|
||||
|
||||
/* parse the user provided launch line */
|
||||
element =
|
||||
gst_parse_launch_full (launch, NULL, GST_PARSE_FLAG_PLACE_IN_BIN, &error);
|
||||
if (element == NULL)
|
||||
goto parse_error;
|
||||
|
||||
g_free (launch);
|
||||
|
||||
if (error != NULL) {
|
||||
/* a recoverable error was encountered */
|
||||
GST_WARNING ("recoverable parsing error: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
/* add backchannel pipeline part, if requested */
|
||||
if (gst_rtsp_onvif_media_factory_requires_backchannel (factory, ctx)) {
|
||||
GstRTSPOnvifMediaFactory *onvif_factory =
|
||||
GST_RTSP_ONVIF_MEDIA_FACTORY (factory);
|
||||
GstElement *backchannel_bin;
|
||||
GstElement *backchannel_depay;
|
||||
GstPad *depay_pad, *depay_ghostpad;
|
||||
|
||||
launch =
|
||||
gst_rtsp_onvif_media_factory_get_backchannel_launch (onvif_factory);
|
||||
if (launch == NULL)
|
||||
goto no_launch_backchannel;
|
||||
|
||||
backchannel_bin =
|
||||
gst_parse_bin_from_description_full (launch, FALSE, NULL,
|
||||
GST_PARSE_FLAG_PLACE_IN_BIN, &error);
|
||||
if (backchannel_bin == NULL)
|
||||
goto parse_error_backchannel;
|
||||
|
||||
g_free (launch);
|
||||
|
||||
if (error != NULL) {
|
||||
/* a recoverable error was encountered */
|
||||
GST_WARNING ("recoverable parsing error: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
gst_object_set_name (GST_OBJECT (backchannel_bin), "onvif-backchannel");
|
||||
|
||||
backchannel_depay =
|
||||
gst_bin_get_by_name (GST_BIN (backchannel_bin), "depay_backchannel");
|
||||
if (!backchannel_depay) {
|
||||
gst_object_unref (backchannel_bin);
|
||||
goto wrongly_formatted_backchannel_bin;
|
||||
}
|
||||
|
||||
depay_pad = gst_element_get_static_pad (backchannel_depay, "sink");
|
||||
if (!depay_pad) {
|
||||
gst_object_unref (backchannel_depay);
|
||||
gst_object_unref (backchannel_bin);
|
||||
goto wrongly_formatted_backchannel_bin;
|
||||
}
|
||||
|
||||
depay_ghostpad = gst_ghost_pad_new ("sink", depay_pad);
|
||||
gst_element_add_pad (backchannel_bin, depay_ghostpad);
|
||||
|
||||
gst_bin_add (GST_BIN (element), backchannel_bin);
|
||||
}
|
||||
|
||||
return element;
|
||||
|
||||
/* ERRORS */
|
||||
no_launch:
|
||||
{
|
||||
g_critical ("no launch line specified");
|
||||
g_free (launch);
|
||||
return NULL;
|
||||
}
|
||||
parse_error:
|
||||
{
|
||||
g_critical ("could not parse launch syntax (%s): %s", launch,
|
||||
(error ? error->message : "unknown reason"));
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
g_free (launch);
|
||||
return NULL;
|
||||
}
|
||||
no_launch_backchannel:
|
||||
{
|
||||
g_critical ("no backchannel launch line specified");
|
||||
gst_object_unref (element);
|
||||
return NULL;
|
||||
}
|
||||
parse_error_backchannel:
|
||||
{
|
||||
g_critical ("could not parse backchannel launch syntax (%s): %s", launch,
|
||||
(error ? error->message : "unknown reason"));
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
g_free (launch);
|
||||
gst_object_unref (element);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wrongly_formatted_backchannel_bin:
|
||||
{
|
||||
g_critical ("invalidly formatted backchannel bin");
|
||||
|
||||
gst_object_unref (element);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_onvif_media_factory_has_backchannel_support_default
|
||||
(GstRTSPOnvifMediaFactory * factory)
|
||||
{
|
||||
/* No locking here, we just check if it's non-NULL */
|
||||
return factory->priv->backchannel_launch != NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_factory_finalize (GObject * object)
|
||||
{
|
||||
GstRTSPOnvifMediaFactory *factory = GST_RTSP_ONVIF_MEDIA_FACTORY (object);
|
||||
|
||||
g_free (factory->priv->backchannel_launch);
|
||||
factory->priv->backchannel_launch = NULL;
|
||||
|
||||
g_mutex_clear (&factory->priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_onvif_media_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_factory_class_init (GstRTSPOnvifMediaFactoryClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstRTSPMediaFactoryClass *factory_klass = (GstRTSPMediaFactoryClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_rtsp_onvif_media_factory_finalize;
|
||||
|
||||
factory_klass->gen_key = gst_rtsp_onvif_media_factory_gen_key;
|
||||
factory_klass->construct = gst_rtsp_onvif_media_factory_construct;
|
||||
factory_klass->create_element = gst_rtsp_onvif_media_factory_create_element;
|
||||
|
||||
klass->has_backchannel_support =
|
||||
gst_rtsp_onvif_media_factory_has_backchannel_support_default;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_factory_init (GstRTSPOnvifMediaFactory * factory)
|
||||
{
|
||||
factory->priv = gst_rtsp_onvif_media_factory_get_instance_private (factory);
|
||||
g_mutex_init (&factory->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_set_backchannel_launch:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
* @launch: the launch description
|
||||
*
|
||||
* The gst_parse_launch() line to use for constructing the ONVIF backchannel
|
||||
* pipeline in the default prepare vmethod if requested by the client.
|
||||
*
|
||||
* The pipeline description should return a GstBin as the toplevel element
|
||||
* which can be accomplished by enclosing the description with brackets '('
|
||||
* ')'.
|
||||
*
|
||||
* The description should return a pipeline with a single depayloader named
|
||||
* depay_backchannel. A caps query on the depayloader's sinkpad should return
|
||||
* all possible, complete RTP caps that are going to be supported. At least
|
||||
* the payload type, clock-rate and encoding-name need to be specified.
|
||||
*
|
||||
* Note: The pipeline part passed here must end in sinks that are not waiting
|
||||
* until pre-rolling before reaching the PAUSED state, i.e. setting
|
||||
* async=false on #GstBaseSink. Otherwise the whole media will not be able to
|
||||
* prepare.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_onvif_media_factory_set_backchannel_launch (GstRTSPOnvifMediaFactory *
|
||||
factory, const gchar * launch)
|
||||
{
|
||||
g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory));
|
||||
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
g_free (factory->priv->backchannel_launch);
|
||||
factory->priv->backchannel_launch = g_strdup (launch);
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_get_backchannel_launch:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Get the gst_parse_launch() pipeline description that will be used in the
|
||||
* default prepare vmethod for generating the ONVIF backchannel pipeline.
|
||||
*
|
||||
* Returns: (transfer full): the configured backchannel launch description. g_free() after
|
||||
* usage.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_onvif_media_factory_get_backchannel_launch (GstRTSPOnvifMediaFactory *
|
||||
factory)
|
||||
{
|
||||
gchar *launch;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), NULL);
|
||||
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
launch = g_strdup (factory->priv->backchannel_launch);
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
|
||||
return launch;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_has_backchannel_support:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Returns %TRUE if an ONVIF backchannel is supported by the media factory.
|
||||
*
|
||||
* Returns: %TRUE if an ONVIF backchannel is supported by the media factory.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory *
|
||||
factory)
|
||||
{
|
||||
GstRTSPOnvifMediaFactoryClass *klass;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), FALSE);
|
||||
|
||||
klass = GST_RTSP_ONVIF_MEDIA_FACTORY_GET_CLASS (factory);
|
||||
|
||||
if (klass->has_backchannel_support)
|
||||
return klass->has_backchannel_support (factory);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_has_replay_support:
|
||||
*
|
||||
* Returns: %TRUE if ONVIF replay is supported by the media factory.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_onvif_media_factory_has_replay_support (GstRTSPOnvifMediaFactory *
|
||||
factory)
|
||||
{
|
||||
gboolean has_replay_support;
|
||||
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
has_replay_support = factory->priv->has_replay_support;
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
|
||||
return has_replay_support;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_set_replay_support:
|
||||
*
|
||||
* Set to %TRUE if ONVIF replay is supported by the media factory.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
void
|
||||
gst_rtsp_onvif_media_factory_set_replay_support (GstRTSPOnvifMediaFactory *
|
||||
factory, gboolean has_replay_support)
|
||||
{
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
factory->priv->has_replay_support = has_replay_support;
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_set_backchannel_bandwidth:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
* @bandwidth: the bandwidth in bits per second
|
||||
*
|
||||
* Set the configured/supported bandwidth of the ONVIF backchannel pipeline in
|
||||
* bits per second.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_onvif_media_factory_set_backchannel_bandwidth (GstRTSPOnvifMediaFactory
|
||||
* factory, guint bandwidth)
|
||||
{
|
||||
g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory));
|
||||
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
factory->priv->backchannel_bandwidth = bandwidth;
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_get_backchannel_bandwidth:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Get the configured/supported bandwidth of the ONVIF backchannel pipeline in
|
||||
* bits per second.
|
||||
*
|
||||
* Returns: the configured/supported backchannel bandwidth.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_onvif_media_factory_get_backchannel_bandwidth (GstRTSPOnvifMediaFactory
|
||||
* factory)
|
||||
{
|
||||
guint bandwidth;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA_FACTORY (factory), 0);
|
||||
|
||||
g_mutex_lock (&factory->priv->lock);
|
||||
bandwidth = factory->priv->backchannel_bandwidth;
|
||||
g_mutex_unlock (&factory->priv->lock);
|
||||
|
||||
return bandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_factory_new:
|
||||
*
|
||||
* Create a new #GstRTSPOnvifMediaFactory
|
||||
*
|
||||
* Returns: A new #GstRTSPOnvifMediaFactory
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
GstRTSPMediaFactory *
|
||||
gst_rtsp_onvif_media_factory_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, NULL);
|
||||
}
|
95
gst/rtsp-server/rtsp-onvif-media-factory.h
Normal file
95
gst/rtsp-server/rtsp-onvif-media-factory.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_ONVIF_MEDIA_FACTORY_H__
|
||||
#define __GST_RTSP_ONVIF_MEDIA_FACTORY_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-media-factory.h"
|
||||
|
||||
#define GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY (gst_rtsp_onvif_media_factory_get_type ())
|
||||
#define GST_IS_RTSP_ONVIF_MEDIA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY))
|
||||
#define GST_IS_RTSP_ONVIF_MEDIA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY))
|
||||
#define GST_RTSP_ONVIF_MEDIA_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactoryClass))
|
||||
#define GST_RTSP_ONVIF_MEDIA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactory))
|
||||
#define GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_MEDIA_FACTORY, GstRTSPOnvifMediaFactoryClass))
|
||||
#define GST_RTSP_ONVIF_MEDIA_FACTORY_CAST(obj) ((GstRTSPOnvifMediaFactory*)(obj))
|
||||
#define GST_RTSP_ONVIF_MEDIA_FACTORY_CLASS_CAST(klass) ((GstRTSPOnvifMediaFactoryClass*)(klass))
|
||||
|
||||
typedef struct GstRTSPOnvifMediaFactoryClass GstRTSPOnvifMediaFactoryClass;
|
||||
typedef struct GstRTSPOnvifMediaFactory GstRTSPOnvifMediaFactory;
|
||||
typedef struct GstRTSPOnvifMediaFactoryPrivate GstRTSPOnvifMediaFactoryPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPOnvifMediaFactory:
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct GstRTSPOnvifMediaFactoryClass
|
||||
{
|
||||
GstRTSPMediaFactoryClass parent;
|
||||
gboolean (*has_backchannel_support) (GstRTSPOnvifMediaFactory * factory);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
struct GstRTSPOnvifMediaFactory
|
||||
{
|
||||
GstRTSPMediaFactory parent;
|
||||
GstRTSPOnvifMediaFactoryPrivate *priv;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_onvif_media_factory_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMediaFactory *gst_rtsp_onvif_media_factory_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_onvif_media_factory_set_backchannel_launch (GstRTSPOnvifMediaFactory *
|
||||
factory, const gchar * launch);
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_onvif_media_factory_get_backchannel_launch (GstRTSPOnvifMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_onvif_media_factory_has_backchannel_support (GstRTSPOnvifMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_onvif_media_factory_has_replay_support (GstRTSPOnvifMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_onvif_media_factory_set_replay_support (GstRTSPOnvifMediaFactory * factory, gboolean has_replay_support);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_onvif_media_factory_set_backchannel_bandwidth (GstRTSPOnvifMediaFactory * factory, guint bandwidth);
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_onvif_media_factory_get_backchannel_bandwidth (GstRTSPOnvifMediaFactory * factory);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_onvif_media_factory_requires_backchannel (GstRTSPMediaFactory * factory, GstRTSPContext * ctx);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPOnvifMediaFactory, gst_object_unref)
|
||||
#endif
|
||||
|
||||
#endif /* __GST_RTSP_ONVIF_MEDIA_FACTORY_H__ */
|
358
gst/rtsp-server/rtsp-onvif-media.c
Normal file
358
gst/rtsp-server/rtsp-onvif-media.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:rtsp-onvif-media
|
||||
* @short_description: The ONVIF media pipeline
|
||||
* @see_also: #GstRTSPMedia, #GstRTSPOnvifMediaFactory, #GstRTSPStream, #GstRTSPSession,
|
||||
* #GstRTSPSessionMedia
|
||||
*
|
||||
* a #GstRTSPOnvifMedia contains the complete GStreamer pipeline to manage the
|
||||
* streaming to the clients. The actual data transfer is done by the
|
||||
* #GstRTSPStream objects that are created and exposed by the #GstRTSPMedia.
|
||||
*
|
||||
* On top of #GstRTSPMedia this subclass adds special ONVIF features.
|
||||
* Special ONVIF features that are currently supported is a backchannel for
|
||||
* the client to send back media to the server in a normal PLAY media. To
|
||||
* handle the ONVIF backchannel, a #GstRTSPOnvifMediaFactory and
|
||||
* #GstRTSPOnvifServer has to be used.
|
||||
*
|
||||
* Since: 1.14
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "rtsp-onvif-media.h"
|
||||
#include "rtsp-latency-bin.h"
|
||||
|
||||
struct GstRTSPOnvifMediaPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
guint backchannel_bandwidth;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPOnvifMedia, gst_rtsp_onvif_media,
|
||||
GST_TYPE_RTSP_MEDIA);
|
||||
|
||||
static gboolean
|
||||
gst_rtsp_onvif_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
|
||||
GstSDPInfo * info)
|
||||
{
|
||||
guint i, n_streams;
|
||||
gchar *rangestr;
|
||||
gboolean res;
|
||||
|
||||
/* Mostly a copy of gst_rtsp_sdp_from_media() which handles the backchannel
|
||||
* stream separately and adds sendonly/recvonly attributes to each media
|
||||
*/
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
|
||||
rangestr = gst_rtsp_media_get_range_string (media, FALSE, GST_RTSP_RANGE_NPT);
|
||||
if (rangestr == NULL)
|
||||
goto not_prepared;
|
||||
|
||||
gst_sdp_message_add_attribute (sdp, "range", rangestr);
|
||||
g_free (rangestr);
|
||||
|
||||
res = TRUE;
|
||||
for (i = 0; res && (i < n_streams); i++) {
|
||||
GstRTSPStream *stream;
|
||||
GstCaps *caps = NULL;
|
||||
GstRTSPProfile profiles;
|
||||
guint mask;
|
||||
GstPad *sinkpad = NULL;
|
||||
guint n_caps, j;
|
||||
|
||||
/* Mostly a copy of gst_rtsp_sdp_from_stream() which handles the
|
||||
* backchannel stream separately */
|
||||
|
||||
stream = gst_rtsp_media_get_stream (media, i);
|
||||
|
||||
if ((sinkpad = gst_rtsp_stream_get_sinkpad (stream))) {
|
||||
caps = gst_pad_query_caps (sinkpad, NULL);
|
||||
} else {
|
||||
caps = gst_rtsp_stream_get_caps (stream);
|
||||
}
|
||||
|
||||
if (caps == NULL) {
|
||||
GST_ERROR ("stream %p has no caps", stream);
|
||||
res = FALSE;
|
||||
if (sinkpad)
|
||||
gst_object_unref (sinkpad);
|
||||
break;
|
||||
} else if (!sinkpad && !gst_caps_is_fixed (caps)) {
|
||||
GST_ERROR ("stream %p has unfixed caps", stream);
|
||||
res = FALSE;
|
||||
gst_caps_unref (caps);
|
||||
break;
|
||||
}
|
||||
|
||||
n_caps = gst_caps_get_size (caps);
|
||||
for (j = 0; res && j < n_caps; j++) {
|
||||
GstStructure *s = gst_caps_get_structure (caps, j);
|
||||
GstCaps *media_caps = gst_caps_new_full (gst_structure_copy (s), NULL);
|
||||
|
||||
if (!gst_caps_is_fixed (media_caps)) {
|
||||
GST_ERROR ("media caps for stream %p are not all fixed", stream);
|
||||
res = FALSE;
|
||||
gst_caps_unref (media_caps);
|
||||
break;
|
||||
}
|
||||
|
||||
/* make a new media for each profile */
|
||||
profiles = gst_rtsp_stream_get_profiles (stream);
|
||||
mask = 1;
|
||||
res = TRUE;
|
||||
while (res && (profiles >= mask)) {
|
||||
GstRTSPProfile prof = profiles & mask;
|
||||
|
||||
if (prof) {
|
||||
res = gst_rtsp_sdp_make_media (sdp, info, stream, media_caps, prof);
|
||||
if (res) {
|
||||
GstSDPMedia *smedia =
|
||||
&g_array_index (sdp->medias, GstSDPMedia, sdp->medias->len - 1);
|
||||
gchar *x_onvif_track, *media_str;
|
||||
|
||||
media_str =
|
||||
g_ascii_strup (gst_structure_get_string (s, "media"), -1);
|
||||
x_onvif_track =
|
||||
g_strdup_printf ("%s%03d", media_str, sdp->medias->len - 1);
|
||||
gst_sdp_media_add_attribute (smedia, "x-onvif-track",
|
||||
x_onvif_track);
|
||||
g_free (x_onvif_track);
|
||||
g_free (media_str);
|
||||
|
||||
if (sinkpad) {
|
||||
GstRTSPOnvifMedia *onvif_media = GST_RTSP_ONVIF_MEDIA (media);
|
||||
|
||||
gst_sdp_media_add_attribute (smedia, "sendonly", "");
|
||||
if (onvif_media->priv->backchannel_bandwidth > 0)
|
||||
gst_sdp_media_add_bandwidth (smedia, GST_SDP_BWTYPE_AS,
|
||||
onvif_media->priv->backchannel_bandwidth);
|
||||
} else {
|
||||
gst_sdp_media_add_attribute (smedia, "recvonly", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
if (sinkpad) {
|
||||
GstStructure *s = gst_caps_get_structure (media_caps, 0);
|
||||
gint pt = -1;
|
||||
|
||||
if (!gst_structure_get_int (s, "payload", &pt) || pt < 0) {
|
||||
GST_ERROR ("stream %p has no payload type", stream);
|
||||
res = FALSE;
|
||||
gst_caps_unref (media_caps);
|
||||
gst_object_unref (sinkpad);
|
||||
break;
|
||||
}
|
||||
|
||||
gst_rtsp_stream_set_pt_map (stream, pt, media_caps);
|
||||
}
|
||||
|
||||
gst_caps_unref (media_caps);
|
||||
}
|
||||
|
||||
gst_caps_unref (caps);
|
||||
if (sinkpad)
|
||||
gst_object_unref (sinkpad);
|
||||
}
|
||||
|
||||
{
|
||||
GstNetTimeProvider *provider;
|
||||
|
||||
if ((provider =
|
||||
gst_rtsp_media_get_time_provider (media, info->server_ip, 0))) {
|
||||
GstClock *clock;
|
||||
gchar *address, *str;
|
||||
gint port;
|
||||
|
||||
g_object_get (provider, "clock", &clock, "address", &address, "port",
|
||||
&port, NULL);
|
||||
|
||||
str = g_strdup_printf ("GstNetTimeProvider %s %s:%d %" G_GUINT64_FORMAT,
|
||||
g_type_name (G_TYPE_FROM_INSTANCE (clock)), address, port,
|
||||
gst_clock_get_time (clock));
|
||||
|
||||
gst_sdp_message_add_attribute (sdp, "x-gst-clock", str);
|
||||
g_free (str);
|
||||
gst_object_unref (clock);
|
||||
g_free (address);
|
||||
gst_object_unref (provider);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
/* ERRORS */
|
||||
not_prepared:
|
||||
{
|
||||
GST_ERROR ("media %p is not prepared", media);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_finalize (GObject * object)
|
||||
{
|
||||
GstRTSPOnvifMedia *media = GST_RTSP_ONVIF_MEDIA (object);
|
||||
|
||||
g_mutex_clear (&media->priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_onvif_media_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_class_init (GstRTSPOnvifMediaClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstRTSPMediaClass *media_class = (GstRTSPMediaClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_rtsp_onvif_media_finalize;
|
||||
|
||||
media_class->setup_sdp = gst_rtsp_onvif_media_setup_sdp;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_media_init (GstRTSPOnvifMedia * media)
|
||||
{
|
||||
media->priv = gst_rtsp_onvif_media_get_instance_private (media);
|
||||
g_mutex_init (&media->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_collect_backchannel:
|
||||
* @media: a #GstRTSPOnvifMedia
|
||||
*
|
||||
* Find the ONVIF backchannel depayloader element. It should be named
|
||||
* 'depay_backchannel', be placed in a bin called 'onvif-backchannel'
|
||||
* and return all supported RTP caps on a caps query. Complete RTP caps with
|
||||
* at least the payload type, clock-rate and encoding-name are required.
|
||||
*
|
||||
* A new #GstRTSPStream is created for the backchannel if found.
|
||||
*
|
||||
* Returns: %TRUE if a backchannel stream could be found and created
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media)
|
||||
{
|
||||
GstElement *element, *backchannel_bin = NULL;
|
||||
GstElement *latency_bin;
|
||||
GstPad *pad = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media), FALSE);
|
||||
|
||||
element = gst_rtsp_media_get_element (GST_RTSP_MEDIA (media));
|
||||
if (!element)
|
||||
return ret;
|
||||
|
||||
backchannel_bin =
|
||||
gst_bin_get_by_name (GST_BIN (element), "onvif-backchannel");
|
||||
if (!backchannel_bin)
|
||||
goto out;
|
||||
|
||||
/* We don't want the backchannel element, which is a receiver, to affect
|
||||
* latency on the complete pipeline. That's why we remove it from the
|
||||
* pipeline and add it to a @GstRTSPLatencyBin which will prevent it from
|
||||
* messing up pipelines latency. The extra reference is needed so that it
|
||||
* is not freed in case the pipeline holds the the only ref to it.
|
||||
*
|
||||
* TODO: a more generic solution should be implemented in
|
||||
* gst_rtsp_media_collect_streams() where all receivers are encapsulated
|
||||
* in a @GstRTSPLatencyBin in cases when there are senders too. */
|
||||
gst_object_ref (backchannel_bin);
|
||||
gst_bin_remove (GST_BIN (element), backchannel_bin);
|
||||
|
||||
latency_bin = gst_rtsp_latency_bin_new (backchannel_bin);
|
||||
g_assert (latency_bin);
|
||||
|
||||
gst_bin_add (GST_BIN (element), latency_bin);
|
||||
|
||||
pad = gst_element_get_static_pad (latency_bin, "sink");
|
||||
if (!pad)
|
||||
goto out;
|
||||
|
||||
gst_rtsp_media_create_stream (GST_RTSP_MEDIA (media), latency_bin, pad);
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
if (pad)
|
||||
gst_object_unref (pad);
|
||||
if (backchannel_bin)
|
||||
gst_object_unref (backchannel_bin);
|
||||
gst_object_unref (element);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_set_backchannel_bandwidth:
|
||||
* @media: a #GstRTSPMedia
|
||||
* @bandwidth: the bandwidth in bits per second
|
||||
*
|
||||
* Set the configured/supported bandwidth of the ONVIF backchannel pipeline in
|
||||
* bits per second.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_onvif_media_set_backchannel_bandwidth (GstRTSPOnvifMedia * media,
|
||||
guint bandwidth)
|
||||
{
|
||||
g_return_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media));
|
||||
|
||||
g_mutex_lock (&media->priv->lock);
|
||||
media->priv->backchannel_bandwidth = bandwidth;
|
||||
g_mutex_unlock (&media->priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_media_get_backchannel_bandwidth:
|
||||
* @media: a #GstRTSPMedia
|
||||
*
|
||||
* Get the configured/supported bandwidth of the ONVIF backchannel pipeline in
|
||||
* bits per second.
|
||||
*
|
||||
* Returns: the configured/supported backchannel bandwidth.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_onvif_media_get_backchannel_bandwidth (GstRTSPOnvifMedia * media)
|
||||
{
|
||||
guint bandwidth;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_ONVIF_MEDIA (media), 0);
|
||||
|
||||
g_mutex_lock (&media->priv->lock);
|
||||
bandwidth = media->priv->backchannel_bandwidth;
|
||||
g_mutex_unlock (&media->priv->lock);
|
||||
|
||||
return bandwidth;
|
||||
}
|
71
gst/rtsp-server/rtsp-onvif-media.h
Normal file
71
gst/rtsp-server/rtsp-onvif-media.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_ONVIF_MEDIA_H__
|
||||
#define __GST_RTSP_ONVIF_MEDIA_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-media.h"
|
||||
|
||||
#define GST_TYPE_RTSP_ONVIF_MEDIA (gst_rtsp_onvif_media_get_type ())
|
||||
#define GST_IS_RTSP_ONVIF_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_MEDIA))
|
||||
#define GST_IS_RTSP_ONVIF_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_MEDIA))
|
||||
#define GST_RTSP_ONVIF_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMediaClass))
|
||||
#define GST_RTSP_ONVIF_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMedia))
|
||||
#define GST_RTSP_ONVIF_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_MEDIA, GstRTSPOnvifMediaClass))
|
||||
#define GST_RTSP_ONVIF_MEDIA_CAST(obj) ((GstRTSPOnvifMedia*)(obj))
|
||||
#define GST_RTSP_ONVIF_MEDIA_CLASS_CAST(klass) ((GstRTSPOnvifMediaClass*)(klass))
|
||||
|
||||
typedef struct GstRTSPOnvifMediaClass GstRTSPOnvifMediaClass;
|
||||
typedef struct GstRTSPOnvifMedia GstRTSPOnvifMedia;
|
||||
typedef struct GstRTSPOnvifMediaPrivate GstRTSPOnvifMediaPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPOnvifMedia:
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct GstRTSPOnvifMediaClass
|
||||
{
|
||||
GstRTSPMediaClass parent;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
struct GstRTSPOnvifMedia
|
||||
{
|
||||
GstRTSPMedia parent;
|
||||
GstRTSPOnvifMediaPrivate *priv;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_onvif_media_get_type (void);
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_onvif_media_collect_backchannel (GstRTSPOnvifMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_onvif_media_set_backchannel_bandwidth (GstRTSPOnvifMedia * media, guint bandwidth);
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_onvif_media_get_backchannel_bandwidth (GstRTSPOnvifMedia * media);
|
||||
|
||||
#endif /* __GST_RTSP_ONVIF_MEDIA_H__ */
|
101
gst/rtsp-server/rtsp-onvif-server.c
Normal file
101
gst/rtsp-server/rtsp-onvif-server.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-onvif-server
|
||||
* @short_description: The main server object
|
||||
* @see_also: #GstRTSPOnvifMediaFactory, #GstRTSPClient
|
||||
*
|
||||
* The server object is the object listening for connections on a port and
|
||||
* creating #GstRTSPOnvifClient objects to handle those connections.
|
||||
*
|
||||
* The only different to #GstRTSPServer is that #GstRTSPOnvifServer creates
|
||||
* #GstRTSPOnvifClient that have special handling for ONVIF specific features,
|
||||
* like a backchannel that allows clients to send back media to the server.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "rtsp-context.h"
|
||||
#include "rtsp-onvif-server.h"
|
||||
#include "rtsp-onvif-client.h"
|
||||
|
||||
G_DEFINE_TYPE (GstRTSPOnvifServer, gst_rtsp_onvif_server, GST_TYPE_RTSP_SERVER);
|
||||
|
||||
static GstRTSPClient *
|
||||
gst_rtsp_onvif_server_create_client (GstRTSPServer * server)
|
||||
{
|
||||
GstRTSPClient *client;
|
||||
GstRTSPSessionPool *session_pool;
|
||||
GstRTSPMountPoints *mount_points;
|
||||
GstRTSPAuth *auth;
|
||||
GstRTSPThreadPool *thread_pool;
|
||||
|
||||
/* a new client connected, create a session to handle the client. */
|
||||
client = g_object_new (GST_TYPE_RTSP_ONVIF_CLIENT, NULL);
|
||||
|
||||
/* set the session pool that this client should use */
|
||||
session_pool = gst_rtsp_server_get_session_pool (server);
|
||||
gst_rtsp_client_set_session_pool (client, session_pool);
|
||||
g_object_unref (session_pool);
|
||||
/* set the mount points that this client should use */
|
||||
mount_points = gst_rtsp_server_get_mount_points (server);
|
||||
gst_rtsp_client_set_mount_points (client, mount_points);
|
||||
g_object_unref (mount_points);
|
||||
/* set authentication manager */
|
||||
auth = gst_rtsp_server_get_auth (server);
|
||||
gst_rtsp_client_set_auth (client, auth);
|
||||
if (auth)
|
||||
g_object_unref (auth);
|
||||
/* set threadpool */
|
||||
thread_pool = gst_rtsp_server_get_thread_pool (server);
|
||||
gst_rtsp_client_set_thread_pool (client, thread_pool);
|
||||
g_object_unref (thread_pool);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_onvif_server_new:
|
||||
*
|
||||
* Create a new #GstRTSPOnvifServer instance.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPOnvifServer
|
||||
*/
|
||||
GstRTSPServer *
|
||||
gst_rtsp_onvif_server_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_RTSP_ONVIF_SERVER, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_server_class_init (GstRTSPOnvifServerClass * klass)
|
||||
{
|
||||
GstRTSPServerClass *server_klass = (GstRTSPServerClass *) klass;
|
||||
|
||||
server_klass->create_client = gst_rtsp_onvif_server_create_client;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_onvif_server_init (GstRTSPOnvifServer * server)
|
||||
{
|
||||
}
|
71
gst/rtsp-server/rtsp-onvif-server.h
Normal file
71
gst/rtsp-server/rtsp-onvif-server.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_ONVIF_SERVER_H__
|
||||
#define __GST_RTSP_ONVIF_SERVER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "rtsp-server-object.h"
|
||||
|
||||
#define GST_TYPE_RTSP_ONVIF_SERVER (gst_rtsp_onvif_server_get_type ())
|
||||
#define GST_IS_RTSP_ONVIF_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_ONVIF_SERVER))
|
||||
#define GST_IS_RTSP_ONVIF_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_ONVIF_SERVER))
|
||||
#define GST_RTSP_ONVIF_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServerClass))
|
||||
#define GST_RTSP_ONVIF_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServer))
|
||||
#define GST_RTSP_ONVIF_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_ONVIF_SERVER, GstRTSPOnvifServerClass))
|
||||
#define GST_RTSP_ONVIF_SERVER_CAST(obj) ((GstRTSPOnvifServer*)(obj))
|
||||
#define GST_RTSP_ONVIF_SERVER_CLASS_CAST(klass) ((GstRTSPOnvifServerClass*)(klass))
|
||||
|
||||
typedef struct GstRTSPOnvifServerClass GstRTSPOnvifServerClass;
|
||||
typedef struct GstRTSPOnvifServer GstRTSPOnvifServer;
|
||||
|
||||
/**
|
||||
* GstRTSPOnvifServer:
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct GstRTSPOnvifServerClass
|
||||
{
|
||||
GstRTSPServerClass parent;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
struct GstRTSPOnvifServer
|
||||
{
|
||||
GstRTSPServer parent;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_onvif_server_get_type (void);
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPServer *gst_rtsp_onvif_server_new (void);
|
||||
|
||||
#define GST_RTSP_ONVIF_BACKCHANNEL_REQUIREMENT "www.onvif.org/ver20/backchannel"
|
||||
#define GST_RTSP_ONVIF_REPLAY_REQUIREMENT "onvif-replay"
|
||||
|
||||
#include "rtsp-onvif-client.h"
|
||||
#include "rtsp-onvif-media-factory.h"
|
||||
#include "rtsp-onvif-media.h"
|
||||
|
||||
#endif /* __GST_RTSP_ONVIF_SERVER_H__ */
|
80
gst/rtsp-server/rtsp-params.c
Normal file
80
gst/rtsp-server/rtsp-params.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-params
|
||||
* @short_description: Param get and set implementation
|
||||
* @see_also: #GstRTSPClient
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-params.h"
|
||||
|
||||
/**
|
||||
* gst_rtsp_params_set:
|
||||
* @client: a #GstRTSPClient
|
||||
* @ctx: (transfer none): a #GstRTSPContext
|
||||
*
|
||||
* Set parameters (not implemented yet)
|
||||
*
|
||||
* Returns: a #GstRTSPResult
|
||||
*/
|
||||
GstRTSPResult
|
||||
gst_rtsp_params_set (GstRTSPClient * client, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPStatusCode code;
|
||||
|
||||
/* FIXME, actually parse the request based on the mime type and try to repond
|
||||
* with a list of the parameters */
|
||||
code = GST_RTSP_STS_PARAMETER_NOT_UNDERSTOOD;
|
||||
|
||||
gst_rtsp_message_init_response (ctx->response, code,
|
||||
gst_rtsp_status_as_text (code), ctx->request);
|
||||
|
||||
return GST_RTSP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_params_get:
|
||||
* @client: a #GstRTSPClient
|
||||
* @ctx: (transfer none): a #GstRTSPContext
|
||||
*
|
||||
* Get parameters (not implemented yet)
|
||||
*
|
||||
* Returns: a #GstRTSPResult
|
||||
*/
|
||||
GstRTSPResult
|
||||
gst_rtsp_params_get (GstRTSPClient * client, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPStatusCode code;
|
||||
|
||||
/* FIXME, actually parse the request based on the mime type and try to repond
|
||||
* with a list of the parameters */
|
||||
code = GST_RTSP_STS_PARAMETER_NOT_UNDERSTOOD;
|
||||
|
||||
gst_rtsp_message_init_response (ctx->response, code,
|
||||
gst_rtsp_status_as_text (code), ctx->request);
|
||||
|
||||
return GST_RTSP_OK;
|
||||
}
|
41
gst/rtsp-server/rtsp-params.h
Normal file
41
gst/rtsp-server/rtsp-params.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp/gstrtspurl.h>
|
||||
#include <gst/rtsp/gstrtspmessage.h>
|
||||
|
||||
#ifndef __GST_RTSP_PARAMS_H__
|
||||
#define __GST_RTSP_PARAMS_H__
|
||||
|
||||
#include "rtsp-client.h"
|
||||
#include "rtsp-session.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPResult gst_rtsp_params_set (GstRTSPClient * client, GstRTSPContext * ctx);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPResult gst_rtsp_params_get (GstRTSPClient * client, GstRTSPContext * ctx);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_PARAMS_H__ */
|
369
gst/rtsp-server/rtsp-permissions.c
Normal file
369
gst/rtsp-server/rtsp-permissions.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2013 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-permissions
|
||||
* @short_description: Roles and associated permissions
|
||||
* @see_also: #GstRTSPToken, #GstRTSPAuth
|
||||
*
|
||||
* The #GstRTSPPermissions object contains an array of roles and associated
|
||||
* permissions. The roles are represented with a string and the permissions with
|
||||
* a generic #GstStructure.
|
||||
*
|
||||
* The permissions are deliberately kept generic. The possible values of the
|
||||
* roles and #GstStructure keys and values are only determined by the #GstRTSPAuth
|
||||
* object that performs the checks on the permissions and the current
|
||||
* #GstRTSPToken.
|
||||
*
|
||||
* As a convenience function, gst_rtsp_permissions_is_allowed() can be used to
|
||||
* check if the permissions contains a role that contains the boolean value
|
||||
* %TRUE for the the given key.
|
||||
*
|
||||
* Last reviewed on 2013-07-15 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-permissions.h"
|
||||
|
||||
typedef struct _GstRTSPPermissionsImpl
|
||||
{
|
||||
GstRTSPPermissions permissions;
|
||||
|
||||
/* Roles, array of GstStructure */
|
||||
GPtrArray *roles;
|
||||
} GstRTSPPermissionsImpl;
|
||||
|
||||
static void
|
||||
free_structure (GstStructure * structure)
|
||||
{
|
||||
gst_structure_set_parent_refcount (structure, NULL);
|
||||
gst_structure_free (structure);
|
||||
}
|
||||
|
||||
//GST_DEBUG_CATEGORY_STATIC (rtsp_permissions_debug);
|
||||
//#define GST_CAT_DEFAULT rtsp_permissions_debug
|
||||
|
||||
GST_DEFINE_MINI_OBJECT_TYPE (GstRTSPPermissions, gst_rtsp_permissions);
|
||||
|
||||
static void gst_rtsp_permissions_init (GstRTSPPermissionsImpl * permissions);
|
||||
|
||||
static void
|
||||
_gst_rtsp_permissions_free (GstRTSPPermissions * permissions)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
|
||||
g_ptr_array_free (impl->roles, TRUE);
|
||||
|
||||
g_slice_free1 (sizeof (GstRTSPPermissionsImpl), permissions);
|
||||
}
|
||||
|
||||
static GstRTSPPermissions *
|
||||
_gst_rtsp_permissions_copy (GstRTSPPermissionsImpl * permissions)
|
||||
{
|
||||
GstRTSPPermissionsImpl *copy;
|
||||
guint i;
|
||||
|
||||
copy = (GstRTSPPermissionsImpl *) gst_rtsp_permissions_new ();
|
||||
|
||||
for (i = 0; i < permissions->roles->len; i++) {
|
||||
GstStructure *entry = g_ptr_array_index (permissions->roles, i);
|
||||
GstStructure *entry_copy = gst_structure_copy (entry);
|
||||
|
||||
gst_structure_set_parent_refcount (entry_copy,
|
||||
©->permissions.mini_object.refcount);
|
||||
g_ptr_array_add (copy->roles, entry_copy);
|
||||
}
|
||||
|
||||
return GST_RTSP_PERMISSIONS (copy);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_permissions_init (GstRTSPPermissionsImpl * permissions)
|
||||
{
|
||||
gst_mini_object_init (GST_MINI_OBJECT_CAST (permissions), 0,
|
||||
GST_TYPE_RTSP_PERMISSIONS,
|
||||
(GstMiniObjectCopyFunction) _gst_rtsp_permissions_copy, NULL,
|
||||
(GstMiniObjectFreeFunction) _gst_rtsp_permissions_free);
|
||||
|
||||
permissions->roles =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify) free_structure);
|
||||
}
|
||||
|
||||
static void
|
||||
add_role_from_structure (GstRTSPPermissionsImpl * impl,
|
||||
GstStructure * structure)
|
||||
{
|
||||
guint i, len;
|
||||
const gchar *role = gst_structure_get_name (structure);
|
||||
|
||||
len = impl->roles->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
GstStructure *entry = g_ptr_array_index (impl->roles, i);
|
||||
|
||||
if (gst_structure_has_name (entry, role)) {
|
||||
g_ptr_array_remove_index_fast (impl->roles, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gst_structure_set_parent_refcount (structure,
|
||||
&impl->permissions.mini_object.refcount);
|
||||
g_ptr_array_add (impl->roles, structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_new:
|
||||
*
|
||||
* Create a new empty Authorization permissions.
|
||||
*
|
||||
* Returns: (transfer full): a new empty authorization permissions.
|
||||
*/
|
||||
GstRTSPPermissions *
|
||||
gst_rtsp_permissions_new (void)
|
||||
{
|
||||
GstRTSPPermissionsImpl *permissions;
|
||||
|
||||
permissions = g_slice_new0 (GstRTSPPermissionsImpl);
|
||||
gst_rtsp_permissions_init (permissions);
|
||||
|
||||
return GST_RTSP_PERMISSIONS (permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_add_permission_for_role:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
* @permission: the permission
|
||||
* @allowed: whether the role has this permission or not
|
||||
*
|
||||
* Add a new @permission for @role to @permissions with the access in @allowed.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_add_permission_for_role (GstRTSPPermissions * permissions,
|
||||
const gchar * role, const gchar * permission, gboolean allowed)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
guint i, len;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_PERMISSIONS (permissions));
|
||||
g_return_if_fail (gst_mini_object_is_writable (&permissions->mini_object));
|
||||
g_return_if_fail (role != NULL);
|
||||
g_return_if_fail (permission != NULL);
|
||||
|
||||
len = impl->roles->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
GstStructure *entry = g_ptr_array_index (impl->roles, i);
|
||||
|
||||
if (gst_structure_has_name (entry, role)) {
|
||||
gst_structure_set (entry, permission, G_TYPE_BOOLEAN, allowed, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gst_rtsp_permissions_add_role (permissions, role,
|
||||
permission, G_TYPE_BOOLEAN, allowed, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_add_role_empty: (rename-to gst_rtsp_permissions_add_role)
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
*
|
||||
* Add a new @role to @permissions without any permissions. You can add
|
||||
* permissions for the role with gst_rtsp_permissions_add_permission_for_role().
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_add_role_empty (GstRTSPPermissions * permissions,
|
||||
const gchar * role)
|
||||
{
|
||||
gst_rtsp_permissions_add_role (permissions, role, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_add_role:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
* @fieldname: the first field name
|
||||
* @...: additional arguments
|
||||
*
|
||||
* Add a new @role to @permissions with the given variables. The fields
|
||||
* are the same layout as gst_structure_new().
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_add_role (GstRTSPPermissions * permissions,
|
||||
const gchar * role, const gchar * fieldname, ...)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, fieldname);
|
||||
gst_rtsp_permissions_add_role_valist (permissions, role, fieldname, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_add_role_valist:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
* @fieldname: the first field name
|
||||
* @var_args: additional fields to add
|
||||
*
|
||||
* Add a new @role to @permissions with the given variables. Structure fields
|
||||
* are set according to the varargs in a manner similar to gst_structure_new().
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_add_role_valist (GstRTSPPermissions * permissions,
|
||||
const gchar * role, const gchar * fieldname, va_list var_args)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
GstStructure *structure;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_PERMISSIONS (permissions));
|
||||
g_return_if_fail (gst_mini_object_is_writable (&permissions->mini_object));
|
||||
g_return_if_fail (role != NULL);
|
||||
|
||||
structure = gst_structure_new_valist (role, fieldname, var_args);
|
||||
g_return_if_fail (structure != NULL);
|
||||
|
||||
add_role_from_structure (impl, structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_add_role_from_structure:
|
||||
*
|
||||
* Add a new role to @permissions based on @structure, for example
|
||||
* given a role named `tester`, which should be granted a permission named
|
||||
* `permission1`, the structure could be created with:
|
||||
*
|
||||
* ```
|
||||
* gst_structure_new ("tester", "permission1", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||
* ```
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_add_role_from_structure (GstRTSPPermissions * permissions,
|
||||
GstStructure * structure)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
GstStructure *copy;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_PERMISSIONS (permissions));
|
||||
g_return_if_fail (GST_IS_STRUCTURE (structure));
|
||||
|
||||
copy = gst_structure_copy (structure);
|
||||
|
||||
add_role_from_structure (impl, copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_remove_role:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
*
|
||||
* Remove all permissions for @role in @permissions.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_permissions_remove_role (GstRTSPPermissions * permissions,
|
||||
const gchar * role)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
guint i, len;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_PERMISSIONS (permissions));
|
||||
g_return_if_fail (gst_mini_object_is_writable (&permissions->mini_object));
|
||||
g_return_if_fail (role != NULL);
|
||||
|
||||
len = impl->roles->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
GstStructure *entry = g_ptr_array_index (impl->roles, i);
|
||||
|
||||
if (gst_structure_has_name (entry, role)) {
|
||||
g_ptr_array_remove_index_fast (impl->roles, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_get_role:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
*
|
||||
* Get all permissions for @role in @permissions.
|
||||
*
|
||||
* Returns: (transfer none): the structure with permissions for @role. It
|
||||
* remains valid for as long as @permissions is valid.
|
||||
*/
|
||||
const GstStructure *
|
||||
gst_rtsp_permissions_get_role (GstRTSPPermissions * permissions,
|
||||
const gchar * role)
|
||||
{
|
||||
GstRTSPPermissionsImpl *impl = (GstRTSPPermissionsImpl *) permissions;
|
||||
guint i, len;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_PERMISSIONS (permissions), NULL);
|
||||
g_return_val_if_fail (role != NULL, NULL);
|
||||
|
||||
len = impl->roles->len;
|
||||
for (i = 0; i < len; i++) {
|
||||
GstStructure *entry = g_ptr_array_index (impl->roles, i);
|
||||
|
||||
if (gst_structure_has_name (entry, role))
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_is_allowed:
|
||||
* @permissions: a #GstRTSPPermissions
|
||||
* @role: a role
|
||||
* @permission: a permission
|
||||
*
|
||||
* Check if @role in @permissions is given permission for @permission.
|
||||
*
|
||||
* Returns: %TRUE if @role is allowed @permission.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_permissions_is_allowed (GstRTSPPermissions * permissions,
|
||||
const gchar * role, const gchar * permission)
|
||||
{
|
||||
const GstStructure *str;
|
||||
gboolean result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_PERMISSIONS (permissions), FALSE);
|
||||
g_return_val_if_fail (role != NULL, FALSE);
|
||||
g_return_val_if_fail (permission != NULL, FALSE);
|
||||
|
||||
str = gst_rtsp_permissions_get_role (permissions, role);
|
||||
if (str == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!gst_structure_get_boolean (str, permission, &result))
|
||||
result = FALSE;
|
||||
|
||||
return result;
|
||||
}
|
122
gst/rtsp-server/rtsp-permissions.h
Normal file
122
gst/rtsp-server/rtsp-permissions.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef __GST_RTSP_PERMISSIONS_H__
|
||||
#define __GST_RTSP_PERMISSIONS_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
typedef struct _GstRTSPPermissions GstRTSPPermissions;
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_permissions_get_type (void);
|
||||
|
||||
#define GST_TYPE_RTSP_PERMISSIONS (gst_rtsp_permissions_get_type ())
|
||||
#define GST_IS_RTSP_PERMISSIONS(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_RTSP_PERMISSIONS))
|
||||
#define GST_RTSP_PERMISSIONS_CAST(obj) ((GstRTSPPermissions*)(obj))
|
||||
#define GST_RTSP_PERMISSIONS(obj) (GST_RTSP_PERMISSIONS_CAST(obj))
|
||||
|
||||
/**
|
||||
* GstRTSPPermissions:
|
||||
*
|
||||
* The opaque permissions structure. It is used to define the permissions
|
||||
* of objects in different roles.
|
||||
*/
|
||||
struct _GstRTSPPermissions {
|
||||
GstMiniObject mini_object;
|
||||
};
|
||||
|
||||
/* refcounting */
|
||||
/**
|
||||
* gst_rtsp_permissions_ref:
|
||||
* @permissions: The permissions to refcount
|
||||
*
|
||||
* Increase the refcount of this permissions.
|
||||
*
|
||||
* Returns: (transfer full): @permissions (for convenience when doing assignments)
|
||||
*/
|
||||
static inline GstRTSPPermissions *
|
||||
gst_rtsp_permissions_ref (GstRTSPPermissions * permissions)
|
||||
{
|
||||
return (GstRTSPPermissions *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_permissions_unref:
|
||||
* @permissions: (transfer full): the permissions to refcount
|
||||
*
|
||||
* Decrease the refcount of an permissions, freeing it if the refcount reaches 0.
|
||||
*/
|
||||
static inline void
|
||||
gst_rtsp_permissions_unref (GstRTSPPermissions * permissions)
|
||||
{
|
||||
gst_mini_object_unref (GST_MINI_OBJECT_CAST (permissions));
|
||||
}
|
||||
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPermissions * gst_rtsp_permissions_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_add_role (GstRTSPPermissions *permissions,
|
||||
const gchar *role,
|
||||
const gchar *fieldname, ...);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_add_role_valist (GstRTSPPermissions *permissions,
|
||||
const gchar *role,
|
||||
const gchar *fieldname,
|
||||
va_list var_args);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_add_role_empty (GstRTSPPermissions * permissions,
|
||||
const gchar * role);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_add_role_from_structure (GstRTSPPermissions * permissions,
|
||||
GstStructure *structure);
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_add_permission_for_role (GstRTSPPermissions * permissions,
|
||||
const gchar * role,
|
||||
const gchar * permission,
|
||||
gboolean allowed);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_permissions_remove_role (GstRTSPPermissions *permissions,
|
||||
const gchar *role);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
const GstStructure * gst_rtsp_permissions_get_role (GstRTSPPermissions *permissions,
|
||||
const gchar *role);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_permissions_is_allowed (GstRTSPPermissions *permissions,
|
||||
const gchar *role, const gchar *permission);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPPermissions, gst_rtsp_permissions_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_PERMISSIONS_H__ */
|
624
gst/rtsp-server/rtsp-sdp.c
Normal file
624
gst/rtsp-server/rtsp-sdp.c
Normal file
|
@ -0,0 +1,624 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
||||
|
||||
/**
|
||||
* SECTION:rtsp-sdp
|
||||
* @short_description: Make SDP messages
|
||||
* @see_also: #GstRTSPMedia
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gst/net/net.h>
|
||||
#include <gst/sdp/gstmikey.h>
|
||||
|
||||
#include "rtsp-sdp.h"
|
||||
|
||||
static gboolean
|
||||
get_info_from_tags (GstPad * pad, GstEvent ** event, gpointer user_data)
|
||||
{
|
||||
GstSDPMedia *media = (GstSDPMedia *) user_data;
|
||||
|
||||
if (GST_EVENT_TYPE (*event) == GST_EVENT_TAG) {
|
||||
GstTagList *tags;
|
||||
guint bitrate = 0;
|
||||
|
||||
gst_event_parse_tag (*event, &tags);
|
||||
|
||||
if (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_STREAM)
|
||||
return TRUE;
|
||||
|
||||
if (!gst_tag_list_get_uint (tags, GST_TAG_MAXIMUM_BITRATE,
|
||||
&bitrate) || bitrate == 0)
|
||||
if (!gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
|
||||
bitrate == 0)
|
||||
return TRUE;
|
||||
|
||||
/* set bandwidth (kbits/s) */
|
||||
gst_sdp_media_add_bandwidth (media, GST_SDP_BWTYPE_AS, bitrate / 1000);
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_sdp_from_tags (GstRTSPStream * stream, GstSDPMedia * stream_media)
|
||||
{
|
||||
GstPad *src_pad;
|
||||
|
||||
src_pad = gst_rtsp_stream_get_srcpad (stream);
|
||||
if (!src_pad)
|
||||
return;
|
||||
|
||||
gst_pad_sticky_events_foreach (src_pad, get_info_from_tags, stream_media);
|
||||
|
||||
gst_object_unref (src_pad);
|
||||
}
|
||||
|
||||
static guint
|
||||
get_roc_from_stats (GstStructure * stats, guint ssrc)
|
||||
{
|
||||
const GValue *va, *v;
|
||||
guint i, len;
|
||||
/* initialize roc to something different than 0, so if we don't get
|
||||
the proper ROC from the encoder, streaming should fail initially. */
|
||||
guint roc = -1;
|
||||
|
||||
va = gst_structure_get_value (stats, "streams");
|
||||
if (!va || !G_VALUE_HOLDS (va, GST_TYPE_ARRAY)) {
|
||||
GST_WARNING ("stats doesn't have a valid 'streams' field");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = gst_value_array_get_size (va);
|
||||
|
||||
/* look if there's any SSRC that matches. */
|
||||
for (i = 0; i < len; i++) {
|
||||
GstStructure *stream;
|
||||
v = gst_value_array_get_value (va, i);
|
||||
if (v && (stream = g_value_get_boxed (v))) {
|
||||
guint stream_ssrc;
|
||||
gst_structure_get_uint (stream, "ssrc", &stream_ssrc);
|
||||
if (stream_ssrc == ssrc) {
|
||||
gst_structure_get_uint (stream, "roc", &roc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roc;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mikey_add_crypto_sessions (GstRTSPStream * stream, GstMIKEYMessage * msg)
|
||||
{
|
||||
guint i;
|
||||
GObject *session;
|
||||
GstElement *encoder;
|
||||
GValueArray *sources;
|
||||
gboolean roc_found;
|
||||
|
||||
encoder = gst_rtsp_stream_get_srtp_encoder (stream);
|
||||
if (encoder == NULL) {
|
||||
GST_ERROR ("unable to get SRTP encoder from stream %p", stream);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
session = gst_rtsp_stream_get_rtpsession (stream);
|
||||
if (session == NULL) {
|
||||
GST_ERROR ("unable to get RTP session from stream %p", stream);
|
||||
gst_object_unref (encoder);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
roc_found = FALSE;
|
||||
g_object_get (session, "sources", &sources, NULL);
|
||||
for (i = 0; sources && (i < sources->n_values); i++) {
|
||||
GValue *val;
|
||||
GObject *source;
|
||||
guint32 ssrc;
|
||||
gboolean is_sender;
|
||||
|
||||
val = g_value_array_get_nth (sources, i);
|
||||
source = (GObject *) g_value_get_object (val);
|
||||
|
||||
g_object_get (source, "ssrc", &ssrc, "is-sender", &is_sender, NULL);
|
||||
|
||||
if (is_sender) {
|
||||
guint32 roc = -1;
|
||||
GstStructure *stats;
|
||||
|
||||
g_object_get (encoder, "stats", &stats, NULL);
|
||||
|
||||
if (stats) {
|
||||
roc = get_roc_from_stats (stats, ssrc);
|
||||
gst_structure_free (stats);
|
||||
}
|
||||
|
||||
roc_found = ! !(roc != -1);
|
||||
if (!roc_found) {
|
||||
GST_ERROR ("unable to obtain ROC for stream %p with SSRC %u",
|
||||
stream, ssrc);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
GST_INFO ("stream %p with SSRC %u has a ROC of %u", stream, ssrc, roc);
|
||||
|
||||
gst_mikey_message_add_cs_srtp (msg, 0, ssrc, roc);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
{
|
||||
g_value_array_free (sources);
|
||||
|
||||
gst_object_unref (encoder);
|
||||
g_object_unref (session);
|
||||
return roc_found;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_sdp_make_media:
|
||||
* @sdp: a #GstRTSPMessage
|
||||
* @info: a #GstSDPInfo
|
||||
* @stream: a #GstRTSPStream
|
||||
* @caps: a #GstCaps
|
||||
* @profile: a #GstRTSPProfile
|
||||
*
|
||||
* Creates a #GstSDPMedia from the parameters and stores it in @sdp.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info,
|
||||
GstRTSPStream * stream, GstCaps * caps, GstRTSPProfile profile)
|
||||
{
|
||||
GstSDPMedia *smedia;
|
||||
gchar *tmp;
|
||||
GstRTSPLowerTrans ltrans;
|
||||
GSocketFamily family;
|
||||
const gchar *addrtype, *proto;
|
||||
gchar *address;
|
||||
guint ttl;
|
||||
GstClockTime rtx_time;
|
||||
gchar *base64;
|
||||
GstMIKEYMessage *mikey_msg;
|
||||
|
||||
gst_sdp_media_new (&smedia);
|
||||
|
||||
if (gst_sdp_media_set_media_from_caps (caps, smedia) != GST_SDP_OK) {
|
||||
goto caps_error;
|
||||
}
|
||||
|
||||
gst_sdp_media_set_port_info (smedia, 0, 1);
|
||||
|
||||
switch (profile) {
|
||||
case GST_RTSP_PROFILE_AVP:
|
||||
proto = "RTP/AVP";
|
||||
break;
|
||||
case GST_RTSP_PROFILE_AVPF:
|
||||
proto = "RTP/AVPF";
|
||||
break;
|
||||
case GST_RTSP_PROFILE_SAVP:
|
||||
proto = "RTP/SAVP";
|
||||
break;
|
||||
case GST_RTSP_PROFILE_SAVPF:
|
||||
proto = "RTP/SAVPF";
|
||||
break;
|
||||
default:
|
||||
proto = "udp";
|
||||
break;
|
||||
}
|
||||
gst_sdp_media_set_proto (smedia, proto);
|
||||
|
||||
if (info->is_ipv6) {
|
||||
addrtype = "IP6";
|
||||
family = G_SOCKET_FAMILY_IPV6;
|
||||
} else {
|
||||
addrtype = "IP4";
|
||||
family = G_SOCKET_FAMILY_IPV4;
|
||||
}
|
||||
|
||||
ltrans = gst_rtsp_stream_get_protocols (stream);
|
||||
if (ltrans == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
|
||||
GstRTSPAddress *addr;
|
||||
|
||||
addr = gst_rtsp_stream_get_multicast_address (stream, family);
|
||||
if (addr == NULL)
|
||||
goto no_multicast;
|
||||
|
||||
address = g_strdup (addr->address);
|
||||
ttl = addr->ttl;
|
||||
gst_rtsp_address_free (addr);
|
||||
} else {
|
||||
ttl = 16;
|
||||
if (info->is_ipv6)
|
||||
address = g_strdup ("::");
|
||||
else
|
||||
address = g_strdup ("0.0.0.0");
|
||||
}
|
||||
|
||||
/* for the c= line */
|
||||
gst_sdp_media_add_connection (smedia, "IN", addrtype, address, ttl, 1);
|
||||
g_free (address);
|
||||
|
||||
/* the config uri */
|
||||
tmp = gst_rtsp_stream_get_control (stream);
|
||||
gst_sdp_media_add_attribute (smedia, "control", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
/* check for srtp */
|
||||
mikey_msg = gst_mikey_message_new_from_caps (caps);
|
||||
if (mikey_msg) {
|
||||
/* add policy '0' for all sending SSRC */
|
||||
if (!mikey_add_crypto_sessions (stream, mikey_msg)) {
|
||||
gst_mikey_message_unref (mikey_msg);
|
||||
goto crypto_sessions_error;
|
||||
}
|
||||
|
||||
base64 = gst_mikey_message_base64_encode (mikey_msg);
|
||||
if (base64) {
|
||||
tmp = g_strdup_printf ("mikey %s", base64);
|
||||
g_free (base64);
|
||||
gst_sdp_media_add_attribute (smedia, "key-mgmt", tmp);
|
||||
g_free (tmp);
|
||||
}
|
||||
|
||||
gst_mikey_message_unref (mikey_msg);
|
||||
}
|
||||
|
||||
/* RFC 7273 clock signalling */
|
||||
if (gst_rtsp_stream_is_sender (stream)) {
|
||||
GstBin *joined_bin = gst_rtsp_stream_get_joined_bin (stream);
|
||||
GstClock *clock = gst_element_get_clock (GST_ELEMENT_CAST (joined_bin));
|
||||
gchar *ts_refclk = NULL;
|
||||
gchar *mediaclk = NULL;
|
||||
guint rtptime, clock_rate;
|
||||
GstClockTime running_time, base_time, clock_time;
|
||||
GstRTSPPublishClockMode publish_clock_mode =
|
||||
gst_rtsp_stream_get_publish_clock_mode (stream);
|
||||
|
||||
if (!gst_rtsp_stream_get_rtpinfo (stream, &rtptime, NULL, &clock_rate,
|
||||
&running_time))
|
||||
goto clock_signalling_cleanup;
|
||||
base_time = gst_element_get_base_time (GST_ELEMENT_CAST (joined_bin));
|
||||
g_assert (base_time != GST_CLOCK_TIME_NONE);
|
||||
clock_time = running_time + base_time;
|
||||
|
||||
if (publish_clock_mode != GST_RTSP_PUBLISH_CLOCK_MODE_NONE && clock) {
|
||||
if (GST_IS_NTP_CLOCK (clock) || GST_IS_PTP_CLOCK (clock)) {
|
||||
if (publish_clock_mode == GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK_AND_OFFSET) {
|
||||
guint32 mediaclk_offset;
|
||||
|
||||
/* Calculate RTP time at the clock's epoch. That's the direct offset */
|
||||
clock_time =
|
||||
gst_util_uint64_scale (clock_time, clock_rate, GST_SECOND);
|
||||
|
||||
clock_time &= 0xffffffff;
|
||||
mediaclk_offset =
|
||||
G_GUINT64_CONSTANT (0xffffffff) + rtptime - clock_time;
|
||||
mediaclk = g_strdup_printf ("direct=%u", (guint32) mediaclk_offset);
|
||||
}
|
||||
|
||||
if (GST_IS_NTP_CLOCK (clock)) {
|
||||
gchar *ntp_address;
|
||||
guint ntp_port;
|
||||
|
||||
g_object_get (clock, "address", &ntp_address, "port", &ntp_port,
|
||||
NULL);
|
||||
|
||||
if (ntp_port == 123)
|
||||
ts_refclk = g_strdup_printf ("ntp=%s", ntp_address);
|
||||
else
|
||||
ts_refclk = g_strdup_printf ("ntp=%s:%u", ntp_address, ntp_port);
|
||||
|
||||
g_free (ntp_address);
|
||||
} else {
|
||||
guint64 ptp_clock_id;
|
||||
guint ptp_domain;
|
||||
|
||||
g_object_get (clock, "grandmaster-clock-id", &ptp_clock_id, "domain",
|
||||
&ptp_domain, NULL);
|
||||
|
||||
if (ptp_domain != 0)
|
||||
ts_refclk =
|
||||
g_strdup_printf
|
||||
("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X:%u",
|
||||
(guint) (ptp_clock_id >> 56) & 0xff,
|
||||
(guint) (ptp_clock_id >> 48) & 0xff,
|
||||
(guint) (ptp_clock_id >> 40) & 0xff,
|
||||
(guint) (ptp_clock_id >> 32) & 0xff,
|
||||
(guint) (ptp_clock_id >> 24) & 0xff,
|
||||
(guint) (ptp_clock_id >> 16) & 0xff,
|
||||
(guint) (ptp_clock_id >> 8) & 0xff,
|
||||
(guint) (ptp_clock_id >> 0) & 0xff, ptp_domain);
|
||||
else
|
||||
ts_refclk =
|
||||
g_strdup_printf
|
||||
("ptp=IEEE1588-2008:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
|
||||
(guint) (ptp_clock_id >> 56) & 0xff,
|
||||
(guint) (ptp_clock_id >> 48) & 0xff,
|
||||
(guint) (ptp_clock_id >> 40) & 0xff,
|
||||
(guint) (ptp_clock_id >> 32) & 0xff,
|
||||
(guint) (ptp_clock_id >> 24) & 0xff,
|
||||
(guint) (ptp_clock_id >> 16) & 0xff,
|
||||
(guint) (ptp_clock_id >> 8) & 0xff,
|
||||
(guint) (ptp_clock_id >> 0) & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
clock_signalling_cleanup:
|
||||
if (clock)
|
||||
gst_object_unref (clock);
|
||||
|
||||
if (!ts_refclk)
|
||||
ts_refclk = g_strdup ("local");
|
||||
if (!mediaclk)
|
||||
mediaclk = g_strdup ("sender");
|
||||
|
||||
gst_sdp_media_add_attribute (smedia, "ts-refclk", ts_refclk);
|
||||
gst_sdp_media_add_attribute (smedia, "mediaclk", mediaclk);
|
||||
g_free (ts_refclk);
|
||||
g_free (mediaclk);
|
||||
gst_object_unref (joined_bin);
|
||||
}
|
||||
|
||||
update_sdp_from_tags (stream, smedia);
|
||||
|
||||
if (profile == GST_RTSP_PROFILE_AVPF || profile == GST_RTSP_PROFILE_SAVPF) {
|
||||
if ((rtx_time = gst_rtsp_stream_get_retransmission_time (stream))) {
|
||||
/* ssrc multiplexed retransmit functionality */
|
||||
guint rtx_pt = gst_rtsp_stream_get_retransmission_pt (stream);
|
||||
|
||||
if (rtx_pt == 0) {
|
||||
g_warning ("failed to find an available dynamic payload type. "
|
||||
"Not adding retransmission");
|
||||
} else {
|
||||
gchar *tmp;
|
||||
GstStructure *s;
|
||||
gint caps_pt, caps_rate;
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
if (s == NULL)
|
||||
goto no_caps_info;
|
||||
|
||||
/* get payload type and clock rate */
|
||||
gst_structure_get_int (s, "payload", &caps_pt);
|
||||
gst_structure_get_int (s, "clock-rate", &caps_rate);
|
||||
|
||||
tmp = g_strdup_printf ("%d", rtx_pt);
|
||||
gst_sdp_media_add_format (smedia, tmp);
|
||||
g_free (tmp);
|
||||
|
||||
tmp = g_strdup_printf ("%d rtx/%d", rtx_pt, caps_rate);
|
||||
gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
tmp =
|
||||
g_strdup_printf ("%d apt=%d;rtx-time=%" G_GINT64_FORMAT, rtx_pt,
|
||||
caps_pt, GST_TIME_AS_MSECONDS (rtx_time));
|
||||
gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (gst_rtsp_stream_get_ulpfec_percentage (stream)) {
|
||||
guint ulpfec_pt = gst_rtsp_stream_get_ulpfec_pt (stream);
|
||||
|
||||
if (ulpfec_pt == 0) {
|
||||
g_warning ("failed to find an available dynamic payload type. "
|
||||
"Not adding ulpfec");
|
||||
} else {
|
||||
gchar *tmp;
|
||||
GstStructure *s;
|
||||
gint caps_pt, caps_rate;
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
if (s == NULL)
|
||||
goto no_caps_info;
|
||||
|
||||
/* get payload type and clock rate */
|
||||
gst_structure_get_int (s, "payload", &caps_pt);
|
||||
gst_structure_get_int (s, "clock-rate", &caps_rate);
|
||||
|
||||
tmp = g_strdup_printf ("%d", ulpfec_pt);
|
||||
gst_sdp_media_add_format (smedia, tmp);
|
||||
g_free (tmp);
|
||||
|
||||
tmp = g_strdup_printf ("%d ulpfec/%d", ulpfec_pt, caps_rate);
|
||||
gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
|
||||
g_free (tmp);
|
||||
|
||||
tmp = g_strdup_printf ("%d apt=%d", ulpfec_pt, caps_pt);
|
||||
gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gst_sdp_message_add_media (sdp, smedia);
|
||||
gst_sdp_media_free (smedia);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
caps_error:
|
||||
{
|
||||
gst_sdp_media_free (smedia);
|
||||
GST_ERROR ("unable to set media from caps for stream %d",
|
||||
gst_rtsp_stream_get_index (stream));
|
||||
return FALSE;
|
||||
}
|
||||
no_multicast:
|
||||
{
|
||||
gst_sdp_media_free (smedia);
|
||||
GST_ERROR ("stream %d has no multicast address",
|
||||
gst_rtsp_stream_get_index (stream));
|
||||
return FALSE;
|
||||
}
|
||||
no_caps_info:
|
||||
{
|
||||
gst_sdp_media_free (smedia);
|
||||
GST_ERROR ("caps for stream %d have no structure",
|
||||
gst_rtsp_stream_get_index (stream));
|
||||
return FALSE;
|
||||
}
|
||||
crypto_sessions_error:
|
||||
{
|
||||
gst_sdp_media_free (smedia);
|
||||
GST_ERROR ("unable to add MIKEY crypto sessions for stream %d",
|
||||
gst_rtsp_stream_get_index (stream));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_sdp_from_media:
|
||||
* @sdp: a #GstSDPMessage
|
||||
* @info: (transfer none): a #GstSDPInfo
|
||||
* @media: (transfer none): a #GstRTSPMedia
|
||||
*
|
||||
* Add @media specific info to @sdp. @info is used to configure the connection
|
||||
* information in the SDP.
|
||||
*
|
||||
* Returns: TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_sdp_from_media (GstSDPMessage * sdp, GstSDPInfo * info,
|
||||
GstRTSPMedia * media)
|
||||
{
|
||||
guint i, n_streams;
|
||||
gchar *rangestr;
|
||||
gboolean res;
|
||||
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
|
||||
rangestr = gst_rtsp_media_get_range_string (media, FALSE, GST_RTSP_RANGE_NPT);
|
||||
if (rangestr == NULL)
|
||||
goto not_prepared;
|
||||
|
||||
gst_sdp_message_add_attribute (sdp, "range", rangestr);
|
||||
g_free (rangestr);
|
||||
|
||||
res = TRUE;
|
||||
for (i = 0; res && (i < n_streams); i++) {
|
||||
GstRTSPStream *stream;
|
||||
|
||||
stream = gst_rtsp_media_get_stream (media, i);
|
||||
res = gst_rtsp_sdp_from_stream (sdp, info, stream);
|
||||
if (!res) {
|
||||
GST_ERROR ("could not get SDP from stream %p", stream);
|
||||
goto sdp_error;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
GstNetTimeProvider *provider;
|
||||
|
||||
if ((provider =
|
||||
gst_rtsp_media_get_time_provider (media, info->server_ip, 0))) {
|
||||
GstClock *clock;
|
||||
gchar *address, *str;
|
||||
gint port;
|
||||
|
||||
g_object_get (provider, "clock", &clock, "address", &address, "port",
|
||||
&port, NULL);
|
||||
|
||||
str = g_strdup_printf ("GstNetTimeProvider %s %s:%d %" G_GUINT64_FORMAT,
|
||||
g_type_name (G_TYPE_FROM_INSTANCE (clock)), address, port,
|
||||
gst_clock_get_time (clock));
|
||||
|
||||
gst_sdp_message_add_attribute (sdp, "x-gst-clock", str);
|
||||
g_free (str);
|
||||
gst_object_unref (clock);
|
||||
g_free (address);
|
||||
gst_object_unref (provider);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
/* ERRORS */
|
||||
not_prepared:
|
||||
{
|
||||
GST_ERROR ("media %p is not prepared", media);
|
||||
return FALSE;
|
||||
}
|
||||
sdp_error:
|
||||
{
|
||||
GST_ERROR ("could not get SDP from media %p", media);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_sdp_from_stream:
|
||||
* @sdp: a #GstSDPMessage
|
||||
* @info: (transfer none): a #GstSDPInfo
|
||||
* @stream: (transfer none): a #GstRTSPStream
|
||||
*
|
||||
* Add info from @stream to @sdp.
|
||||
*
|
||||
* Returns: TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_sdp_from_stream (GstSDPMessage * sdp, GstSDPInfo * info,
|
||||
GstRTSPStream * stream)
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstRTSPProfile profiles;
|
||||
guint mask;
|
||||
gboolean res;
|
||||
|
||||
caps = gst_rtsp_stream_get_caps (stream);
|
||||
|
||||
if (caps == NULL) {
|
||||
GST_ERROR ("stream %p has no caps", stream);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* make a new media for each profile */
|
||||
profiles = gst_rtsp_stream_get_profiles (stream);
|
||||
mask = 1;
|
||||
res = TRUE;
|
||||
while (res && (profiles >= mask)) {
|
||||
GstRTSPProfile prof = profiles & mask;
|
||||
|
||||
if (prof)
|
||||
res = gst_rtsp_sdp_make_media (sdp, info, stream, caps, prof);
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
gst_caps_unref (caps);
|
||||
|
||||
return res;
|
||||
}
|
49
gst/rtsp-server/rtsp-sdp.h
Normal file
49
gst/rtsp-server/rtsp-sdp.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/sdp/gstsdpmessage.h>
|
||||
|
||||
#include "rtsp-media.h"
|
||||
|
||||
#ifndef __GST_RTSP_SDP_H__
|
||||
#define __GST_RTSP_SDP_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct {
|
||||
gboolean is_ipv6;
|
||||
const gchar *server_ip;
|
||||
} GstSDPInfo;
|
||||
|
||||
/* creating SDP */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_sdp_from_media (GstSDPMessage *sdp, GstSDPInfo *info, GstRTSPMedia * media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_sdp_from_stream (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean
|
||||
gst_rtsp_sdp_make_media (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPStream * stream, GstCaps * caps, GstRTSPProfile profile);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SDP_H__ */
|
66
gst/rtsp-server/rtsp-server-internal.h
Normal file
66
gst/rtsp-server/rtsp-server-internal.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Mathieu Duponchelle <mathieu@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_SERVER_INTERNAL_H__
|
||||
#define __GST_RTSP_SERVER_INTERNAL_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include "rtsp-stream-transport.h"
|
||||
|
||||
/* Internal GstRTSPStreamTransport interface */
|
||||
|
||||
typedef gboolean (*GstRTSPBackPressureFunc) (guint8 channel, gpointer user_data);
|
||||
|
||||
gboolean gst_rtsp_stream_transport_backlog_push (GstRTSPStreamTransport *trans,
|
||||
GstBuffer *buffer,
|
||||
GstBufferList *buffer_list,
|
||||
gboolean is_rtp);
|
||||
|
||||
gboolean gst_rtsp_stream_transport_backlog_pop (GstRTSPStreamTransport *trans,
|
||||
GstBuffer **buffer,
|
||||
GstBufferList **buffer_list,
|
||||
gboolean *is_rtp);
|
||||
|
||||
gboolean gst_rtsp_stream_transport_backlog_is_empty (GstRTSPStreamTransport *trans);
|
||||
|
||||
void gst_rtsp_stream_transport_clear_backlog (GstRTSPStreamTransport * trans);
|
||||
|
||||
void gst_rtsp_stream_transport_lock_backlog (GstRTSPStreamTransport * trans);
|
||||
|
||||
void gst_rtsp_stream_transport_unlock_backlog (GstRTSPStreamTransport * trans);
|
||||
|
||||
void gst_rtsp_stream_transport_set_back_pressure_callback (GstRTSPStreamTransport *trans,
|
||||
GstRTSPBackPressureFunc back_pressure_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
gboolean gst_rtsp_stream_transport_check_back_pressure (GstRTSPStreamTransport *trans,
|
||||
gboolean is_rtp);
|
||||
|
||||
gboolean gst_rtsp_stream_is_tcp_receiver (GstRTSPStream * stream);
|
||||
|
||||
void gst_rtsp_media_set_enable_rtcp (GstRTSPMedia *media, gboolean enable);
|
||||
void gst_rtsp_stream_set_enable_rtcp (GstRTSPStream *stream, gboolean enable);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SERVER_INTERNAL_H__ */
|
211
gst/rtsp-server/rtsp-server-object.h
Normal file
211
gst/rtsp-server/rtsp-server-object.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_SERVER_OBJECT_H__
|
||||
#define __GST_RTSP_SERVER_OBJECT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstRTSPServer GstRTSPServer;
|
||||
typedef struct _GstRTSPServerClass GstRTSPServerClass;
|
||||
typedef struct _GstRTSPServerPrivate GstRTSPServerPrivate;
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
#include "rtsp-session-pool.h"
|
||||
#include "rtsp-session.h"
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-stream.h"
|
||||
#include "rtsp-stream-transport.h"
|
||||
#include "rtsp-address-pool.h"
|
||||
#include "rtsp-thread-pool.h"
|
||||
#include "rtsp-client.h"
|
||||
#include "rtsp-context.h"
|
||||
#include "rtsp-mount-points.h"
|
||||
#include "rtsp-media-factory.h"
|
||||
#include "rtsp-permissions.h"
|
||||
#include "rtsp-auth.h"
|
||||
#include "rtsp-token.h"
|
||||
#include "rtsp-session-media.h"
|
||||
#include "rtsp-sdp.h"
|
||||
#include "rtsp-media-factory-uri.h"
|
||||
#include "rtsp-params.h"
|
||||
|
||||
#define GST_TYPE_RTSP_SERVER (gst_rtsp_server_get_type ())
|
||||
#define GST_IS_RTSP_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SERVER))
|
||||
#define GST_IS_RTSP_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SERVER))
|
||||
#define GST_RTSP_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SERVER, GstRTSPServerClass))
|
||||
#define GST_RTSP_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SERVER, GstRTSPServer))
|
||||
#define GST_RTSP_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SERVER, GstRTSPServerClass))
|
||||
#define GST_RTSP_SERVER_CAST(obj) ((GstRTSPServer*)(obj))
|
||||
#define GST_RTSP_SERVER_CLASS_CAST(klass) ((GstRTSPServerClass*)(klass))
|
||||
|
||||
/**
|
||||
* GstRTSPServer:
|
||||
*
|
||||
* This object listens on a port, creates and manages the clients connected to
|
||||
* it.
|
||||
*/
|
||||
struct _GstRTSPServer {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPServerPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPServerClass:
|
||||
* @create_client: Create, configure a new GstRTSPClient
|
||||
* object that handles the new connection on @socket. The default
|
||||
* implementation will create a GstRTSPClient and will configure the
|
||||
* mount-points, auth, session-pool and thread-pool on the client.
|
||||
* @client_connected: emitted when a new client connected.
|
||||
*
|
||||
* The RTSP server class structure
|
||||
*/
|
||||
struct _GstRTSPServerClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
GstRTSPClient * (*create_client) (GstRTSPServer *server);
|
||||
|
||||
/* signals */
|
||||
void (*client_connected) (GstRTSPServer *server, GstRTSPClient *client);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_server_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPServer * gst_rtsp_server_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_address (GstRTSPServer *server, const gchar *address);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_server_get_address (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_service (GstRTSPServer *server, const gchar *service);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_server_get_service (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_backlog (GstRTSPServer *server, gint backlog);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_server_get_backlog (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
int gst_rtsp_server_get_bound_port (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_session_pool (GstRTSPServer *server, GstRTSPSessionPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionPool * gst_rtsp_server_get_session_pool (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_mount_points (GstRTSPServer *server, GstRTSPMountPoints *mounts);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMountPoints * gst_rtsp_server_get_mount_points (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_content_length_limit (GstRTSPServer * server, guint limit);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_server_get_content_length_limit (GstRTSPServer * server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_auth (GstRTSPServer *server, GstRTSPAuth *auth);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAuth * gst_rtsp_server_get_auth (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_server_set_thread_pool (GstRTSPServer *server, GstRTSPThreadPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPThreadPool * gst_rtsp_server_get_thread_pool (GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_server_transfer_connection (GstRTSPServer * server, GSocket *socket,
|
||||
const gchar * ip, gint port,
|
||||
const gchar *initial_buffer);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_server_io_func (GSocket *socket, GIOCondition condition,
|
||||
GstRTSPServer *server);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSocket * gst_rtsp_server_create_socket (GstRTSPServer *server,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSource * gst_rtsp_server_create_source (GstRTSPServer *server,
|
||||
GCancellable * cancellable,
|
||||
GError **error);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_server_attach (GstRTSPServer *server,
|
||||
GMainContext *context);
|
||||
|
||||
/**
|
||||
* GstRTSPServerClientFilterFunc:
|
||||
* @server: a #GstRTSPServer object
|
||||
* @client: a #GstRTSPClient in @server
|
||||
* @user_data: user data that has been given to gst_rtsp_server_client_filter()
|
||||
*
|
||||
* This function will be called by the gst_rtsp_server_client_filter(). An
|
||||
* implementation should return a value of #GstRTSPFilterResult.
|
||||
*
|
||||
* When this function returns #GST_RTSP_FILTER_REMOVE, @client will be removed
|
||||
* from @server.
|
||||
*
|
||||
* A return value of #GST_RTSP_FILTER_KEEP will leave @client untouched in
|
||||
* @server.
|
||||
*
|
||||
* A value of #GST_RTSP_FILTER_REF will add @client to the result #GList of
|
||||
* gst_rtsp_server_client_filter().
|
||||
*
|
||||
* Returns: a #GstRTSPFilterResult.
|
||||
*/
|
||||
typedef GstRTSPFilterResult (*GstRTSPServerClientFilterFunc) (GstRTSPServer *server,
|
||||
GstRTSPClient *client,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GList * gst_rtsp_server_client_filter (GstRTSPServer *server,
|
||||
GstRTSPServerClientFilterFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPServer, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SERVER_OBJECT_H__ */
|
44
gst/rtsp-server/rtsp-server-prelude.h
Normal file
44
gst/rtsp-server/rtsp-server-prelude.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* GStreamer RtspServer Library
|
||||
* Copyright (C) 2018 GStreamer developers
|
||||
*
|
||||
* rtspserver-prelude.h: prelude include header for gst-rtspserver library
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_SERVER_PRELUDE_H__
|
||||
#define __GST_RTSP_SERVER_PRELUDE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef GST_RTSP_SERVER_API
|
||||
# ifdef BUILDING_GST_RTSP_SERVER
|
||||
# define GST_RTSP_SERVER_API GST_API_EXPORT /* from config.h */
|
||||
# else
|
||||
# define GST_RTSP_SERVER_API GST_API_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Do *not* use these defines outside of rtsp-server. Use G_DEPRECATED instead. */
|
||||
#ifdef GST_DISABLE_DEPRECATED
|
||||
#define GST_RTSP_SERVER_DEPRECATED GST_RTSP_SERVER_API
|
||||
#define GST_RTSP_SERVER_DEPRECATED_FOR(f) GST_RTSP_SERVER_API
|
||||
#else
|
||||
#define GST_RTSP_SERVER_DEPRECATED G_DEPRECATED GST_RTSP_SERVER_API
|
||||
#define GST_RTSP_SERVER_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) GST_RTSP_SERVER_API
|
||||
#endif
|
||||
|
||||
#endif /* __GST_RTSP_SERVER_PRELUDE_H__ */
|
1520
gst/rtsp-server/rtsp-server.c
Normal file
1520
gst/rtsp-server/rtsp-server.c
Normal file
File diff suppressed because it is too large
Load diff
56
gst/rtsp-server/rtsp-server.h
Normal file
56
gst/rtsp-server/rtsp-server.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTSP_SERVER_H__
|
||||
#define __GST_RTSP_SERVER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
#include "rtsp-server-object.h"
|
||||
#include "rtsp-session-pool.h"
|
||||
#include "rtsp-session.h"
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-stream.h"
|
||||
#include "rtsp-stream-transport.h"
|
||||
#include "rtsp-address-pool.h"
|
||||
#include "rtsp-thread-pool.h"
|
||||
#include "rtsp-client.h"
|
||||
#include "rtsp-context.h"
|
||||
#include "rtsp-server.h"
|
||||
#include "rtsp-mount-points.h"
|
||||
#include "rtsp-media-factory.h"
|
||||
#include "rtsp-permissions.h"
|
||||
#include "rtsp-auth.h"
|
||||
#include "rtsp-token.h"
|
||||
#include "rtsp-session-media.h"
|
||||
#include "rtsp-sdp.h"
|
||||
#include "rtsp-media-factory-uri.h"
|
||||
#include "rtsp-params.h"
|
||||
|
||||
#include "rtsp-onvif-client.h"
|
||||
#include "rtsp-onvif-media-factory.h"
|
||||
#include "rtsp-onvif-media.h"
|
||||
#include "rtsp-onvif-server.h"
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SERVER_H__ */
|
544
gst/rtsp-server/rtsp-session-media.c
Normal file
544
gst/rtsp-server/rtsp-session-media.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-session-media
|
||||
* @short_description: Media managed in a session
|
||||
* @see_also: #GstRTSPMedia, #GstRTSPSession
|
||||
*
|
||||
* The #GstRTSPSessionMedia object manages a #GstRTSPMedia with a given path.
|
||||
*
|
||||
* With gst_rtsp_session_media_get_transport() and
|
||||
* gst_rtsp_session_media_set_transport() the transports of a #GstRTSPStream of
|
||||
* the managed #GstRTSPMedia can be retrieved and configured.
|
||||
*
|
||||
* Use gst_rtsp_session_media_set_state() to control the media state and
|
||||
* transports.
|
||||
*
|
||||
* Last reviewed on 2013-07-16 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-session.h"
|
||||
|
||||
struct _GstRTSPSessionMediaPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
gchar *path; /* unmutable */
|
||||
gint path_len; /* unmutable */
|
||||
GstRTSPMedia *media; /* unmutable */
|
||||
GstRTSPState state; /* protected by lock */
|
||||
guint counter; /* protected by lock */
|
||||
|
||||
GPtrArray *transports; /* protected by lock */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_session_media_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_session_media_debug
|
||||
|
||||
static void gst_rtsp_session_media_finalize (GObject * obj);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSessionMedia, gst_rtsp_session_media,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_rtsp_session_media_class_init (GstRTSPSessionMediaClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_rtsp_session_media_finalize;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_session_media_debug, "rtspsessionmedia", 0,
|
||||
"GstRTSPSessionMedia");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_media_init (GstRTSPSessionMedia * media)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
|
||||
media->priv = priv = gst_rtsp_session_media_get_instance_private (media);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
priv->state = GST_RTSP_STATE_INIT;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_media_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPSessionMedia *media;
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
|
||||
media = GST_RTSP_SESSION_MEDIA (obj);
|
||||
priv = media->priv;
|
||||
|
||||
GST_INFO ("free session media %p", media);
|
||||
|
||||
gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
|
||||
|
||||
gst_rtsp_media_unprepare (priv->media);
|
||||
|
||||
g_ptr_array_unref (priv->transports);
|
||||
|
||||
g_free (priv->path);
|
||||
g_object_unref (priv->media);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_session_media_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
free_session_media (gpointer data)
|
||||
{
|
||||
if (data)
|
||||
g_object_unref (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_new:
|
||||
* @path: the path
|
||||
* @media: (transfer full): the #GstRTSPMedia
|
||||
*
|
||||
* Create a new #GstRTSPSessionMedia that manages the streams
|
||||
* in @media for @path. @media should be prepared.
|
||||
*
|
||||
* Ownership is taken of @media.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPSessionMedia.
|
||||
*/
|
||||
GstRTSPSessionMedia *
|
||||
gst_rtsp_session_media_new (const gchar * path, GstRTSPMedia * media)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GstRTSPSessionMedia *result;
|
||||
guint n_streams;
|
||||
GstRTSPMediaStatus status;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
|
||||
|
||||
status = gst_rtsp_media_get_status (media);
|
||||
g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
|
||||
GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_SESSION_MEDIA, NULL);
|
||||
priv = result->priv;
|
||||
|
||||
priv->path = g_strdup (path);
|
||||
priv->path_len = strlen (path);
|
||||
priv->media = media;
|
||||
|
||||
/* prealloc the streams now, filled with NULL */
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
priv->transports = g_ptr_array_new_full (n_streams, free_session_media);
|
||||
g_ptr_array_set_size (priv->transports, n_streams);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_matches:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @path: a path
|
||||
* @matched: (out): the amount of matched characters of @path
|
||||
*
|
||||
* Check if the path of @media matches @path. It @path matches, the amount of
|
||||
* matched characters is returned in @matched.
|
||||
*
|
||||
* Returns: %TRUE when @path matches the path of @media.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_media_matches (GstRTSPSessionMedia * media,
|
||||
const gchar * path, gint * matched)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
gint len;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
|
||||
g_return_val_if_fail (path != NULL, FALSE);
|
||||
g_return_val_if_fail (matched != NULL, FALSE);
|
||||
|
||||
priv = media->priv;
|
||||
len = strlen (path);
|
||||
|
||||
/* path needs to be smaller than the media path */
|
||||
if (len < priv->path_len)
|
||||
return FALSE;
|
||||
|
||||
/* special case when "/" is the entire path */
|
||||
if (priv->path_len == 1 && priv->path[0] == '/' && path[0] == '/') {
|
||||
*matched = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if media path is larger, it there should be a / following the path */
|
||||
if (len > priv->path_len && path[priv->path_len] != '/')
|
||||
return FALSE;
|
||||
|
||||
*matched = priv->path_len;
|
||||
|
||||
return strncmp (path, priv->path, priv->path_len) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_media:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
*
|
||||
* Get the #GstRTSPMedia that was used when constructing @media
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the #GstRTSPMedia of @media.
|
||||
* Remains valid as long as @media is valid.
|
||||
*/
|
||||
GstRTSPMedia *
|
||||
gst_rtsp_session_media_get_media (GstRTSPSessionMedia * media)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
|
||||
|
||||
return media->priv->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_base_time:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
*
|
||||
* Get the base_time of the #GstRTSPMedia in @media
|
||||
*
|
||||
* Returns: the base_time of the media.
|
||||
*/
|
||||
GstClockTime
|
||||
gst_rtsp_session_media_get_base_time (GstRTSPSessionMedia * media)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), GST_CLOCK_TIME_NONE);
|
||||
|
||||
return gst_rtsp_media_get_base_time (media->priv->media);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_rtpinfo:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
*
|
||||
* Retrieve the RTP-Info header string for all streams in @media
|
||||
* with configured transports.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): The RTP-Info as a string or
|
||||
* %NULL when no RTP-Info could be generated, g_free() after usage.
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_session_media_get_rtpinfo (GstRTSPSessionMedia * media)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GString *rtpinfo = NULL;
|
||||
GstRTSPStreamTransport *transport;
|
||||
GstRTSPStream *stream;
|
||||
guint i, n_streams;
|
||||
GstClockTime earliest = GST_CLOCK_TIME_NONE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
|
||||
|
||||
priv = media->priv;
|
||||
g_mutex_lock (&priv->lock);
|
||||
|
||||
if (gst_rtsp_media_get_status (priv->media) != GST_RTSP_MEDIA_STATUS_PREPARED)
|
||||
goto not_prepared;
|
||||
|
||||
n_streams = priv->transports->len;
|
||||
|
||||
/* first step, take lowest running-time from all streams */
|
||||
GST_LOG_OBJECT (media, "determining start time among %d transports",
|
||||
n_streams);
|
||||
|
||||
for (i = 0; i < n_streams; i++) {
|
||||
GstClockTime running_time;
|
||||
|
||||
transport = g_ptr_array_index (priv->transports, i);
|
||||
if (transport == NULL) {
|
||||
GST_DEBUG_OBJECT (media, "ignoring unconfigured transport %d", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream = gst_rtsp_stream_transport_get_stream (transport);
|
||||
if (!gst_rtsp_stream_is_sender (stream))
|
||||
continue;
|
||||
if (!gst_rtsp_stream_get_rtpinfo (stream, NULL, NULL, NULL, &running_time))
|
||||
continue;
|
||||
|
||||
GST_LOG_OBJECT (media, "running time of %d stream: %" GST_TIME_FORMAT, i,
|
||||
GST_TIME_ARGS (running_time));
|
||||
|
||||
if (!GST_CLOCK_TIME_IS_VALID (earliest)) {
|
||||
earliest = running_time;
|
||||
} else {
|
||||
earliest = MIN (earliest, running_time);
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (media, "media start time: %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (earliest));
|
||||
|
||||
/* next step, scale all rtptime of all streams to lowest running-time */
|
||||
GST_LOG_OBJECT (media, "collecting RTP info for %d transports", n_streams);
|
||||
|
||||
for (i = 0; i < n_streams; i++) {
|
||||
gchar *stream_rtpinfo;
|
||||
|
||||
transport = g_ptr_array_index (priv->transports, i);
|
||||
if (transport == NULL) {
|
||||
GST_DEBUG_OBJECT (media, "ignoring unconfigured transport %d", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream_rtpinfo =
|
||||
gst_rtsp_stream_transport_get_rtpinfo (transport, earliest);
|
||||
if (stream_rtpinfo == NULL) {
|
||||
GST_DEBUG_OBJECT (media, "ignoring unknown RTPInfo %d", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rtpinfo == NULL)
|
||||
rtpinfo = g_string_new ("");
|
||||
else
|
||||
g_string_append (rtpinfo, ", ");
|
||||
|
||||
g_string_append (rtpinfo, stream_rtpinfo);
|
||||
g_free (stream_rtpinfo);
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (rtpinfo == NULL) {
|
||||
GST_WARNING_OBJECT (media, "RTP info is empty");
|
||||
return NULL;
|
||||
}
|
||||
return g_string_free (rtpinfo, FALSE);
|
||||
|
||||
/* ERRORS */
|
||||
not_prepared:
|
||||
{
|
||||
g_mutex_unlock (&priv->lock);
|
||||
GST_ERROR_OBJECT (media, "media was not prepared");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_set_transport:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @stream: a #GstRTSPStream
|
||||
* @tr: (transfer full): a #GstRTSPTransport
|
||||
*
|
||||
* Configure the transport for @stream to @tr in @media.
|
||||
*
|
||||
* Returns: (transfer none): the new or updated #GstRTSPStreamTransport for @stream.
|
||||
*/
|
||||
GstRTSPStreamTransport *
|
||||
gst_rtsp_session_media_set_transport (GstRTSPSessionMedia * media,
|
||||
GstRTSPStream * stream, GstRTSPTransport * tr)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GstRTSPStreamTransport *result;
|
||||
guint idx;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
|
||||
g_return_val_if_fail (tr != NULL, NULL);
|
||||
priv = media->priv;
|
||||
idx = gst_rtsp_stream_get_index (stream);
|
||||
g_return_val_if_fail (idx < priv->transports->len, NULL);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_ptr_array_index (priv->transports, idx);
|
||||
if (result == NULL) {
|
||||
result = gst_rtsp_stream_transport_new (stream, tr);
|
||||
g_ptr_array_index (priv->transports, idx) = result;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
} else {
|
||||
gst_rtsp_stream_transport_set_transport (result, tr);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_transport:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @idx: the stream index
|
||||
*
|
||||
* Get a previously created #GstRTSPStreamTransport for the stream at @idx.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): a #GstRTSPStreamTransport that is
|
||||
* valid until the session of @media is unreffed.
|
||||
*/
|
||||
GstRTSPStreamTransport *
|
||||
gst_rtsp_session_media_get_transport (GstRTSPSessionMedia * media, guint idx)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GstRTSPStreamTransport *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
|
||||
priv = media->priv;
|
||||
g_return_val_if_fail (idx < priv->transports->len, NULL);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_ptr_array_index (priv->transports, idx);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_transports:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
*
|
||||
* Get a list of all available #GstRTSPStreamTransport in this session.
|
||||
*
|
||||
* Returns: (transfer full) (element-type GstRTSPStreamTransport): a
|
||||
* list of #GstRTSPStreamTransport, g_ptr_array_unref () after usage.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
GPtrArray *
|
||||
gst_rtsp_session_media_get_transports (GstRTSPSessionMedia * media)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GPtrArray *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_ptr_array_ref (priv->transports);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_alloc_channels:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @range: (out): a #GstRTSPRange
|
||||
*
|
||||
* Fill @range with the next available min and max channels for
|
||||
* interleaved transport.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
|
||||
GstRTSPRange * range)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
range->min = priv->counter++;
|
||||
range->max = priv->counter++;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_set_state:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @state: the new state
|
||||
*
|
||||
* Tell the media object @media to change to @state.
|
||||
*
|
||||
* Returns: %TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
gboolean ret;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
ret = gst_rtsp_media_set_state (priv->media, state, priv->transports);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_set_rtsp_state:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
* @state: a #GstRTSPState
|
||||
*
|
||||
* Set the RTSP state of @media to @state.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_media_set_rtsp_state (GstRTSPSessionMedia * media,
|
||||
GstRTSPState state)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_SESSION_MEDIA (media));
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->state = state;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_get_rtsp_state:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
*
|
||||
* Get the current RTSP state of @media.
|
||||
*
|
||||
* Returns: the current RTSP state of @media.
|
||||
*/
|
||||
GstRTSPState
|
||||
gst_rtsp_session_media_get_rtsp_state (GstRTSPSessionMedia * media)
|
||||
{
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
GstRTSPState ret;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media),
|
||||
GST_RTSP_STATE_INVALID);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
ret = priv->state;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
123
gst/rtsp-server/rtsp-session-media.h
Normal file
123
gst/rtsp-server/rtsp-session-media.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp/gstrtsptransport.h>
|
||||
|
||||
#ifndef __GST_RTSP_SESSION_MEDIA_H__
|
||||
#define __GST_RTSP_SESSION_MEDIA_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_SESSION_MEDIA (gst_rtsp_session_media_get_type ())
|
||||
#define GST_IS_RTSP_SESSION_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SESSION_MEDIA))
|
||||
#define GST_IS_RTSP_SESSION_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SESSION_MEDIA))
|
||||
#define GST_RTSP_SESSION_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
|
||||
#define GST_RTSP_SESSION_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMedia))
|
||||
#define GST_RTSP_SESSION_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
|
||||
#define GST_RTSP_SESSION_MEDIA_CAST(obj) ((GstRTSPSessionMedia*)(obj))
|
||||
#define GST_RTSP_SESSION_MEDIA_CLASS_CAST(klass) ((GstRTSPSessionMediaClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
|
||||
typedef struct _GstRTSPSessionMediaClass GstRTSPSessionMediaClass;
|
||||
typedef struct _GstRTSPSessionMediaPrivate GstRTSPSessionMediaPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPSessionMedia:
|
||||
*
|
||||
* State of a client session regarding a specific media identified by path.
|
||||
*/
|
||||
struct _GstRTSPSessionMedia
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPSessionMediaPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct _GstRTSPSessionMediaClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_session_media_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionMedia * gst_rtsp_session_media_new (const gchar *path,
|
||||
GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_media_matches (GstRTSPSessionMedia *media,
|
||||
const gchar *path,
|
||||
gint * matched);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPMedia * gst_rtsp_session_media_get_media (GstRTSPSessionMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTime gst_rtsp_session_media_get_base_time (GstRTSPSessionMedia *media);
|
||||
/* control media */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_media_set_state (GstRTSPSessionMedia *media,
|
||||
GstState state);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_media_set_rtsp_state (GstRTSPSessionMedia *media,
|
||||
GstRTSPState state);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPState gst_rtsp_session_media_get_rtsp_state (GstRTSPSessionMedia *media);
|
||||
|
||||
/* get stream transport config */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStreamTransport * gst_rtsp_session_media_set_transport (GstRTSPSessionMedia *media,
|
||||
GstRTSPStream *stream,
|
||||
GstRTSPTransport *tr);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStreamTransport * gst_rtsp_session_media_get_transport (GstRTSPSessionMedia *media,
|
||||
guint idx);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GPtrArray * gst_rtsp_session_media_get_transports (GstRTSPSessionMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia *media,
|
||||
GstRTSPRange *range);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_session_media_get_rtpinfo (GstRTSPSessionMedia * media);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPSessionMedia, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SESSION_MEDIA_H__ */
|
766
gst/rtsp-server/rtsp-session-pool.c
Normal file
766
gst/rtsp-server/rtsp-session-pool.c
Normal file
|
@ -0,0 +1,766 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-session-pool
|
||||
* @short_description: An object for managing sessions
|
||||
* @see_also: #GstRTSPSession
|
||||
*
|
||||
* The #GstRTSPSessionPool object manages a list of #GstRTSPSession objects.
|
||||
*
|
||||
* The maximum number of sessions can be configured with
|
||||
* gst_rtsp_session_pool_set_max_sessions(). The current number of sessions can
|
||||
* be retrieved with gst_rtsp_session_pool_get_n_sessions().
|
||||
*
|
||||
* Use gst_rtsp_session_pool_create() to create a new #GstRTSPSession object.
|
||||
* The session object can be found again with its id and
|
||||
* gst_rtsp_session_pool_find().
|
||||
*
|
||||
* All sessions can be iterated with gst_rtsp_session_pool_filter().
|
||||
*
|
||||
* Run gst_rtsp_session_pool_cleanup() periodically to remove timed out sessions
|
||||
* or use gst_rtsp_session_pool_create_watch() to be notified when session
|
||||
* cleanup should be performed.
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "rtsp-session-pool.h"
|
||||
|
||||
struct _GstRTSPSessionPoolPrivate
|
||||
{
|
||||
GMutex lock; /* protects everything in this struct */
|
||||
guint max_sessions;
|
||||
GHashTable *sessions;
|
||||
guint sessions_cookie;
|
||||
};
|
||||
|
||||
#define DEFAULT_MAX_SESSIONS 0
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAX_SESSIONS,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static const gchar session_id_charset[] =
|
||||
{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
|
||||
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '-', '_', '.', '+' /* '$' Live555 in VLC strips off $ chars */
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_SESSION_REMOVED,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
static guint gst_rtsp_session_pool_signals[SIGNAL_LAST] = { 0 };
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_session_debug
|
||||
|
||||
static void gst_rtsp_session_pool_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_session_pool_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_session_pool_finalize (GObject * object);
|
||||
|
||||
static gchar *create_session_id (GstRTSPSessionPool * pool);
|
||||
static GstRTSPSession *create_session (GstRTSPSessionPool * pool,
|
||||
const gchar * id);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSessionPool, gst_rtsp_session_pool,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_rtsp_session_pool_class_init (GstRTSPSessionPoolClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gst_rtsp_session_pool_get_property;
|
||||
gobject_class->set_property = gst_rtsp_session_pool_set_property;
|
||||
gobject_class->finalize = gst_rtsp_session_pool_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_MAX_SESSIONS,
|
||||
g_param_spec_uint ("max-sessions", "Max Sessions",
|
||||
"the maximum amount of sessions (0 = unlimited)",
|
||||
0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED] =
|
||||
g_signal_new ("session-removed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPSessionPoolClass,
|
||||
session_removed), NULL, NULL, NULL, G_TYPE_NONE, 1,
|
||||
GST_TYPE_RTSP_SESSION);
|
||||
|
||||
klass->create_session_id = create_session_id;
|
||||
klass->create_session = create_session;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsessionpool", 0,
|
||||
"GstRTSPSessionPool");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_pool_init (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
|
||||
pool->priv = priv = gst_rtsp_session_pool_get_instance_private (pool);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
priv->sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL, g_object_unref);
|
||||
priv->max_sessions = DEFAULT_MAX_SESSIONS;
|
||||
}
|
||||
|
||||
static GstRTSPFilterResult
|
||||
remove_sessions_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
|
||||
gpointer user_data)
|
||||
{
|
||||
return GST_RTSP_FILTER_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_pool_finalize (GObject * object)
|
||||
{
|
||||
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
|
||||
GstRTSPSessionPoolPrivate *priv = pool->priv;
|
||||
|
||||
gst_rtsp_session_pool_filter (pool, remove_sessions_func, NULL);
|
||||
g_hash_table_unref (priv->sessions);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_pool_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_MAX_SESSIONS:
|
||||
g_value_set_uint (value, gst_rtsp_session_pool_get_max_sessions (pool));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_pool_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_MAX_SESSIONS:
|
||||
gst_rtsp_session_pool_set_max_sessions (pool, g_value_get_uint (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_new:
|
||||
*
|
||||
* Create a new #GstRTSPSessionPool instance.
|
||||
*
|
||||
* Returns: (transfer full): A new #GstRTSPSessionPool. g_object_unref() after
|
||||
* usage.
|
||||
*/
|
||||
GstRTSPSessionPool *
|
||||
gst_rtsp_session_pool_new (void)
|
||||
{
|
||||
GstRTSPSessionPool *result;
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_SESSION_POOL, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_set_max_sessions:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
* @max: the maximum number of sessions
|
||||
*
|
||||
* Configure the maximum allowed number of sessions in @pool to @max.
|
||||
* A value of 0 means an unlimited amount of sessions.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool * pool, guint max)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_SESSION_POOL (pool));
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->max_sessions = max;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_get_max_sessions:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
*
|
||||
* Get the maximum allowed number of sessions in @pool. 0 means an unlimited
|
||||
* amount of sessions.
|
||||
*
|
||||
* Returns: the maximum allowed number of sessions.
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
guint result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = priv->max_sessions;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_get_n_sessions:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
*
|
||||
* Get the amount of active sessions in @pool.
|
||||
*
|
||||
* Returns: the amount of active sessions in @pool.
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
guint result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_hash_table_size (priv->sessions);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_find:
|
||||
* @pool: the pool to search
|
||||
* @sessionid: the session id
|
||||
*
|
||||
* Find the session with @sessionid in @pool. The access time of the session
|
||||
* will be updated with gst_rtsp_session_touch().
|
||||
*
|
||||
* Returns: (transfer full) (nullable): the #GstRTSPSession with @sessionid
|
||||
* or %NULL when the session did not exist. g_object_unref() after usage.
|
||||
*/
|
||||
GstRTSPSession *
|
||||
gst_rtsp_session_pool_find (GstRTSPSessionPool * pool, const gchar * sessionid)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
GstRTSPSession *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
|
||||
g_return_val_if_fail (sessionid != NULL, NULL);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result = g_hash_table_lookup (priv->sessions, sessionid);
|
||||
if (result) {
|
||||
g_object_ref (result);
|
||||
gst_rtsp_session_touch (result);
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
create_session_id (GstRTSPSessionPool * pool)
|
||||
{
|
||||
gchar id[16];
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
id[i] =
|
||||
session_id_charset[g_random_int_range (0,
|
||||
G_N_ELEMENTS (session_id_charset))];
|
||||
}
|
||||
|
||||
return g_strndup (id, 16);
|
||||
}
|
||||
|
||||
static GstRTSPSession *
|
||||
create_session (GstRTSPSessionPool * pool, const gchar * id)
|
||||
{
|
||||
return gst_rtsp_session_new (id);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_create:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
*
|
||||
* Create a new #GstRTSPSession object in @pool.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a new #GstRTSPSession.
|
||||
*/
|
||||
GstRTSPSession *
|
||||
gst_rtsp_session_pool_create (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
GstRTSPSession *result = NULL;
|
||||
GstRTSPSessionPoolClass *klass;
|
||||
gchar *id = NULL;
|
||||
guint retry;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
klass = GST_RTSP_SESSION_POOL_GET_CLASS (pool);
|
||||
|
||||
retry = 0;
|
||||
do {
|
||||
/* start by creating a new random session id, we assume that this is random
|
||||
* enough to not cause a collision, which we will check later */
|
||||
if (klass->create_session_id)
|
||||
id = klass->create_session_id (pool);
|
||||
else
|
||||
goto no_function;
|
||||
|
||||
if (id == NULL)
|
||||
goto no_session;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
/* check session limit */
|
||||
if (priv->max_sessions > 0) {
|
||||
if (g_hash_table_size (priv->sessions) >= priv->max_sessions)
|
||||
goto too_many_sessions;
|
||||
}
|
||||
/* check if the sessionid existed */
|
||||
result = g_hash_table_lookup (priv->sessions, id);
|
||||
if (result) {
|
||||
/* found, retry with a different session id */
|
||||
result = NULL;
|
||||
retry++;
|
||||
if (retry > 100)
|
||||
goto collision;
|
||||
} else {
|
||||
/* not found, create session and insert it in the pool */
|
||||
if (klass->create_session)
|
||||
result = klass->create_session (pool, id);
|
||||
if (result == NULL)
|
||||
goto too_many_sessions;
|
||||
/* take additional ref for the pool */
|
||||
g_object_ref (result);
|
||||
g_hash_table_insert (priv->sessions,
|
||||
(gchar *) gst_rtsp_session_get_sessionid (result), result);
|
||||
priv->sessions_cookie++;
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
g_free (id);
|
||||
} while (result == NULL);
|
||||
|
||||
return result;
|
||||
|
||||
/* ERRORS */
|
||||
no_function:
|
||||
{
|
||||
GST_WARNING ("no create_session_id vmethod in GstRTSPSessionPool %p", pool);
|
||||
return NULL;
|
||||
}
|
||||
no_session:
|
||||
{
|
||||
GST_WARNING ("can't create session id with GstRTSPSessionPool %p", pool);
|
||||
return NULL;
|
||||
}
|
||||
collision:
|
||||
{
|
||||
GST_WARNING ("can't find unique sessionid for GstRTSPSessionPool %p", pool);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
g_free (id);
|
||||
return NULL;
|
||||
}
|
||||
too_many_sessions:
|
||||
{
|
||||
GST_WARNING ("session pool reached max sessions of %d", priv->max_sessions);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
g_free (id);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_remove:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
* @sess: (transfer none): a #GstRTSPSession
|
||||
*
|
||||
* Remove @sess from @pool, releasing the ref that the pool has on @sess.
|
||||
*
|
||||
* Returns: %TRUE if the session was found and removed.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_pool_remove (GstRTSPSessionPool * pool, GstRTSPSession * sess)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
gboolean found;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), FALSE);
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_object_ref (sess);
|
||||
found =
|
||||
g_hash_table_remove (priv->sessions,
|
||||
gst_rtsp_session_get_sessionid (sess));
|
||||
if (found)
|
||||
priv->sessions_cookie++;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (found)
|
||||
g_signal_emit (pool, gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED],
|
||||
0, sess);
|
||||
|
||||
g_object_unref (sess);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint64 now_monotonic_time;
|
||||
GstRTSPSessionPool *pool;
|
||||
GList *removed;
|
||||
} CleanupData;
|
||||
|
||||
static gboolean
|
||||
cleanup_func (gchar * sessionid, GstRTSPSession * sess, CleanupData * data)
|
||||
{
|
||||
gboolean expired;
|
||||
|
||||
expired = gst_rtsp_session_is_expired_usec (sess, data->now_monotonic_time);
|
||||
|
||||
if (expired) {
|
||||
GST_DEBUG ("session expired");
|
||||
data->removed = g_list_prepend (data->removed, g_object_ref (sess));
|
||||
}
|
||||
|
||||
return expired;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_cleanup:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
*
|
||||
* Inspect all the sessions in @pool and remove the sessions that are inactive
|
||||
* for more than their timeout.
|
||||
*
|
||||
* Returns: the amount of sessions that got removed.
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_session_pool_cleanup (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
guint result;
|
||||
CleanupData data;
|
||||
GList *walk;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
data.now_monotonic_time = g_get_monotonic_time ();
|
||||
|
||||
data.pool = pool;
|
||||
data.removed = NULL;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
result =
|
||||
g_hash_table_foreach_remove (priv->sessions, (GHRFunc) cleanup_func,
|
||||
&data);
|
||||
if (result > 0)
|
||||
priv->sessions_cookie++;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
for (walk = data.removed; walk; walk = walk->next) {
|
||||
GstRTSPSession *sess = walk->data;
|
||||
|
||||
g_signal_emit (pool,
|
||||
gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0, sess);
|
||||
|
||||
g_object_unref (sess);
|
||||
}
|
||||
g_list_free (data.removed);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_filter:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
* @func: (scope call) (allow-none): a callback
|
||||
* @user_data: (closure): user data passed to @func
|
||||
*
|
||||
* Call @func for each session in @pool. The result value of @func determines
|
||||
* what happens to the session. @func will be called with the session pool
|
||||
* locked so no further actions on @pool can be performed from @func.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_REMOVE, the session will be set to the
|
||||
* expired state and removed from @pool.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @pool.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_REF, the session will remain in @pool but
|
||||
* will also be added with an additional ref to the result GList of this
|
||||
* function..
|
||||
*
|
||||
* When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all sessions.
|
||||
*
|
||||
* Returns: (element-type GstRTSPSession) (transfer full): a GList with all
|
||||
* sessions for which @func returned #GST_RTSP_FILTER_REF. After usage, each
|
||||
* element in the GList should be unreffed before the list is freed.
|
||||
*/
|
||||
GList *
|
||||
gst_rtsp_session_pool_filter (GstRTSPSessionPool * pool,
|
||||
GstRTSPSessionPoolFilterFunc func, gpointer user_data)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
GList *result;
|
||||
GHashTable *visited;
|
||||
guint cookie;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
result = NULL;
|
||||
if (func)
|
||||
visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
restart:
|
||||
g_hash_table_iter_init (&iter, priv->sessions);
|
||||
cookie = priv->sessions_cookie;
|
||||
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
||||
GstRTSPSession *session = value;
|
||||
GstRTSPFilterResult res;
|
||||
gboolean changed;
|
||||
|
||||
if (func) {
|
||||
/* only visit each session once */
|
||||
if (g_hash_table_contains (visited, session))
|
||||
continue;
|
||||
|
||||
g_hash_table_add (visited, g_object_ref (session));
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
res = func (pool, session, user_data);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
} else
|
||||
res = GST_RTSP_FILTER_REF;
|
||||
|
||||
changed = (cookie != priv->sessions_cookie);
|
||||
|
||||
switch (res) {
|
||||
case GST_RTSP_FILTER_REMOVE:
|
||||
{
|
||||
gboolean removed = TRUE;
|
||||
|
||||
if (changed)
|
||||
/* something changed, check if we still have the session */
|
||||
removed = g_hash_table_remove (priv->sessions, key);
|
||||
else
|
||||
g_hash_table_iter_remove (&iter);
|
||||
|
||||
if (removed) {
|
||||
/* if we managed to remove the session, update the cookie and
|
||||
* signal */
|
||||
cookie = ++priv->sessions_cookie;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
g_signal_emit (pool,
|
||||
gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0,
|
||||
session);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
/* cookie could have changed again, make sure we restart */
|
||||
changed |= (cookie != priv->sessions_cookie);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_RTSP_FILTER_REF:
|
||||
/* keep ref */
|
||||
result = g_list_prepend (result, g_object_ref (session));
|
||||
break;
|
||||
case GST_RTSP_FILTER_KEEP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (changed)
|
||||
goto restart;
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (func)
|
||||
g_hash_table_unref (visited);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
GstRTSPSessionPool *pool;
|
||||
gint timeout;
|
||||
} GstPoolSource;
|
||||
|
||||
static void
|
||||
collect_timeout (gchar * sessionid, GstRTSPSession * sess, GstPoolSource * psrc)
|
||||
{
|
||||
gint timeout;
|
||||
gint64 now_monotonic_time;
|
||||
|
||||
now_monotonic_time = g_get_monotonic_time ();
|
||||
|
||||
timeout = gst_rtsp_session_next_timeout_usec (sess, now_monotonic_time);
|
||||
|
||||
GST_INFO ("%p: next timeout: %d", sess, timeout);
|
||||
if (psrc->timeout == -1 || timeout < psrc->timeout)
|
||||
psrc->timeout = timeout;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pool_source_prepare (GSource * source, gint * timeout)
|
||||
{
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
GstPoolSource *psrc;
|
||||
gboolean result;
|
||||
|
||||
psrc = (GstPoolSource *) source;
|
||||
psrc->timeout = -1;
|
||||
priv = psrc->pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_hash_table_foreach (priv->sessions, (GHFunc) collect_timeout, psrc);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (timeout)
|
||||
*timeout = psrc->timeout;
|
||||
|
||||
result = psrc->timeout == 0;
|
||||
|
||||
GST_INFO ("prepare %d, %d", psrc->timeout, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pool_source_check (GSource * source)
|
||||
{
|
||||
GST_INFO ("check");
|
||||
|
||||
return gst_pool_source_prepare (source, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_pool_source_dispatch (GSource * source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
gboolean res;
|
||||
GstPoolSource *psrc = (GstPoolSource *) source;
|
||||
GstRTSPSessionPoolFunc func = (GstRTSPSessionPoolFunc) callback;
|
||||
|
||||
GST_INFO ("dispatch");
|
||||
|
||||
if (func)
|
||||
res = func (psrc->pool, user_data);
|
||||
else
|
||||
res = FALSE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_pool_source_finalize (GSource * source)
|
||||
{
|
||||
GstPoolSource *psrc = (GstPoolSource *) source;
|
||||
|
||||
GST_INFO ("finalize %p", psrc);
|
||||
|
||||
g_object_unref (psrc->pool);
|
||||
psrc->pool = NULL;
|
||||
}
|
||||
|
||||
static GSourceFuncs gst_pool_source_funcs = {
|
||||
gst_pool_source_prepare,
|
||||
gst_pool_source_check,
|
||||
gst_pool_source_dispatch,
|
||||
gst_pool_source_finalize
|
||||
};
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_pool_create_watch:
|
||||
* @pool: a #GstRTSPSessionPool
|
||||
*
|
||||
* Create a #GSource that will be dispatched when the session should be cleaned
|
||||
* up.
|
||||
*
|
||||
* Returns: (transfer full): a #GSource
|
||||
*/
|
||||
GSource *
|
||||
gst_rtsp_session_pool_create_watch (GstRTSPSessionPool * pool)
|
||||
{
|
||||
GstPoolSource *source;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
|
||||
|
||||
source = (GstPoolSource *) g_source_new (&gst_pool_source_funcs,
|
||||
sizeof (GstPoolSource));
|
||||
source->pool = g_object_ref (pool);
|
||||
|
||||
return (GSource *) source;
|
||||
}
|
169
gst/rtsp-server/rtsp-session-pool.h
Normal file
169
gst/rtsp-server/rtsp-session-pool.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef __GST_RTSP_SESSION_POOL_H__
|
||||
#define __GST_RTSP_SESSION_POOL_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstRTSPSessionPool GstRTSPSessionPool;
|
||||
typedef struct _GstRTSPSessionPoolClass GstRTSPSessionPoolClass;
|
||||
typedef struct _GstRTSPSessionPoolPrivate GstRTSPSessionPoolPrivate;
|
||||
|
||||
#include "rtsp-session.h"
|
||||
|
||||
#define GST_TYPE_RTSP_SESSION_POOL (gst_rtsp_session_pool_get_type ())
|
||||
#define GST_IS_RTSP_SESSION_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SESSION_POOL))
|
||||
#define GST_IS_RTSP_SESSION_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SESSION_POOL))
|
||||
#define GST_RTSP_SESSION_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SESSION_POOL, GstRTSPSessionPoolClass))
|
||||
#define GST_RTSP_SESSION_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SESSION_POOL, GstRTSPSessionPool))
|
||||
#define GST_RTSP_SESSION_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SESSION_POOL, GstRTSPSessionPoolClass))
|
||||
#define GST_RTSP_SESSION_POOL_CAST(obj) ((GstRTSPSessionPool*)(obj))
|
||||
#define GST_RTSP_SESSION_POOL_CLASS_CAST(klass) ((GstRTSPSessionPoolClass*)(klass))
|
||||
|
||||
/**
|
||||
* GstRTSPSessionPool:
|
||||
*
|
||||
* An object that keeps track of the active sessions. This object is usually
|
||||
* attached to a #GstRTSPServer object to manage the sessions in that server.
|
||||
*/
|
||||
struct _GstRTSPSessionPool {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPSessionPoolPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPSessionPoolClass:
|
||||
* @create_session_id: create a new random session id. Subclasses can create
|
||||
* custom session ids and should not check if the session exists.
|
||||
* @create_session: make a new session object.
|
||||
* @session_removed: a session was removed from the pool
|
||||
*/
|
||||
struct _GstRTSPSessionPoolClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
gchar * (*create_session_id) (GstRTSPSessionPool *pool);
|
||||
GstRTSPSession * (*create_session) (GstRTSPSessionPool *pool, const gchar *id);
|
||||
|
||||
/* signals */
|
||||
void (*session_removed) (GstRTSPSessionPool *pool,
|
||||
GstRTSPSession *session);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPSessionPoolFunc:
|
||||
* @pool: a #GstRTSPSessionPool object
|
||||
* @user_data: user data that has been given when registering the handler
|
||||
*
|
||||
* The function that will be called from the GSource watch on the session pool.
|
||||
*
|
||||
* The function will be called when the pool must be cleaned up because one or
|
||||
* more sessions timed out.
|
||||
*
|
||||
* Returns: %FALSE if the source should be removed.
|
||||
*/
|
||||
typedef gboolean (*GstRTSPSessionPoolFunc) (GstRTSPSessionPool *pool, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPSessionPoolFilterFunc:
|
||||
* @pool: a #GstRTSPSessionPool object
|
||||
* @session: a #GstRTSPSession in @pool
|
||||
* @user_data: user data that has been given to gst_rtsp_session_pool_filter()
|
||||
*
|
||||
* This function will be called by the gst_rtsp_session_pool_filter(). An
|
||||
* implementation should return a value of #GstRTSPFilterResult.
|
||||
*
|
||||
* When this function returns #GST_RTSP_FILTER_REMOVE, @session will be removed
|
||||
* from @pool.
|
||||
*
|
||||
* A return value of #GST_RTSP_FILTER_KEEP will leave @session untouched in
|
||||
* @pool.
|
||||
*
|
||||
* A value of GST_RTSP_FILTER_REF will add @session to the result #GList of
|
||||
* gst_rtsp_session_pool_filter().
|
||||
*
|
||||
* Returns: a #GstRTSPFilterResult.
|
||||
*/
|
||||
typedef GstRTSPFilterResult (*GstRTSPSessionPoolFilterFunc) (GstRTSPSessionPool *pool,
|
||||
GstRTSPSession *session,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_session_pool_get_type (void);
|
||||
|
||||
/* creating a session pool */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionPool * gst_rtsp_session_pool_new (void);
|
||||
|
||||
/* counting sessions */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_pool_set_max_sessions (GstRTSPSessionPool *pool, guint max);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_session_pool_get_max_sessions (GstRTSPSessionPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_session_pool_get_n_sessions (GstRTSPSessionPool *pool);
|
||||
|
||||
/* managing sessions */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSession * gst_rtsp_session_pool_create (GstRTSPSessionPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSession * gst_rtsp_session_pool_find (GstRTSPSessionPool *pool,
|
||||
const gchar *sessionid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_pool_remove (GstRTSPSessionPool *pool,
|
||||
GstRTSPSession *sess);
|
||||
|
||||
/* perform session maintenance */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GList * gst_rtsp_session_pool_filter (GstRTSPSessionPool *pool,
|
||||
GstRTSPSessionPoolFilterFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_session_pool_cleanup (GstRTSPSessionPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSource * gst_rtsp_session_pool_create_watch (GstRTSPSessionPool *pool);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPSessionPool, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SESSION_POOL_H__ */
|
807
gst/rtsp-server/rtsp-session.c
Normal file
807
gst/rtsp-server/rtsp-session.c
Normal file
|
@ -0,0 +1,807 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-session
|
||||
* @short_description: An object to manage media
|
||||
* @see_also: #GstRTSPSessionPool, #GstRTSPSessionMedia, #GstRTSPMedia
|
||||
*
|
||||
* The #GstRTSPSession is identified by an id, unique in the
|
||||
* #GstRTSPSessionPool that created the session and manages media and its
|
||||
* configuration.
|
||||
*
|
||||
* A #GstRTSPSession has a timeout that can be retrieved with
|
||||
* gst_rtsp_session_get_timeout(). You can check if the sessions is expired with
|
||||
* gst_rtsp_session_is_expired(). gst_rtsp_session_touch() will reset the
|
||||
* expiration counter of the session.
|
||||
*
|
||||
* When a client configures a media with SETUP, a session will be created to
|
||||
* keep track of the configuration of that media. With
|
||||
* gst_rtsp_session_manage_media(), the media is added to the managed media
|
||||
* in the session. With gst_rtsp_session_release_media() the media can be
|
||||
* released again from the session. Managed media is identified in the sessions
|
||||
* with a url. Use gst_rtsp_session_get_media() to get the media that matches
|
||||
* (part of) the given url.
|
||||
*
|
||||
* The media in a session can be iterated with gst_rtsp_session_filter().
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-session.h"
|
||||
|
||||
struct _GstRTSPSessionPrivate
|
||||
{
|
||||
GMutex lock; /* protects everything but sessionid and create_time */
|
||||
gchar *sessionid;
|
||||
|
||||
guint timeout;
|
||||
gboolean timeout_always_visible;
|
||||
GMutex last_access_lock;
|
||||
gint64 last_access_monotonic_time;
|
||||
gint64 last_access_real_time;
|
||||
gint expire_count;
|
||||
|
||||
GList *medias;
|
||||
guint medias_cookie;
|
||||
guint extra_time_timeout;
|
||||
};
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#define DEFAULT_TIMEOUT 60
|
||||
#define NO_TIMEOUT -1
|
||||
#define DEFAULT_ALWAYS_VISIBLE FALSE
|
||||
#define DEFAULT_EXTRA_TIMEOUT 5
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_SESSIONID,
|
||||
PROP_TIMEOUT,
|
||||
PROP_TIMEOUT_ALWAYS_VISIBLE,
|
||||
PROP_EXTRA_TIME_TIMEOUT,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_session_debug
|
||||
|
||||
static void gst_rtsp_session_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_session_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_session_finalize (GObject * obj);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPSession, gst_rtsp_session, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_rtsp_session_class_init (GstRTSPSessionClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gst_rtsp_session_get_property;
|
||||
gobject_class->set_property = gst_rtsp_session_set_property;
|
||||
gobject_class->finalize = gst_rtsp_session_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_SESSIONID,
|
||||
g_param_spec_string ("sessionid", "Sessionid", "the session id",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_TIMEOUT,
|
||||
g_param_spec_uint ("timeout", "timeout",
|
||||
"the timeout of the session (0 = never)", 0, G_MAXUINT,
|
||||
DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_TIMEOUT_ALWAYS_VISIBLE,
|
||||
g_param_spec_boolean ("timeout-always-visible", "Timeout Always Visible ",
|
||||
"timeout always visible in header",
|
||||
DEFAULT_ALWAYS_VISIBLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstRTSPSession::extra-timeout:
|
||||
*
|
||||
* Extra time to add to the timeout, in seconds. This only affects the
|
||||
* time until a session is considered timed out and is not signalled
|
||||
* in the RTSP request responses. Only the value of the timeout
|
||||
* property is signalled in the request responses.
|
||||
*
|
||||
* Default value is 5 seconds.
|
||||
* If the application is using a buffer that is configured to hold
|
||||
* amount of data equal to the sessiontimeout, extra-timeout can be
|
||||
* set to zero to prevent loss of data
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_EXTRA_TIME_TIMEOUT,
|
||||
g_param_spec_uint ("extra-timeout",
|
||||
"Add extra time to timeout ", "Add extra time to timeout", 0,
|
||||
G_MAXUINT, DEFAULT_EXTRA_TIMEOUT,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_session_debug, "rtspsession", 0,
|
||||
"GstRTSPSession");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_init (GstRTSPSession * session)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
|
||||
session->priv = priv = gst_rtsp_session_get_instance_private (session);
|
||||
|
||||
GST_INFO ("init session %p", session);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
g_mutex_init (&priv->last_access_lock);
|
||||
priv->timeout = DEFAULT_TIMEOUT;
|
||||
priv->extra_time_timeout = DEFAULT_EXTRA_TIMEOUT;
|
||||
|
||||
gst_rtsp_session_touch (session);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPSession *session;
|
||||
GstRTSPSessionPrivate *priv;
|
||||
|
||||
session = GST_RTSP_SESSION (obj);
|
||||
priv = session->priv;
|
||||
|
||||
GST_INFO ("finalize session %p", session);
|
||||
|
||||
/* free all media */
|
||||
g_list_free_full (priv->medias, g_object_unref);
|
||||
|
||||
/* free session id */
|
||||
g_free (priv->sessionid);
|
||||
g_mutex_clear (&priv->last_access_lock);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPSession *session = GST_RTSP_SESSION (object);
|
||||
GstRTSPSessionPrivate *priv = session->priv;
|
||||
|
||||
switch (propid) {
|
||||
case PROP_SESSIONID:
|
||||
g_value_set_string (value, priv->sessionid);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_uint (value, gst_rtsp_session_get_timeout (session));
|
||||
break;
|
||||
case PROP_TIMEOUT_ALWAYS_VISIBLE:
|
||||
g_value_set_boolean (value, priv->timeout_always_visible);
|
||||
break;
|
||||
case PROP_EXTRA_TIME_TIMEOUT:
|
||||
g_value_set_uint (value, priv->extra_time_timeout);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPSession *session = GST_RTSP_SESSION (object);
|
||||
GstRTSPSessionPrivate *priv = session->priv;
|
||||
|
||||
switch (propid) {
|
||||
case PROP_SESSIONID:
|
||||
g_free (priv->sessionid);
|
||||
priv->sessionid = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
gst_rtsp_session_set_timeout (session, g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_TIMEOUT_ALWAYS_VISIBLE:
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->timeout_always_visible = g_value_get_boolean (value);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
break;
|
||||
case PROP_EXTRA_TIME_TIMEOUT:
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->extra_time_timeout = g_value_get_uint (value);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_manage_media:
|
||||
* @sess: a #GstRTSPSession
|
||||
* @path: the path for the media
|
||||
* @media: (transfer full): a #GstRTSPMedia
|
||||
*
|
||||
* Manage the media object @obj in @sess. @path will be used to retrieve this
|
||||
* media from the session with gst_rtsp_session_get_media().
|
||||
*
|
||||
* Ownership is taken from @media.
|
||||
*
|
||||
* Returns: (transfer none): a new @GstRTSPSessionMedia object.
|
||||
*/
|
||||
GstRTSPSessionMedia *
|
||||
gst_rtsp_session_manage_media (GstRTSPSession * sess, const gchar * path,
|
||||
GstRTSPMedia * media)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
GstRTSPSessionMedia *result;
|
||||
GstRTSPMediaStatus status;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
|
||||
status = gst_rtsp_media_get_status (media);
|
||||
g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
|
||||
GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
|
||||
|
||||
priv = sess->priv;
|
||||
|
||||
result = gst_rtsp_session_media_new (path, media);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->medias = g_list_prepend (priv->medias, result);
|
||||
priv->medias_cookie++;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
GST_INFO ("manage new media %p in session %p", media, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_session_unset_transport_keepalive (GstRTSPSessionMedia * sessmedia)
|
||||
{
|
||||
GstRTSPMedia *media;
|
||||
guint i, n_streams;
|
||||
|
||||
media = gst_rtsp_session_media_get_media (sessmedia);
|
||||
n_streams = gst_rtsp_media_n_streams (media);
|
||||
|
||||
for (i = 0; i < n_streams; i++) {
|
||||
GstRTSPStreamTransport *transport =
|
||||
gst_rtsp_session_media_get_transport (sessmedia, i);
|
||||
|
||||
if (!transport)
|
||||
continue;
|
||||
|
||||
gst_rtsp_stream_transport_set_keepalive (transport, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_release_media:
|
||||
* @sess: a #GstRTSPSession
|
||||
* @media: (transfer none): a #GstRTSPMedia
|
||||
*
|
||||
* Release the managed @media in @sess, freeing the memory allocated by it.
|
||||
*
|
||||
* Returns: %TRUE if there are more media session left in @sess.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_release_media (GstRTSPSession * sess,
|
||||
GstRTSPSessionMedia * media)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
GList *find;
|
||||
gboolean more;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
|
||||
g_return_val_if_fail (media != NULL, FALSE);
|
||||
|
||||
priv = sess->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
find = g_list_find (priv->medias, media);
|
||||
if (find) {
|
||||
priv->medias = g_list_delete_link (priv->medias, find);
|
||||
priv->medias_cookie++;
|
||||
}
|
||||
more = (priv->medias != NULL);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (find && !more)
|
||||
gst_rtsp_session_unset_transport_keepalive (media);
|
||||
|
||||
if (find)
|
||||
g_object_unref (media);
|
||||
|
||||
return more;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_get_media:
|
||||
* @sess: a #GstRTSPSession
|
||||
* @path: the path for the media
|
||||
* @matched: (out): the amount of matched characters
|
||||
*
|
||||
* Get the session media for @path. @matched will contain the number of matched
|
||||
* characters of @path.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the configuration for @path in @sess.
|
||||
*/
|
||||
GstRTSPSessionMedia *
|
||||
gst_rtsp_session_get_media (GstRTSPSession * sess, const gchar * path,
|
||||
gint * matched)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
GstRTSPSessionMedia *result;
|
||||
GList *walk;
|
||||
gint best;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
priv = sess->priv;
|
||||
result = NULL;
|
||||
best = 0;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
for (walk = priv->medias; walk; walk = g_list_next (walk)) {
|
||||
GstRTSPSessionMedia *test;
|
||||
|
||||
test = (GstRTSPSessionMedia *) walk->data;
|
||||
|
||||
/* find largest match */
|
||||
if (gst_rtsp_session_media_matches (test, path, matched)) {
|
||||
if (best < *matched) {
|
||||
result = test;
|
||||
best = *matched;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
*matched = best;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_filter:
|
||||
* @sess: a #GstRTSPSession
|
||||
* @func: (scope call) (allow-none): a callback
|
||||
* @user_data: (closure): user data passed to @func
|
||||
*
|
||||
* Call @func for each media in @sess. The result value of @func determines
|
||||
* what happens to the media. @func will be called with @sess
|
||||
* locked so no further actions on @sess can be performed from @func.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_REMOVE, the media will be removed from
|
||||
* @sess.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_KEEP, the media will remain in @sess.
|
||||
*
|
||||
* If @func returns #GST_RTSP_FILTER_REF, the media will remain in @sess but
|
||||
* will also be added with an additional ref to the result #GList of this
|
||||
* function..
|
||||
*
|
||||
* When @func is %NULL, #GST_RTSP_FILTER_REF will be assumed for all media.
|
||||
*
|
||||
* Returns: (element-type GstRTSPSessionMedia) (transfer full): a GList with all
|
||||
* media for which @func returned #GST_RTSP_FILTER_REF. After usage, each
|
||||
* element in the #GList should be unreffed before the list is freed.
|
||||
*/
|
||||
GList *
|
||||
gst_rtsp_session_filter (GstRTSPSession * sess,
|
||||
GstRTSPSessionFilterFunc func, gpointer user_data)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
GList *result, *walk, *next;
|
||||
GHashTable *visited;
|
||||
guint cookie;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
|
||||
|
||||
priv = sess->priv;
|
||||
|
||||
result = NULL;
|
||||
if (func)
|
||||
visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
restart:
|
||||
cookie = priv->medias_cookie;
|
||||
for (walk = priv->medias; walk; walk = next) {
|
||||
GstRTSPSessionMedia *media = walk->data;
|
||||
GstRTSPFilterResult res;
|
||||
gboolean changed;
|
||||
|
||||
next = g_list_next (walk);
|
||||
|
||||
if (func) {
|
||||
/* only visit each media once */
|
||||
if (g_hash_table_contains (visited, media))
|
||||
continue;
|
||||
|
||||
g_hash_table_add (visited, g_object_ref (media));
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
res = func (sess, media, user_data);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
} else
|
||||
res = GST_RTSP_FILTER_REF;
|
||||
|
||||
changed = (cookie != priv->medias_cookie);
|
||||
|
||||
switch (res) {
|
||||
case GST_RTSP_FILTER_REMOVE:
|
||||
if (changed)
|
||||
priv->medias = g_list_remove (priv->medias, media);
|
||||
else
|
||||
priv->medias = g_list_delete_link (priv->medias, walk);
|
||||
cookie = ++priv->medias_cookie;
|
||||
g_object_unref (media);
|
||||
break;
|
||||
case GST_RTSP_FILTER_REF:
|
||||
result = g_list_prepend (result, g_object_ref (media));
|
||||
break;
|
||||
case GST_RTSP_FILTER_KEEP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (changed)
|
||||
goto restart;
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
if (func)
|
||||
g_hash_table_unref (visited);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_new:
|
||||
* @sessionid: a session id
|
||||
*
|
||||
* Create a new #GstRTSPSession instance with @sessionid.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPSession
|
||||
*/
|
||||
GstRTSPSession *
|
||||
gst_rtsp_session_new (const gchar * sessionid)
|
||||
{
|
||||
GstRTSPSession *result;
|
||||
|
||||
g_return_val_if_fail (sessionid != NULL, NULL);
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_SESSION, "sessionid", sessionid, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_get_sessionid:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Get the sessionid of @session.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the sessionid of @session.
|
||||
* The value remains valid as long as @session is alive.
|
||||
*/
|
||||
const gchar *
|
||||
gst_rtsp_session_get_sessionid (GstRTSPSession * session)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
|
||||
|
||||
return session->priv->sessionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_get_header:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Get the string that can be placed in the Session header field.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): the Session header of @session.
|
||||
* g_free() after usage.
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_session_get_header (GstRTSPSession * session)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
gchar *result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), NULL);
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
if (priv->timeout_always_visible || priv->timeout != 60)
|
||||
result = g_strdup_printf ("%s;timeout=%d", priv->sessionid, priv->timeout);
|
||||
else
|
||||
result = g_strdup (priv->sessionid);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_set_timeout:
|
||||
* @session: a #GstRTSPSession
|
||||
* @timeout: the new timeout
|
||||
*
|
||||
* Configure @session for a timeout of @timeout seconds. The session will be
|
||||
* cleaned up when there is no activity for @timeout seconds.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_set_timeout (GstRTSPSession * session, guint timeout)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->timeout = timeout;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_get_timeout:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Get the timeout value of @session.
|
||||
*
|
||||
* Returns: the timeout of @session in seconds.
|
||||
*/
|
||||
guint
|
||||
gst_rtsp_session_get_timeout (GstRTSPSession * session)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
guint res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), 0);
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
res = priv->timeout;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_touch:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Update the last_access time of the session to the current time.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_touch (GstRTSPSession * session)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
g_mutex_lock (&priv->last_access_lock);
|
||||
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
||||
priv->last_access_real_time = g_get_real_time ();
|
||||
g_mutex_unlock (&priv->last_access_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_prevent_expire:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Prevent @session from expiring.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_prevent_expire (GstRTSPSession * session)
|
||||
{
|
||||
g_return_if_fail (GST_IS_RTSP_SESSION (session));
|
||||
|
||||
g_atomic_int_add (&session->priv->expire_count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_allow_expire:
|
||||
* @session: a #GstRTSPSession
|
||||
*
|
||||
* Allow @session to expire. This method must be called an equal
|
||||
* amount of time as gst_rtsp_session_prevent_expire().
|
||||
*/
|
||||
void
|
||||
gst_rtsp_session_allow_expire (GstRTSPSession * session)
|
||||
{
|
||||
g_atomic_int_add (&session->priv->expire_count, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_next_timeout_usec:
|
||||
* @session: a #GstRTSPSession
|
||||
* @now: the current monotonic time
|
||||
*
|
||||
* Get the amount of milliseconds till the session will expire.
|
||||
*
|
||||
* Returns: the amount of milliseconds since the session will time out.
|
||||
*/
|
||||
gint
|
||||
gst_rtsp_session_next_timeout_usec (GstRTSPSession * session, gint64 now)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
gint res;
|
||||
GstClockTime last_access, now_ns;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
/* If timeout is set to 0, we never timeout */
|
||||
if (priv->timeout == 0) {
|
||||
g_mutex_unlock (&priv->lock);
|
||||
return NO_TIMEOUT;
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
g_mutex_lock (&priv->last_access_lock);
|
||||
if (g_atomic_int_get (&priv->expire_count) != 0) {
|
||||
/* touch session when the expire count is not 0 */
|
||||
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
||||
priv->last_access_real_time = g_get_real_time ();
|
||||
}
|
||||
|
||||
last_access = GST_USECOND * (priv->last_access_monotonic_time);
|
||||
|
||||
/* add timeout allow for priv->extra_time_timeout
|
||||
* seconds of extra time */
|
||||
last_access += priv->timeout * GST_SECOND +
|
||||
(priv->extra_time_timeout * GST_SECOND);
|
||||
|
||||
g_mutex_unlock (&priv->last_access_lock);
|
||||
|
||||
now_ns = GST_USECOND * now;
|
||||
|
||||
if (last_access > now_ns) {
|
||||
res = GST_TIME_AS_MSECONDS (last_access - now_ns);
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/****** Deprecated API *******/
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_next_timeout:
|
||||
* @session: a #GstRTSPSession
|
||||
* @now: (transfer none): the current system time
|
||||
*
|
||||
* Get the amount of milliseconds till the session will expire.
|
||||
*
|
||||
* Returns: the amount of milliseconds since the session will time out.
|
||||
*
|
||||
* Deprecated: Use gst_rtsp_session_next_timeout_usec() instead.
|
||||
*/
|
||||
#ifndef GST_REMOVE_DEPRECATED
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS gint
|
||||
gst_rtsp_session_next_timeout (GstRTSPSession * session, GTimeVal * now)
|
||||
{
|
||||
GstRTSPSessionPrivate *priv;
|
||||
gint res;
|
||||
GstClockTime last_access, now_ns;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_SESSION (session), -1);
|
||||
g_return_val_if_fail (now != NULL, -1);
|
||||
|
||||
priv = session->priv;
|
||||
|
||||
g_mutex_lock (&priv->last_access_lock);
|
||||
if (g_atomic_int_get (&priv->expire_count) != 0) {
|
||||
/* touch session when the expire count is not 0 */
|
||||
priv->last_access_monotonic_time = g_get_monotonic_time ();
|
||||
priv->last_access_real_time = g_get_real_time ();
|
||||
}
|
||||
|
||||
last_access = GST_USECOND * (priv->last_access_real_time);
|
||||
|
||||
/* add timeout allow for priv->extra_time_timeout
|
||||
* seconds of extra time */
|
||||
last_access += priv->timeout * GST_SECOND +
|
||||
(priv->extra_time_timeout * GST_SECOND);
|
||||
|
||||
g_mutex_unlock (&priv->last_access_lock);
|
||||
|
||||
now_ns = GST_TIMEVAL_TO_TIME (*now);
|
||||
|
||||
if (last_access > now_ns) {
|
||||
res = GST_TIME_AS_MSECONDS (last_access - now_ns);
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
#endif
|
||||
/**
|
||||
* gst_rtsp_session_is_expired_usec:
|
||||
* @session: a #GstRTSPSession
|
||||
* @now: the current monotonic time
|
||||
*
|
||||
* Check if @session timeout out.
|
||||
*
|
||||
* Returns: %TRUE if @session timed out
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_session_is_expired_usec (GstRTSPSession * session, gint64 now)
|
||||
{
|
||||
gboolean res;
|
||||
|
||||
res = (gst_rtsp_session_next_timeout_usec (session, now) == 0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/****** Deprecated API *******/
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_is_expired:
|
||||
* @session: a #GstRTSPSession
|
||||
* @now: (transfer none): the current system time
|
||||
*
|
||||
* Check if @session timeout out.
|
||||
*
|
||||
* Returns: %TRUE if @session timed out
|
||||
*
|
||||
* Deprecated: Use gst_rtsp_session_is_expired_usec() instead.
|
||||
*/
|
||||
#ifndef GST_REMOVE_DEPRECATED
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS gboolean
|
||||
gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
|
||||
{
|
||||
gboolean res;
|
||||
|
||||
res = gst_rtsp_session_next_timeout_usec (session,
|
||||
(now->tv_sec * G_USEC_PER_SEC) + (now->tv_usec));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
#endif
|
181
gst/rtsp-server/rtsp-session.h
Normal file
181
gst/rtsp-server/rtsp-session.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp/gstrtsptransport.h>
|
||||
#include "rtsp-server-prelude.h" /* for GST_RTSP_SERVER_DEPRECATED_FOR */
|
||||
|
||||
#ifndef __GST_RTSP_SESSION_H__
|
||||
#define __GST_RTSP_SESSION_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_SESSION (gst_rtsp_session_get_type ())
|
||||
#define GST_IS_RTSP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SESSION))
|
||||
#define GST_IS_RTSP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SESSION))
|
||||
#define GST_RTSP_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SESSION, GstRTSPSessionClass))
|
||||
#define GST_RTSP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SESSION, GstRTSPSession))
|
||||
#define GST_RTSP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SESSION, GstRTSPSessionClass))
|
||||
#define GST_RTSP_SESSION_CAST(obj) ((GstRTSPSession*)(obj))
|
||||
#define GST_RTSP_SESSION_CLASS_CAST(klass) ((GstRTSPSessionClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPSession GstRTSPSession;
|
||||
typedef struct _GstRTSPSessionClass GstRTSPSessionClass;
|
||||
typedef struct _GstRTSPSessionPrivate GstRTSPSessionPrivate;
|
||||
|
||||
/**
|
||||
* GstRTSPFilterResult:
|
||||
* @GST_RTSP_FILTER_REMOVE: Remove session
|
||||
* @GST_RTSP_FILTER_KEEP: Keep session in the pool
|
||||
* @GST_RTSP_FILTER_REF: Ref session in the result list
|
||||
*
|
||||
* Possible return values for gst_rtsp_session_pool_filter().
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_RTSP_FILTER_REMOVE,
|
||||
GST_RTSP_FILTER_KEEP,
|
||||
GST_RTSP_FILTER_REF,
|
||||
} GstRTSPFilterResult;
|
||||
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-session-media.h"
|
||||
|
||||
/**
|
||||
* GstRTSPSession:
|
||||
*
|
||||
* Session information kept by the server for a specific client.
|
||||
* One client session, identified with a session id, can handle multiple medias
|
||||
* identified with the url of a media.
|
||||
*/
|
||||
struct _GstRTSPSession {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPSessionPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct _GstRTSPSessionClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_session_get_type (void);
|
||||
|
||||
/* create a new session */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
const gchar * gst_rtsp_session_get_sessionid (GstRTSPSession *session);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_session_get_header (GstRTSPSession *session);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_set_timeout (GstRTSPSession *session, guint timeout);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_session_get_timeout (GstRTSPSession *session);
|
||||
|
||||
/* session timeout stuff */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_touch (GstRTSPSession *session);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_prevent_expire (GstRTSPSession *session);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_session_allow_expire (GstRTSPSession *session);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_session_next_timeout_usec (GstRTSPSession *session, gint64 now);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_is_expired_usec (GstRTSPSession *session, gint64 now);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
GST_RTSP_SERVER_DEPRECATED_FOR(gst_rtsp_session_next_timeout_usec)
|
||||
gint gst_rtsp_session_next_timeout (GstRTSPSession *session, GTimeVal *now);
|
||||
|
||||
GST_RTSP_SERVER_DEPRECATED_FOR(gst_rtsp_session_is_expired_usec)
|
||||
gboolean gst_rtsp_session_is_expired (GstRTSPSession *session, GTimeVal *now);
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
/* handle media in a session */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess,
|
||||
const gchar *path,
|
||||
GstRTSPMedia *media);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_session_release_media (GstRTSPSession *sess,
|
||||
GstRTSPSessionMedia *media);
|
||||
/* get media in a session */
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess,
|
||||
const gchar *path,
|
||||
gint * matched);
|
||||
|
||||
/**
|
||||
* GstRTSPSessionFilterFunc:
|
||||
* @sess: a #GstRTSPSession object
|
||||
* @media: a #GstRTSPSessionMedia in @sess
|
||||
* @user_data: user data that has been given to gst_rtsp_session_filter()
|
||||
*
|
||||
* This function will be called by the gst_rtsp_session_filter(). An
|
||||
* implementation should return a value of #GstRTSPFilterResult.
|
||||
*
|
||||
* When this function returns #GST_RTSP_FILTER_REMOVE, @media will be removed
|
||||
* from @sess.
|
||||
*
|
||||
* A return value of #GST_RTSP_FILTER_KEEP will leave @media untouched in
|
||||
* @sess.
|
||||
*
|
||||
* A value of GST_RTSP_FILTER_REF will add @media to the result #GList of
|
||||
* gst_rtsp_session_filter().
|
||||
*
|
||||
* Returns: a #GstRTSPFilterResult.
|
||||
*/
|
||||
typedef GstRTSPFilterResult (*GstRTSPSessionFilterFunc) (GstRTSPSession *sess,
|
||||
GstRTSPSessionMedia *media,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GList * gst_rtsp_session_filter (GstRTSPSession *sess,
|
||||
GstRTSPSessionFilterFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPSession, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_SESSION_H__ */
|
984
gst/rtsp-server/rtsp-stream-transport.c
Normal file
984
gst/rtsp-server/rtsp-stream-transport.c
Normal file
|
@ -0,0 +1,984 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-stream-transport
|
||||
* @short_description: A media stream transport configuration
|
||||
* @see_also: #GstRTSPStream, #GstRTSPSessionMedia
|
||||
*
|
||||
* The #GstRTSPStreamTransport configures the transport used by a
|
||||
* #GstRTSPStream. It is usually manages by a #GstRTSPSessionMedia object.
|
||||
*
|
||||
* With gst_rtsp_stream_transport_set_callbacks(), callbacks can be configured
|
||||
* to handle the RTP and RTCP packets from the stream, for example when they
|
||||
* need to be sent over TCP.
|
||||
*
|
||||
* With gst_rtsp_stream_transport_set_active() the transports are added and
|
||||
* removed from the stream.
|
||||
*
|
||||
* A #GstRTSPStream will call gst_rtsp_stream_transport_keep_alive() when RTCP
|
||||
* is received from the client. It will also call
|
||||
* gst_rtsp_stream_transport_set_timed_out() when a receiver has timed out.
|
||||
*
|
||||
* A #GstRTSPClient will call gst_rtsp_stream_transport_message_sent() when it
|
||||
* has sent a data message for the transport.
|
||||
*
|
||||
* Last reviewed on 2013-07-16 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rtsp-stream-transport.h"
|
||||
#include "rtsp-server-internal.h"
|
||||
|
||||
struct _GstRTSPStreamTransportPrivate
|
||||
{
|
||||
GstRTSPStream *stream;
|
||||
|
||||
GstRTSPSendFunc send_rtp;
|
||||
GstRTSPSendFunc send_rtcp;
|
||||
gpointer user_data;
|
||||
GDestroyNotify notify;
|
||||
|
||||
GstRTSPSendListFunc send_rtp_list;
|
||||
GstRTSPSendListFunc send_rtcp_list;
|
||||
gpointer list_user_data;
|
||||
GDestroyNotify list_notify;
|
||||
|
||||
GstRTSPBackPressureFunc back_pressure_func;
|
||||
gpointer back_pressure_func_data;
|
||||
GDestroyNotify back_pressure_func_notify;
|
||||
|
||||
GstRTSPKeepAliveFunc keep_alive;
|
||||
gpointer ka_user_data;
|
||||
GDestroyNotify ka_notify;
|
||||
gboolean timed_out;
|
||||
|
||||
GstRTSPMessageSentFunc message_sent;
|
||||
gpointer ms_user_data;
|
||||
GDestroyNotify ms_notify;
|
||||
|
||||
GstRTSPMessageSentFuncFull message_sent_full;
|
||||
gpointer msf_user_data;
|
||||
GDestroyNotify msf_notify;
|
||||
|
||||
GstRTSPTransport *transport;
|
||||
GstRTSPUrl *url;
|
||||
|
||||
GObject *rtpsource;
|
||||
|
||||
/* TCP backlog */
|
||||
GstClockTime first_rtp_timestamp;
|
||||
GstQueueArray *items;
|
||||
GRecMutex backlog_lock;
|
||||
};
|
||||
|
||||
#define MAX_BACKLOG_DURATION (10 * GST_SECOND)
|
||||
#define MAX_BACKLOG_SIZE 100
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
GstBufferList *buffer_list;
|
||||
gboolean is_rtp;
|
||||
} BackLogItem;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_stream_transport_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_stream_transport_debug
|
||||
|
||||
static void gst_rtsp_stream_transport_finalize (GObject * obj);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPStreamTransport, gst_rtsp_stream_transport,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_rtsp_stream_transport_class_init (GstRTSPStreamTransportClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_rtsp_stream_transport_finalize;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_stream_transport_debug, "rtspmediatransport",
|
||||
0, "GstRTSPStreamTransport");
|
||||
}
|
||||
|
||||
static void
|
||||
clear_backlog_item (BackLogItem * item)
|
||||
{
|
||||
gst_clear_buffer (&item->buffer);
|
||||
gst_clear_buffer_list (&item->buffer_list);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_stream_transport_init (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
trans->priv = gst_rtsp_stream_transport_get_instance_private (trans);
|
||||
trans->priv->items = gst_queue_array_new_for_struct (sizeof (BackLogItem), 0);
|
||||
trans->priv->first_rtp_timestamp = GST_CLOCK_TIME_NONE;
|
||||
gst_queue_array_set_clear_func (trans->priv->items,
|
||||
(GDestroyNotify) clear_backlog_item);
|
||||
g_rec_mutex_init (&trans->priv->backlog_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_stream_transport_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
GstRTSPStreamTransport *trans;
|
||||
|
||||
trans = GST_RTSP_STREAM_TRANSPORT (obj);
|
||||
priv = trans->priv;
|
||||
|
||||
/* remove callbacks now */
|
||||
gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
|
||||
gst_rtsp_stream_transport_set_keepalive (trans, NULL, NULL, NULL);
|
||||
gst_rtsp_stream_transport_set_message_sent (trans, NULL, NULL, NULL);
|
||||
|
||||
if (priv->stream)
|
||||
g_object_unref (priv->stream);
|
||||
|
||||
if (priv->transport)
|
||||
gst_rtsp_transport_free (priv->transport);
|
||||
|
||||
if (priv->url)
|
||||
gst_rtsp_url_free (priv->url);
|
||||
|
||||
gst_queue_array_free (priv->items);
|
||||
|
||||
g_rec_mutex_clear (&priv->backlog_lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_stream_transport_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_new:
|
||||
* @stream: a #GstRTSPStream
|
||||
* @tr: (transfer full): a GstRTSPTransport
|
||||
*
|
||||
* Create a new #GstRTSPStreamTransport that can be used to manage
|
||||
* @stream with transport @tr.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPStreamTransport
|
||||
*/
|
||||
GstRTSPStreamTransport *
|
||||
gst_rtsp_stream_transport_new (GstRTSPStream * stream, GstRTSPTransport * tr)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
GstRTSPStreamTransport *trans;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
|
||||
g_return_val_if_fail (tr != NULL, NULL);
|
||||
|
||||
trans = g_object_new (GST_TYPE_RTSP_STREAM_TRANSPORT, NULL);
|
||||
priv = trans->priv;
|
||||
priv->stream = stream;
|
||||
priv->stream = g_object_ref (priv->stream);
|
||||
priv->transport = tr;
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_get_stream:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Get the #GstRTSPStream used when constructing @trans.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the stream used when constructing @trans.
|
||||
*/
|
||||
GstRTSPStream *
|
||||
gst_rtsp_stream_transport_get_stream (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
|
||||
|
||||
return trans->priv->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_callbacks:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @send_rtp: (scope notified): a callback called when RTP should be sent
|
||||
* @send_rtcp: (scope notified): a callback called when RTCP should be sent
|
||||
* @user_data: (closure): user data passed to callbacks
|
||||
* @notify: (allow-none): called with the user_data when no longer needed.
|
||||
*
|
||||
* Install callbacks that will be called when data for a stream should be sent
|
||||
* to a client. This is usually used when sending RTP/RTCP over TCP.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport * trans,
|
||||
GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
|
||||
gpointer user_data, GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->send_rtp = send_rtp;
|
||||
priv->send_rtcp = send_rtcp;
|
||||
if (priv->notify)
|
||||
priv->notify (priv->user_data);
|
||||
priv->user_data = user_data;
|
||||
priv->notify = notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_list_callbacks:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @send_rtp_list: (scope notified): a callback called when RTP should be sent
|
||||
* @send_rtcp_list: (scope notified): a callback called when RTCP should be sent
|
||||
* @user_data: (closure): user data passed to callbacks
|
||||
* @notify: (allow-none): called with the user_data when no longer needed.
|
||||
*
|
||||
* Install callbacks that will be called when data for a stream should be sent
|
||||
* to a client. This is usually used when sending RTP/RTCP over TCP.
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_list_callbacks (GstRTSPStreamTransport * trans,
|
||||
GstRTSPSendListFunc send_rtp_list, GstRTSPSendListFunc send_rtcp_list,
|
||||
gpointer user_data, GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->send_rtp_list = send_rtp_list;
|
||||
priv->send_rtcp_list = send_rtcp_list;
|
||||
if (priv->list_notify)
|
||||
priv->list_notify (priv->list_user_data);
|
||||
priv->list_user_data = user_data;
|
||||
priv->list_notify = notify;
|
||||
}
|
||||
|
||||
void
|
||||
gst_rtsp_stream_transport_set_back_pressure_callback (GstRTSPStreamTransport *
|
||||
trans, GstRTSPBackPressureFunc back_pressure_func, gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->back_pressure_func = back_pressure_func;
|
||||
if (priv->back_pressure_func_notify)
|
||||
priv->back_pressure_func_notify (priv->back_pressure_func_data);
|
||||
priv->back_pressure_func_data = user_data;
|
||||
priv->back_pressure_func_notify = notify;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_check_back_pressure (GstRTSPStreamTransport * trans,
|
||||
gboolean is_rtp)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean ret = FALSE;
|
||||
guint8 channel;
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (is_rtp)
|
||||
channel = priv->transport->interleaved.min;
|
||||
else
|
||||
channel = priv->transport->interleaved.max;
|
||||
|
||||
if (priv->back_pressure_func)
|
||||
ret = priv->back_pressure_func (channel, priv->back_pressure_func_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_keepalive:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @keep_alive: (scope notified): a callback called when the receiver is active
|
||||
* @user_data: (closure): user data passed to callback
|
||||
* @notify: (allow-none): called with the user_data when no longer needed.
|
||||
*
|
||||
* Install callbacks that will be called when RTCP packets are received from the
|
||||
* receiver of @trans.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport * trans,
|
||||
GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->keep_alive = keep_alive;
|
||||
if (priv->ka_notify)
|
||||
priv->ka_notify (priv->ka_user_data);
|
||||
priv->ka_user_data = user_data;
|
||||
priv->ka_notify = notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_message_sent:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @message_sent: (scope notified): a callback called when a message has been sent
|
||||
* @user_data: (closure): user data passed to callback
|
||||
* @notify: (allow-none): called with the user_data when no longer needed
|
||||
*
|
||||
* Install a callback that will be called when a message has been sent on @trans.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_message_sent (GstRTSPStreamTransport * trans,
|
||||
GstRTSPMessageSentFunc message_sent, gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->message_sent = message_sent;
|
||||
if (priv->ms_notify)
|
||||
priv->ms_notify (priv->ms_user_data);
|
||||
priv->ms_user_data = user_data;
|
||||
priv->ms_notify = notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_message_sent_full:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @message_sent: (scope notified): a callback called when a message has been sent
|
||||
* @user_data: (closure): user data passed to callback
|
||||
* @notify: (allow-none): called with the user_data when no longer needed
|
||||
*
|
||||
* Install a callback that will be called when a message has been sent on @trans.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_message_sent_full (GstRTSPStreamTransport * trans,
|
||||
GstRTSPMessageSentFuncFull message_sent, gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
priv->message_sent_full = message_sent;
|
||||
if (priv->msf_notify)
|
||||
priv->msf_notify (priv->msf_user_data);
|
||||
priv->msf_user_data = user_data;
|
||||
priv->msf_notify = notify;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_transport:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @tr: (transfer full): a client #GstRTSPTransport
|
||||
*
|
||||
* Set @tr as the client transport. This function takes ownership of the
|
||||
* passed @tr.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport * trans,
|
||||
GstRTSPTransport * tr)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
g_return_if_fail (tr != NULL);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
/* keep track of the transports in the stream. */
|
||||
if (priv->transport)
|
||||
gst_rtsp_transport_free (priv->transport);
|
||||
priv->transport = tr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_get_transport:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Get the transport configured in @trans.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the transport configured in @trans. It remains
|
||||
* valid for as long as @trans is valid.
|
||||
*/
|
||||
const GstRTSPTransport *
|
||||
gst_rtsp_stream_transport_get_transport (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
|
||||
|
||||
return trans->priv->transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_url:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @url: (transfer none) (nullable): a client #GstRTSPUrl
|
||||
*
|
||||
* Set @url as the client url.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_url (GstRTSPStreamTransport * trans,
|
||||
const GstRTSPUrl * url)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
/* keep track of the transports in the stream. */
|
||||
if (priv->url)
|
||||
gst_rtsp_url_free (priv->url);
|
||||
priv->url = (url ? gst_rtsp_url_copy (url) : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_get_url:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Get the url configured in @trans.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the url configured in @trans.
|
||||
* It remains valid for as long as @trans is valid.
|
||||
*/
|
||||
const GstRTSPUrl *
|
||||
gst_rtsp_stream_transport_get_url (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
|
||||
|
||||
return trans->priv->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_get_rtpinfo:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @start_time: a star time
|
||||
*
|
||||
* Get the RTP-Info string for @trans and @start_time.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): the RTPInfo string for @trans
|
||||
* and @start_time or %NULL when the RTP-Info could not be
|
||||
* determined. g_free() after usage.
|
||||
*/
|
||||
gchar *
|
||||
gst_rtsp_stream_transport_get_rtpinfo (GstRTSPStreamTransport * trans,
|
||||
GstClockTime start_time)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gchar *url_str;
|
||||
GString *rtpinfo;
|
||||
guint rtptime, seq, clock_rate;
|
||||
GstClockTime running_time = GST_CLOCK_TIME_NONE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (!gst_rtsp_stream_is_sender (priv->stream))
|
||||
return NULL;
|
||||
if (!gst_rtsp_stream_get_rtpinfo (priv->stream, &rtptime, &seq, &clock_rate,
|
||||
&running_time))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("RTP time %u, seq %u, rate %u, running-time %" GST_TIME_FORMAT,
|
||||
rtptime, seq, clock_rate, GST_TIME_ARGS (running_time));
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (running_time)
|
||||
&& GST_CLOCK_TIME_IS_VALID (start_time)) {
|
||||
if (running_time > start_time) {
|
||||
rtptime -=
|
||||
gst_util_uint64_scale_int (running_time - start_time, clock_rate,
|
||||
GST_SECOND);
|
||||
} else {
|
||||
rtptime +=
|
||||
gst_util_uint64_scale_int (start_time - running_time, clock_rate,
|
||||
GST_SECOND);
|
||||
}
|
||||
}
|
||||
GST_DEBUG ("RTP time %u, for start-time %" GST_TIME_FORMAT,
|
||||
rtptime, GST_TIME_ARGS (start_time));
|
||||
|
||||
rtpinfo = g_string_new ("");
|
||||
|
||||
url_str = gst_rtsp_url_get_request_uri (trans->priv->url);
|
||||
g_string_append_printf (rtpinfo, "url=%s;seq=%u;rtptime=%u",
|
||||
url_str, seq, rtptime);
|
||||
g_free (url_str);
|
||||
|
||||
return g_string_free (rtpinfo, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_active:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @active: new state of @trans
|
||||
*
|
||||
* Activate or deactivate datatransfer configured in @trans.
|
||||
*
|
||||
* Returns: %TRUE when the state was changed.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_set_active (GstRTSPStreamTransport * trans,
|
||||
gboolean active)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (active)
|
||||
res = gst_rtsp_stream_add_transport (priv->stream, trans);
|
||||
else
|
||||
res = gst_rtsp_stream_remove_transport (priv->stream, trans);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_set_timed_out:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @timedout: timed out value
|
||||
*
|
||||
* Set the timed out state of @trans to @timedout
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_set_timed_out (GstRTSPStreamTransport * trans,
|
||||
gboolean timedout)
|
||||
{
|
||||
g_return_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans));
|
||||
|
||||
trans->priv->timed_out = timedout;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_is_timed_out:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Check if @trans is timed out.
|
||||
*
|
||||
* Returns: %TRUE if @trans timed out.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_is_timed_out (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
|
||||
|
||||
return trans->priv->timed_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_send_rtp:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @buffer: (transfer none): a #GstBuffer
|
||||
*
|
||||
* Send @buffer to the installed RTP callback for @trans.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_send_rtp (GstRTSPStreamTransport * trans,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean res = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->send_rtp)
|
||||
res =
|
||||
priv->send_rtp (buffer, priv->transport->interleaved.min,
|
||||
priv->user_data);
|
||||
|
||||
if (res)
|
||||
gst_rtsp_stream_transport_keep_alive (trans);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_send_rtcp:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @buffer: (transfer none): a #GstBuffer
|
||||
*
|
||||
* Send @buffer to the installed RTCP callback for @trans.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_send_rtcp (GstRTSPStreamTransport * trans,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean res = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->send_rtcp)
|
||||
res =
|
||||
priv->send_rtcp (buffer, priv->transport->interleaved.max,
|
||||
priv->user_data);
|
||||
|
||||
if (res)
|
||||
gst_rtsp_stream_transport_keep_alive (trans);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_send_rtp_list:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @buffer_list: (transfer none): a #GstBufferList
|
||||
*
|
||||
* Send @buffer_list to the installed RTP callback for @trans.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_send_rtp_list (GstRTSPStreamTransport * trans,
|
||||
GstBufferList * buffer_list)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean res = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER_LIST (buffer_list), FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->send_rtp_list) {
|
||||
res =
|
||||
priv->send_rtp_list (buffer_list, priv->transport->interleaved.min,
|
||||
priv->list_user_data);
|
||||
} else if (priv->send_rtp) {
|
||||
guint n = gst_buffer_list_length (buffer_list), i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
GstBuffer *buffer = gst_buffer_list_get (buffer_list, i);
|
||||
|
||||
res =
|
||||
priv->send_rtp (buffer, priv->transport->interleaved.min,
|
||||
priv->user_data);
|
||||
if (!res)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
gst_rtsp_stream_transport_keep_alive (trans);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_send_rtcp_list:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @buffer_list: (transfer none): a #GstBuffer
|
||||
*
|
||||
* Send @buffer_list to the installed RTCP callback for @trans.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_send_rtcp_list (GstRTSPStreamTransport * trans,
|
||||
GstBufferList * buffer_list)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gboolean res = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER_LIST (buffer_list), FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->send_rtcp_list) {
|
||||
res =
|
||||
priv->send_rtcp_list (buffer_list, priv->transport->interleaved.max,
|
||||
priv->list_user_data);
|
||||
} else if (priv->send_rtcp) {
|
||||
guint n = gst_buffer_list_length (buffer_list), i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
GstBuffer *buffer = gst_buffer_list_get (buffer_list, i);
|
||||
|
||||
res =
|
||||
priv->send_rtcp (buffer, priv->transport->interleaved.max,
|
||||
priv->user_data);
|
||||
if (!res)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
gst_rtsp_stream_transport_keep_alive (trans);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_keep_alive:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Signal the installed keep_alive callback for @trans.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_keep_alive (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->keep_alive)
|
||||
priv->keep_alive (priv->ka_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_message_sent:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
*
|
||||
* Signal the installed message_sent / message_sent_full callback for @trans.
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_transport_message_sent (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (priv->message_sent_full)
|
||||
priv->message_sent_full (trans, priv->msf_user_data);
|
||||
if (priv->message_sent)
|
||||
priv->message_sent (priv->ms_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_transport_recv_data:
|
||||
* @trans: a #GstRTSPStreamTransport
|
||||
* @channel: a channel
|
||||
* @buffer: (transfer full): a #GstBuffer
|
||||
*
|
||||
* Receive @buffer on @channel @trans.
|
||||
*
|
||||
* Returns: a #GstFlowReturn. Returns GST_FLOW_NOT_LINKED when @channel is not
|
||||
* configured in the transport of @trans.
|
||||
*/
|
||||
GstFlowReturn
|
||||
gst_rtsp_stream_transport_recv_data (GstRTSPStreamTransport * trans,
|
||||
guint channel, GstBuffer * buffer)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
const GstRTSPTransport *tr;
|
||||
GstFlowReturn res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
|
||||
|
||||
priv = trans->priv;
|
||||
tr = priv->transport;
|
||||
|
||||
if (tr->interleaved.min == channel) {
|
||||
res = gst_rtsp_stream_recv_rtp (priv->stream, buffer);
|
||||
} else if (tr->interleaved.max == channel) {
|
||||
res = gst_rtsp_stream_recv_rtcp (priv->stream, buffer);
|
||||
} else {
|
||||
res = GST_FLOW_NOT_LINKED;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
get_backlog_item_timestamp (BackLogItem * item)
|
||||
{
|
||||
GstClockTime ret = GST_CLOCK_TIME_NONE;
|
||||
|
||||
if (item->buffer) {
|
||||
ret = GST_BUFFER_DTS_OR_PTS (item->buffer);
|
||||
} else if (item->buffer_list) {
|
||||
g_assert (gst_buffer_list_length (item->buffer_list) > 0);
|
||||
ret = GST_BUFFER_DTS_OR_PTS (gst_buffer_list_get (item->buffer_list, 0));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
get_first_backlog_timestamp (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
GstRTSPStreamTransportPrivate *priv = trans->priv;
|
||||
GstClockTime ret = GST_CLOCK_TIME_NONE;
|
||||
guint i, l;
|
||||
|
||||
l = gst_queue_array_get_length (priv->items);
|
||||
|
||||
for (i = 0; i < l; i++) {
|
||||
BackLogItem *item = (BackLogItem *)
|
||||
gst_queue_array_peek_nth_struct (priv->items, i);
|
||||
|
||||
if (item->is_rtp) {
|
||||
ret = get_backlog_item_timestamp (item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not MT-safe, caller should ensure consistent locking (see
|
||||
* gst_rtsp_stream_transport_lock_backlog()). Ownership
|
||||
* of @buffer and @buffer_list is transfered to the transport */
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_backlog_push (GstRTSPStreamTransport * trans,
|
||||
GstBuffer * buffer, GstBufferList * buffer_list, gboolean is_rtp)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
BackLogItem item = { 0, };
|
||||
GstClockTime item_timestamp;
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
if (buffer)
|
||||
item.buffer = buffer;
|
||||
if (buffer_list)
|
||||
item.buffer_list = buffer_list;
|
||||
item.is_rtp = is_rtp;
|
||||
|
||||
gst_queue_array_push_tail_struct (priv->items, &item);
|
||||
|
||||
item_timestamp = get_backlog_item_timestamp (&item);
|
||||
|
||||
if (is_rtp && priv->first_rtp_timestamp != GST_CLOCK_TIME_NONE) {
|
||||
GstClockTimeDiff queue_duration;
|
||||
|
||||
g_assert (GST_CLOCK_TIME_IS_VALID (item_timestamp));
|
||||
|
||||
queue_duration = GST_CLOCK_DIFF (priv->first_rtp_timestamp, item_timestamp);
|
||||
|
||||
g_assert (queue_duration >= 0);
|
||||
|
||||
if (queue_duration > MAX_BACKLOG_DURATION &&
|
||||
gst_queue_array_get_length (priv->items) > MAX_BACKLOG_SIZE) {
|
||||
ret = FALSE;
|
||||
}
|
||||
} else if (is_rtp) {
|
||||
priv->first_rtp_timestamp = item_timestamp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not MT-safe, caller should ensure consistent locking (see
|
||||
* gst_rtsp_stream_transport_lock_backlog()). Ownership
|
||||
* of @buffer and @buffer_list is transfered back to the caller,
|
||||
* if either of those is NULL the underlying object is unreffed */
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_backlog_pop (GstRTSPStreamTransport * trans,
|
||||
GstBuffer ** buffer, GstBufferList ** buffer_list, gboolean * is_rtp)
|
||||
{
|
||||
BackLogItem *item;
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (!gst_rtsp_stream_transport_backlog_is_empty (trans),
|
||||
FALSE);
|
||||
|
||||
priv = trans->priv;
|
||||
|
||||
item = (BackLogItem *) gst_queue_array_pop_head_struct (priv->items);
|
||||
|
||||
priv->first_rtp_timestamp = get_first_backlog_timestamp (trans);
|
||||
|
||||
if (buffer)
|
||||
*buffer = item->buffer;
|
||||
else if (item->buffer)
|
||||
gst_buffer_unref (item->buffer);
|
||||
|
||||
if (buffer_list)
|
||||
*buffer_list = item->buffer_list;
|
||||
else if (item->buffer_list)
|
||||
gst_buffer_list_unref (item->buffer_list);
|
||||
|
||||
if (is_rtp)
|
||||
*is_rtp = item->is_rtp;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Not MT-safe, caller should ensure consistent locking.
|
||||
* See gst_rtsp_stream_transport_lock_backlog() */
|
||||
gboolean
|
||||
gst_rtsp_stream_transport_backlog_is_empty (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
return gst_queue_array_is_empty (trans->priv->items);
|
||||
}
|
||||
|
||||
/* Not MT-safe, caller should ensure consistent locking.
|
||||
* See gst_rtsp_stream_transport_lock_backlog() */
|
||||
void
|
||||
gst_rtsp_stream_transport_clear_backlog (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
while (!gst_rtsp_stream_transport_backlog_is_empty (trans)) {
|
||||
gst_rtsp_stream_transport_backlog_pop (trans, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal API, protects access to the TCP backlog. Safe to
|
||||
* call recursively */
|
||||
void
|
||||
gst_rtsp_stream_transport_lock_backlog (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_rec_mutex_lock (&trans->priv->backlog_lock);
|
||||
}
|
||||
|
||||
/* See gst_rtsp_stream_transport_lock_backlog() */
|
||||
void
|
||||
gst_rtsp_stream_transport_unlock_backlog (GstRTSPStreamTransport * trans)
|
||||
{
|
||||
g_rec_mutex_unlock (&trans->priv->backlog_lock);
|
||||
}
|
229
gst/rtsp-server/rtsp-stream-transport.h
Normal file
229
gst/rtsp-server/rtsp-stream-transport.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/base.h>
|
||||
#include <gst/rtsp/gstrtsprange.h>
|
||||
#include <gst/rtsp/gstrtspurl.h>
|
||||
|
||||
#ifndef __GST_RTSP_STREAM_TRANSPORT_H__
|
||||
#define __GST_RTSP_STREAM_TRANSPORT_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* types for the media */
|
||||
#define GST_TYPE_RTSP_STREAM_TRANSPORT (gst_rtsp_stream_transport_get_type ())
|
||||
#define GST_IS_RTSP_STREAM_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT))
|
||||
#define GST_IS_RTSP_STREAM_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT))
|
||||
#define GST_RTSP_STREAM_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
|
||||
#define GST_RTSP_STREAM_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransport))
|
||||
#define GST_RTSP_STREAM_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
|
||||
#define GST_RTSP_STREAM_TRANSPORT_CAST(obj) ((GstRTSPStreamTransport*)(obj))
|
||||
#define GST_RTSP_STREAM_TRANSPORT_CLASS_CAST(klass) ((GstRTSPStreamTransportClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPStreamTransport GstRTSPStreamTransport;
|
||||
typedef struct _GstRTSPStreamTransportClass GstRTSPStreamTransportClass;
|
||||
typedef struct _GstRTSPStreamTransportPrivate GstRTSPStreamTransportPrivate;
|
||||
|
||||
#include "rtsp-stream.h"
|
||||
|
||||
/**
|
||||
* GstRTSPSendFunc:
|
||||
* @buffer: a #GstBuffer
|
||||
* @channel: a channel
|
||||
* @user_data: user data
|
||||
*
|
||||
* Function registered with gst_rtsp_stream_transport_set_callbacks() and
|
||||
* called when @buffer must be sent on @channel.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
typedef gboolean (*GstRTSPSendFunc) (GstBuffer *buffer, guint8 channel, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPSendListFunc:
|
||||
* @buffer_list: a #GstBufferList
|
||||
* @channel: a channel
|
||||
* @user_data: user data
|
||||
*
|
||||
* Function registered with gst_rtsp_stream_transport_set_callbacks() and
|
||||
* called when @buffer_list must be sent on @channel.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
typedef gboolean (*GstRTSPSendListFunc) (GstBufferList *buffer_list, guint8 channel, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPKeepAliveFunc:
|
||||
* @user_data: user data
|
||||
*
|
||||
* Function registered with gst_rtsp_stream_transport_set_keepalive() and called
|
||||
* when the stream is active.
|
||||
*/
|
||||
typedef void (*GstRTSPKeepAliveFunc) (gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPMessageSentFunc:
|
||||
* @user_data: user data
|
||||
*
|
||||
* Function registered with gst_rtsp_stream_transport_set_message_sent()
|
||||
* and called when a message has been sent on the transport.
|
||||
*/
|
||||
typedef void (*GstRTSPMessageSentFunc) (gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPMessageSentFuncFull:
|
||||
* @user_data: user data
|
||||
*
|
||||
* Function registered with gst_rtsp_stream_transport_set_message_sent_full()
|
||||
* and called when a message has been sent on the transport.
|
||||
*
|
||||
* Since: 1.18
|
||||
*/
|
||||
typedef void (*GstRTSPMessageSentFuncFull) (GstRTSPStreamTransport *trans, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstRTSPStreamTransport:
|
||||
* @parent: parent instance
|
||||
*
|
||||
* A Transport description for a stream
|
||||
*/
|
||||
struct _GstRTSPStreamTransport {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPStreamTransportPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct _GstRTSPStreamTransportClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_stream_transport_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStreamTransport * gst_rtsp_stream_transport_new (GstRTSPStream *stream,
|
||||
GstRTSPTransport *tr);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStream * gst_rtsp_stream_transport_get_stream (GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport *trans,
|
||||
GstRTSPTransport * tr);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
const GstRTSPTransport * gst_rtsp_stream_transport_get_transport (GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_url (GstRTSPStreamTransport *trans,
|
||||
const GstRTSPUrl * url);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
const GstRTSPUrl * gst_rtsp_stream_transport_get_url (GstRTSPStreamTransport *trans);
|
||||
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_stream_transport_get_rtpinfo (GstRTSPStreamTransport *trans,
|
||||
GstClockTime start_time);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport *trans,
|
||||
GstRTSPSendFunc send_rtp,
|
||||
GstRTSPSendFunc send_rtcp,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_list_callbacks (GstRTSPStreamTransport *trans,
|
||||
GstRTSPSendListFunc send_rtp_list,
|
||||
GstRTSPSendListFunc send_rtcp_list,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport *trans,
|
||||
GstRTSPKeepAliveFunc keep_alive,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_message_sent (GstRTSPStreamTransport *trans,
|
||||
GstRTSPMessageSentFunc message_sent,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_message_sent_full (GstRTSPStreamTransport *trans,
|
||||
GstRTSPMessageSentFuncFull message_sent,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_keep_alive (GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_message_sent (GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_set_active (GstRTSPStreamTransport *trans,
|
||||
gboolean active);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_transport_set_timed_out (GstRTSPStreamTransport *trans,
|
||||
gboolean timedout);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_is_timed_out (GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_send_rtp (GstRTSPStreamTransport *trans,
|
||||
GstBuffer *buffer);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_send_rtcp (GstRTSPStreamTransport *trans,
|
||||
GstBuffer *buffer);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_send_rtp_list (GstRTSPStreamTransport *trans,
|
||||
GstBufferList *buffer_list);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_transport_send_rtcp_list(GstRTSPStreamTransport *trans,
|
||||
GstBufferList *buffer_list);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstFlowReturn gst_rtsp_stream_transport_recv_data (GstRTSPStreamTransport *trans,
|
||||
guint channel, GstBuffer *buffer);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPStreamTransport, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_STREAM_TRANSPORT_H__ */
|
6366
gst/rtsp-server/rtsp-stream.c
Normal file
6366
gst/rtsp-server/rtsp-stream.c
Normal file
File diff suppressed because it is too large
Load diff
406
gst/rtsp-server/rtsp-stream.h
Normal file
406
gst/rtsp-server/rtsp-stream.h
Normal file
|
@ -0,0 +1,406 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtsp/rtsp.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#ifndef __GST_RTSP_STREAM_H__
|
||||
#define __GST_RTSP_STREAM_H__
|
||||
|
||||
#include "rtsp-server-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* types for the media stream */
|
||||
#define GST_TYPE_RTSP_STREAM (gst_rtsp_stream_get_type ())
|
||||
#define GST_IS_RTSP_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM))
|
||||
#define GST_IS_RTSP_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM))
|
||||
#define GST_RTSP_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
|
||||
#define GST_RTSP_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStream))
|
||||
#define GST_RTSP_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
|
||||
#define GST_RTSP_STREAM_CAST(obj) ((GstRTSPStream*)(obj))
|
||||
#define GST_RTSP_STREAM_CLASS_CAST(klass) ((GstRTSPStreamClass*)(klass))
|
||||
|
||||
typedef struct _GstRTSPStream GstRTSPStream;
|
||||
typedef struct _GstRTSPStreamClass GstRTSPStreamClass;
|
||||
typedef struct _GstRTSPStreamPrivate GstRTSPStreamPrivate;
|
||||
|
||||
#include "rtsp-stream-transport.h"
|
||||
#include "rtsp-address-pool.h"
|
||||
#include "rtsp-session.h"
|
||||
#include "rtsp-media.h"
|
||||
|
||||
/**
|
||||
* GstRTSPStream:
|
||||
*
|
||||
* The definition of a media stream.
|
||||
*/
|
||||
struct _GstRTSPStream {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPStreamPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
struct _GstRTSPStreamClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_stream_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPStream * gst_rtsp_stream_new (guint idx, GstElement *payloader,
|
||||
GstPad *pad);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_index (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_pt (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstPad * gst_rtsp_stream_get_srcpad (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstPad * gst_rtsp_stream_get_sinkpad (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_control (GstRTSPStream *stream, const gchar *control);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_stream_get_control (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_has_control (GstRTSPStream *stream, const gchar *control);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_mtu (GstRTSPStream *stream, guint mtu);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_mtu (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_dscp_qos (GstRTSPStream *stream, gint dscp_qos);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_stream_get_dscp_qos (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_transport_supported (GstRTSPStream *stream,
|
||||
GstRTSPTransport *transport);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_profiles (GstRTSPStream *stream, GstRTSPProfile profiles);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPProfile gst_rtsp_stream_get_profiles (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_protocols (GstRTSPStream *stream, GstRTSPLowerTrans protocols);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPLowerTrans gst_rtsp_stream_get_protocols (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_address_pool (GstRTSPStream *stream, GstRTSPAddressPool *pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddressPool *
|
||||
gst_rtsp_stream_get_address_pool (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_multicast_iface (GstRTSPStream *stream, const gchar * multicast_iface);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_stream_get_multicast_iface (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddress * gst_rtsp_stream_reserve_address (GstRTSPStream *stream,
|
||||
const gchar * address,
|
||||
guint port,
|
||||
guint n_ports,
|
||||
guint ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_join_bin (GstRTSPStream *stream,
|
||||
GstBin *bin, GstElement *rtpbin,
|
||||
GstState state);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_leave_bin (GstRTSPStream *stream,
|
||||
GstBin *bin, GstElement *rtpbin);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstBin * gst_rtsp_stream_get_joined_bin (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_set_blocked (GstRTSPStream * stream,
|
||||
gboolean blocked);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_blocking (GstRTSPStream * stream);
|
||||
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_unblock_linked (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_client_side (GstRTSPStream *stream, gboolean client_side);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_client_side (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_get_server_port (GstRTSPStream *stream,
|
||||
GstRTSPRange *server_port,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPAddress * gst_rtsp_stream_get_multicast_address (GstRTSPStream *stream,
|
||||
GSocketFamily family);
|
||||
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GObject * gst_rtsp_stream_get_rtpsession (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_stream_get_srtp_encoder (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_get_ssrc (GstRTSPStream *stream,
|
||||
guint *ssrc);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_get_rtpinfo (GstRTSPStream *stream,
|
||||
guint *rtptime, guint *seq,
|
||||
guint *clock_rate,
|
||||
GstClockTime *running_time);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_get_rates (GstRTSPStream * stream,
|
||||
gdouble * rate,
|
||||
gdouble * applied_rate);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstCaps * gst_rtsp_stream_get_caps (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstFlowReturn gst_rtsp_stream_recv_rtp (GstRTSPStream *stream,
|
||||
GstBuffer *buffer);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstFlowReturn gst_rtsp_stream_recv_rtcp (GstRTSPStream *stream,
|
||||
GstBuffer *buffer);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_add_transport (GstRTSPStream *stream,
|
||||
GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_remove_transport (GstRTSPStream *stream,
|
||||
GstRTSPStreamTransport *trans);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSocket * gst_rtsp_stream_get_rtp_socket (GstRTSPStream *stream,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSocket * gst_rtsp_stream_get_rtcp_socket (GstRTSPStream *stream,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSocket * gst_rtsp_stream_get_rtp_multicast_socket (GstRTSPStream *stream,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GSocket * gst_rtsp_stream_get_rtcp_multicast_socket (GstRTSPStream *stream,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_add_multicast_client_address (GstRTSPStream * stream,
|
||||
const gchar * destination,
|
||||
guint rtp_port,
|
||||
guint rtcp_port,
|
||||
GSocketFamily family);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gchar * gst_rtsp_stream_get_multicast_client_addresses (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_update_crypto (GstRTSPStream * stream,
|
||||
guint ssrc, GstCaps * crypto);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_query_position (GstRTSPStream * stream,
|
||||
gint64 * position);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_query_stop (GstRTSPStream * stream,
|
||||
gint64 * stop);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_seekable (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_seqnum_offset (GstRTSPStream *stream, guint16 seqnum);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint16 gst_rtsp_stream_get_current_seqnum (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_retransmission_time (GstRTSPStream *stream, GstClockTime time);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstClockTime gst_rtsp_stream_get_retransmission_time (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_retransmission_pt (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_retransmission_pt (GstRTSPStream * stream,
|
||||
guint rtx_pt);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_buffer_size (GstRTSPStream *stream, guint size);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_buffer_size (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_pt_map (GstRTSPStream * stream, guint pt, GstCaps * caps);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_stream_request_aux_sender (GstRTSPStream * stream, guint sessid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_stream_request_aux_receiver (GstRTSPStream * stream, guint sessid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_allocate_udp_sockets (GstRTSPStream * stream, GSocketFamily family,
|
||||
GstRTSPTransport *transport, gboolean use_client_settings);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_publish_clock_mode (GstRTSPStream * stream, GstRTSPPublishClockMode mode);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPPublishClockMode gst_rtsp_stream_get_publish_clock_mode (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_set_max_mcast_ttl (GstRTSPStream *stream, guint ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_max_mcast_ttl (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_verify_mcast_ttl (GstRTSPStream *stream, guint ttl);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_bind_mcast_address (GstRTSPStream * stream, gboolean bind_mcast_addr);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_bind_mcast_address (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_complete_stream (GstRTSPStream * stream, const GstRTSPTransport * transport);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_complete (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_sender (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_is_receiver (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_handle_keymgmt (GstRTSPStream *stream, const gchar *keymgmt);
|
||||
|
||||
/* ULP Forward Error Correction (RFC 5109) */
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_get_ulpfec_enabled (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_ulpfec_pt (GstRTSPStream *stream, guint pt);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_ulpfec_pt (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_stream_request_ulpfec_decoder (GstRTSPStream *stream, GstElement *rtpbin, guint sessid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstElement * gst_rtsp_stream_request_ulpfec_encoder (GstRTSPStream *stream, guint sessid);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_ulpfec_percentage (GstRTSPStream *stream, guint percentage);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
guint gst_rtsp_stream_get_ulpfec_percentage (GstRTSPStream *stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_set_rate_control (GstRTSPStream * stream, gboolean enabled);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_stream_get_rate_control (GstRTSPStream * stream);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_stream_unblock_rtcp (GstRTSPStream * stream);
|
||||
|
||||
/**
|
||||
* GstRTSPStreamTransportFilterFunc:
|
||||
* @stream: a #GstRTSPStream object
|
||||
* @trans: a #GstRTSPStreamTransport in @stream
|
||||
* @user_data: user data that has been given to gst_rtsp_stream_transport_filter()
|
||||
*
|
||||
* This function will be called by the gst_rtsp_stream_transport_filter(). An
|
||||
* implementation should return a value of #GstRTSPFilterResult.
|
||||
*
|
||||
* When this function returns #GST_RTSP_FILTER_REMOVE, @trans will be removed
|
||||
* from @stream.
|
||||
*
|
||||
* A return value of #GST_RTSP_FILTER_KEEP will leave @trans untouched in
|
||||
* @stream.
|
||||
*
|
||||
* A value of #GST_RTSP_FILTER_REF will add @trans to the result #GList of
|
||||
* gst_rtsp_stream_transport_filter().
|
||||
*
|
||||
* Returns: a #GstRTSPFilterResult.
|
||||
*/
|
||||
typedef GstRTSPFilterResult (*GstRTSPStreamTransportFilterFunc) (GstRTSPStream *stream,
|
||||
GstRTSPStreamTransport *trans,
|
||||
gpointer user_data);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GList * gst_rtsp_stream_transport_filter (GstRTSPStream *stream,
|
||||
GstRTSPStreamTransportFilterFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPStream, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_STREAM_H__ */
|
565
gst/rtsp-server/rtsp-thread-pool.c
Normal file
565
gst/rtsp-server/rtsp-thread-pool.c
Normal file
|
@ -0,0 +1,565 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2013 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-thread-pool
|
||||
* @short_description: A pool of threads
|
||||
* @see_also: #GstRTSPMedia, #GstRTSPClient
|
||||
*
|
||||
* A #GstRTSPThreadPool manages reusable threads for various server tasks.
|
||||
* Currently the defined thread types can be found in #GstRTSPThreadType.
|
||||
*
|
||||
* Threads of type #GST_RTSP_THREAD_TYPE_CLIENT are used to handle requests from
|
||||
* a connected client. With gst_rtsp_thread_pool_get_max_threads() a maximum
|
||||
* number of threads can be set after which the pool will start to reuse the
|
||||
* same thread for multiple clients.
|
||||
*
|
||||
* Threads of type #GST_RTSP_THREAD_TYPE_MEDIA will be used to perform the state
|
||||
* changes of the media pipelines and handle its bus messages.
|
||||
*
|
||||
* gst_rtsp_thread_pool_get_thread() can be used to create a #GstRTSPThread
|
||||
* object of the right type. The thread object contains a mainloop and context
|
||||
* that run in a seperate thread and can be used to attached sources to.
|
||||
*
|
||||
* gst_rtsp_thread_reuse() can be used to reuse a thread for multiple purposes.
|
||||
* If all gst_rtsp_thread_reuse() calls are matched with a
|
||||
* gst_rtsp_thread_stop() call, the mainloop will be quit and the thread will
|
||||
* stop.
|
||||
*
|
||||
* To configure the threads, a subclass of this object should be made and the
|
||||
* virtual methods should be overriden to implement the desired functionality.
|
||||
*
|
||||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-thread-pool.h"
|
||||
|
||||
typedef struct _GstRTSPThreadImpl
|
||||
{
|
||||
GstRTSPThread thread;
|
||||
|
||||
gint reused;
|
||||
GSource *source;
|
||||
/* FIXME, the source has to be part of GstRTSPThreadImpl, due to a bug in GLib:
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=720186 */
|
||||
} GstRTSPThreadImpl;
|
||||
|
||||
GST_DEFINE_MINI_OBJECT_TYPE (GstRTSPThread, gst_rtsp_thread);
|
||||
|
||||
static void gst_rtsp_thread_init (GstRTSPThreadImpl * impl);
|
||||
|
||||
static void
|
||||
_gst_rtsp_thread_free (GstRTSPThreadImpl * impl)
|
||||
{
|
||||
GST_DEBUG ("free thread %p", impl);
|
||||
|
||||
g_source_unref (impl->source);
|
||||
g_main_loop_unref (impl->thread.loop);
|
||||
g_main_context_unref (impl->thread.context);
|
||||
g_slice_free1 (sizeof (GstRTSPThreadImpl), impl);
|
||||
}
|
||||
|
||||
static GstRTSPThread *
|
||||
_gst_rtsp_thread_copy (GstRTSPThreadImpl * impl)
|
||||
{
|
||||
GstRTSPThreadImpl *copy;
|
||||
|
||||
GST_DEBUG ("copy thread %p", impl);
|
||||
|
||||
copy = g_slice_new0 (GstRTSPThreadImpl);
|
||||
gst_rtsp_thread_init (copy);
|
||||
copy->thread.context = g_main_context_ref (impl->thread.context);
|
||||
copy->thread.loop = g_main_loop_ref (impl->thread.loop);
|
||||
|
||||
return GST_RTSP_THREAD (copy);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_init (GstRTSPThreadImpl * impl)
|
||||
{
|
||||
gst_mini_object_init (GST_MINI_OBJECT_CAST (impl), 0,
|
||||
GST_TYPE_RTSP_THREAD,
|
||||
(GstMiniObjectCopyFunction) _gst_rtsp_thread_copy, NULL,
|
||||
(GstMiniObjectFreeFunction) _gst_rtsp_thread_free);
|
||||
|
||||
g_atomic_int_set (&impl->reused, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_new:
|
||||
* @type: the thread type
|
||||
*
|
||||
* Create a new thread object that can run a mainloop.
|
||||
*
|
||||
* Returns: (transfer full): a #GstRTSPThread.
|
||||
*/
|
||||
GstRTSPThread *
|
||||
gst_rtsp_thread_new (GstRTSPThreadType type)
|
||||
{
|
||||
GstRTSPThreadImpl *impl;
|
||||
|
||||
impl = g_slice_new0 (GstRTSPThreadImpl);
|
||||
|
||||
gst_rtsp_thread_init (impl);
|
||||
impl->thread.type = type;
|
||||
impl->thread.context = g_main_context_new ();
|
||||
impl->thread.loop = g_main_loop_new (impl->thread.context, TRUE);
|
||||
|
||||
return GST_RTSP_THREAD (impl);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_reuse:
|
||||
* @thread: (transfer none): a #GstRTSPThread
|
||||
*
|
||||
* Reuse the mainloop of @thread
|
||||
*
|
||||
* Returns: %TRUE if the mainloop could be reused
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_thread_reuse (GstRTSPThread * thread)
|
||||
{
|
||||
GstRTSPThreadImpl *impl = (GstRTSPThreadImpl *) thread;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_THREAD (thread), FALSE);
|
||||
|
||||
GST_DEBUG ("reuse thread %p", thread);
|
||||
|
||||
res = g_atomic_int_add (&impl->reused, 1) > 0;
|
||||
if (res)
|
||||
gst_rtsp_thread_ref (thread);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_quit (GstRTSPThread * thread)
|
||||
{
|
||||
GST_DEBUG ("stop mainloop of thread %p", thread);
|
||||
g_main_loop_quit (thread->loop);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_stop:
|
||||
* @thread: (transfer full): a #GstRTSPThread
|
||||
*
|
||||
* Stop and unref @thread. When no threads are using the mainloop, the thread
|
||||
* will be stopped and the final ref to @thread will be released.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_thread_stop (GstRTSPThread * thread)
|
||||
{
|
||||
GstRTSPThreadImpl *impl = (GstRTSPThreadImpl *) thread;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_THREAD (thread));
|
||||
|
||||
GST_DEBUG ("stop thread %p", thread);
|
||||
|
||||
if (g_atomic_int_dec_and_test (&impl->reused)) {
|
||||
GST_DEBUG ("add idle source to quit mainloop of thread %p", thread);
|
||||
impl->source = g_idle_source_new ();
|
||||
g_source_set_callback (impl->source, (GSourceFunc) do_quit,
|
||||
thread, (GDestroyNotify) gst_rtsp_thread_unref);
|
||||
g_source_attach (impl->source, thread->context);
|
||||
} else
|
||||
gst_rtsp_thread_unref (thread);
|
||||
}
|
||||
|
||||
struct _GstRTSPThreadPoolPrivate
|
||||
{
|
||||
GMutex lock;
|
||||
|
||||
gint max_threads;
|
||||
/* currently used mainloops */
|
||||
GQueue threads;
|
||||
};
|
||||
|
||||
#define DEFAULT_MAX_THREADS 1
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MAX_THREADS,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtsp_thread_pool_debug);
|
||||
#define GST_CAT_DEFAULT rtsp_thread_pool_debug
|
||||
|
||||
static GQuark thread_pool;
|
||||
|
||||
static void gst_rtsp_thread_pool_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_thread_pool_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtsp_thread_pool_finalize (GObject * obj);
|
||||
|
||||
static gpointer do_loop (GstRTSPThread * thread);
|
||||
static GstRTSPThread *default_get_thread (GstRTSPThreadPool * pool,
|
||||
GstRTSPThreadType type, GstRTSPContext * ctx);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GstRTSPThreadPool, gst_rtsp_thread_pool,
|
||||
G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_pool_class_init (GstRTSPThreadPoolClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gst_rtsp_thread_pool_get_property;
|
||||
gobject_class->set_property = gst_rtsp_thread_pool_set_property;
|
||||
gobject_class->finalize = gst_rtsp_thread_pool_finalize;
|
||||
|
||||
/**
|
||||
* GstRTSPThreadPool::max-threads:
|
||||
*
|
||||
* The maximum amount of threads to use for client connections. A value of
|
||||
* 0 means to use only the mainloop, -1 means an unlimited amount of
|
||||
* threads.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_MAX_THREADS,
|
||||
g_param_spec_int ("max-threads", "Max Threads",
|
||||
"The maximum amount of threads to use for client connections "
|
||||
"(0 = only mainloop, -1 = unlimited)", -1, G_MAXINT,
|
||||
DEFAULT_MAX_THREADS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
klass->get_thread = default_get_thread;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtsp_thread_pool_debug, "rtspthreadpool", 0,
|
||||
"GstRTSPThreadPool");
|
||||
|
||||
thread_pool = g_quark_from_string ("gst.rtsp.thread.pool");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_pool_init (GstRTSPThreadPool * pool)
|
||||
{
|
||||
GstRTSPThreadPoolPrivate *priv;
|
||||
|
||||
pool->priv = priv = gst_rtsp_thread_pool_get_instance_private (pool);
|
||||
|
||||
g_mutex_init (&priv->lock);
|
||||
priv->max_threads = DEFAULT_MAX_THREADS;
|
||||
g_queue_init (&priv->threads);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_pool_finalize (GObject * obj)
|
||||
{
|
||||
GstRTSPThreadPool *pool = GST_RTSP_THREAD_POOL (obj);
|
||||
GstRTSPThreadPoolPrivate *priv = pool->priv;
|
||||
|
||||
GST_INFO ("finalize pool %p", pool);
|
||||
|
||||
g_queue_clear (&priv->threads);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_thread_pool_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_pool_get_property (GObject * object, guint propid,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPThreadPool *pool = GST_RTSP_THREAD_POOL (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_MAX_THREADS:
|
||||
g_value_set_int (value, gst_rtsp_thread_pool_get_max_threads (pool));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_thread_pool_set_property (GObject * object, guint propid,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRTSPThreadPool *pool = GST_RTSP_THREAD_POOL (object);
|
||||
|
||||
switch (propid) {
|
||||
case PROP_MAX_THREADS:
|
||||
gst_rtsp_thread_pool_set_max_threads (pool, g_value_get_int (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
do_loop (GstRTSPThread * thread)
|
||||
{
|
||||
GstRTSPThreadPoolPrivate *priv;
|
||||
GstRTSPThreadPoolClass *klass;
|
||||
GstRTSPThreadPool *pool;
|
||||
|
||||
pool = gst_mini_object_get_qdata (GST_MINI_OBJECT (thread), thread_pool);
|
||||
priv = pool->priv;
|
||||
|
||||
klass = GST_RTSP_THREAD_POOL_GET_CLASS (pool);
|
||||
|
||||
if (klass->thread_enter)
|
||||
klass->thread_enter (pool, thread);
|
||||
|
||||
GST_INFO ("enter mainloop of thread %p", thread);
|
||||
g_main_loop_run (thread->loop);
|
||||
GST_INFO ("exit mainloop of thread %p", thread);
|
||||
|
||||
if (klass->thread_leave)
|
||||
klass->thread_leave (pool, thread);
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_queue_remove (&priv->threads, thread);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
gst_rtsp_thread_unref (thread);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_pool_new:
|
||||
*
|
||||
* Create a new #GstRTSPThreadPool instance.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPThreadPool
|
||||
*/
|
||||
GstRTSPThreadPool *
|
||||
gst_rtsp_thread_pool_new (void)
|
||||
{
|
||||
GstRTSPThreadPool *result;
|
||||
|
||||
result = g_object_new (GST_TYPE_RTSP_THREAD_POOL, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_pool_set_max_threads:
|
||||
* @pool: a #GstRTSPThreadPool
|
||||
* @max_threads: maximum threads
|
||||
*
|
||||
* Set the maximum threads used by the pool to handle client requests.
|
||||
* A value of 0 will use the pool mainloop, a value of -1 will use an
|
||||
* unlimited number of threads.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_thread_pool_set_max_threads (GstRTSPThreadPool * pool,
|
||||
gint max_threads)
|
||||
{
|
||||
GstRTSPThreadPoolPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_THREAD_POOL (pool));
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->max_threads = max_threads;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_pool_get_max_threads:
|
||||
* @pool: a #GstRTSPThreadPool
|
||||
*
|
||||
* Get the maximum number of threads used for client connections.
|
||||
* See gst_rtsp_thread_pool_set_max_threads().
|
||||
*
|
||||
* Returns: the maximum number of threads.
|
||||
*/
|
||||
gint
|
||||
gst_rtsp_thread_pool_get_max_threads (GstRTSPThreadPool * pool)
|
||||
{
|
||||
GstRTSPThreadPoolPrivate *priv;
|
||||
gint res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_THREAD_POOL (pool), -1);
|
||||
|
||||
priv = pool->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
res = priv->max_threads;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstRTSPThread *
|
||||
make_thread (GstRTSPThreadPool * pool, GstRTSPThreadType type,
|
||||
GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPThreadPoolClass *klass;
|
||||
GstRTSPThread *thread;
|
||||
|
||||
klass = GST_RTSP_THREAD_POOL_GET_CLASS (pool);
|
||||
|
||||
thread = gst_rtsp_thread_new (type);
|
||||
gst_mini_object_set_qdata (GST_MINI_OBJECT (thread), thread_pool,
|
||||
g_object_ref (pool), g_object_unref);
|
||||
|
||||
GST_DEBUG_OBJECT (pool, "new thread %p", thread);
|
||||
|
||||
if (klass->configure_thread)
|
||||
klass->configure_thread (pool, thread, ctx);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
static GstRTSPThread *
|
||||
default_get_thread (GstRTSPThreadPool * pool,
|
||||
GstRTSPThreadType type, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPThreadPoolPrivate *priv = pool->priv;
|
||||
GstRTSPThreadPoolClass *klass;
|
||||
GstRTSPThread *thread;
|
||||
GError *error = NULL;
|
||||
|
||||
klass = GST_RTSP_THREAD_POOL_GET_CLASS (pool);
|
||||
|
||||
switch (type) {
|
||||
case GST_RTSP_THREAD_TYPE_CLIENT:
|
||||
if (priv->max_threads == 0) {
|
||||
/* no threads allowed */
|
||||
GST_DEBUG_OBJECT (pool, "no client threads allowed");
|
||||
thread = NULL;
|
||||
} else {
|
||||
g_mutex_lock (&priv->lock);
|
||||
retry:
|
||||
if (priv->max_threads > 0 &&
|
||||
g_queue_get_length (&priv->threads) >= priv->max_threads) {
|
||||
/* max threads reached, recycle from queue */
|
||||
thread = g_queue_pop_head (&priv->threads);
|
||||
GST_DEBUG_OBJECT (pool, "recycle client thread %p", thread);
|
||||
if (!gst_rtsp_thread_reuse (thread)) {
|
||||
GST_DEBUG_OBJECT (pool, "thread %p stopping, retry", thread);
|
||||
/* this can happen if we just decremented the reuse counter of the
|
||||
* thread and signaled the mainloop that it should stop. We leave
|
||||
* the thread out of the queue now, there is no point to add it
|
||||
* again, it will be removed from the mainloop otherwise after it
|
||||
* stops. */
|
||||
goto retry;
|
||||
}
|
||||
} else {
|
||||
/* make more threads */
|
||||
GST_DEBUG_OBJECT (pool, "make new client thread");
|
||||
thread = make_thread (pool, type, ctx);
|
||||
|
||||
if (!g_thread_pool_push (klass->pool, gst_rtsp_thread_ref (thread),
|
||||
&error))
|
||||
goto thread_error;
|
||||
}
|
||||
g_queue_push_tail (&priv->threads, thread);
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
break;
|
||||
case GST_RTSP_THREAD_TYPE_MEDIA:
|
||||
GST_DEBUG_OBJECT (pool, "make new media thread");
|
||||
thread = make_thread (pool, type, ctx);
|
||||
|
||||
if (!g_thread_pool_push (klass->pool, gst_rtsp_thread_ref (thread),
|
||||
&error))
|
||||
goto thread_error;
|
||||
break;
|
||||
default:
|
||||
thread = NULL;
|
||||
break;
|
||||
}
|
||||
return thread;
|
||||
|
||||
/* ERRORS */
|
||||
thread_error:
|
||||
{
|
||||
GST_ERROR_OBJECT (pool, "failed to push thread %s", error->message);
|
||||
gst_rtsp_thread_unref (thread);
|
||||
/* drop also the ref dedicated for the pool */
|
||||
gst_rtsp_thread_unref (thread);
|
||||
g_clear_error (&error);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_pool_get_thread:
|
||||
* @pool: a #GstRTSPThreadPool
|
||||
* @type: the #GstRTSPThreadType
|
||||
* @ctx: (transfer none): a #GstRTSPContext
|
||||
*
|
||||
* Get a new #GstRTSPThread for @type and @ctx.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a new #GstRTSPThread,
|
||||
* gst_rtsp_thread_stop() after usage
|
||||
*/
|
||||
GstRTSPThread *
|
||||
gst_rtsp_thread_pool_get_thread (GstRTSPThreadPool * pool,
|
||||
GstRTSPThreadType type, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPThreadPoolClass *klass;
|
||||
GstRTSPThread *result = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_THREAD_POOL (pool), NULL);
|
||||
|
||||
klass = GST_RTSP_THREAD_POOL_GET_CLASS (pool);
|
||||
|
||||
/* We want to be thread safe as there might be 2 threads wanting to get new
|
||||
* #GstRTSPThread at the same time
|
||||
*/
|
||||
if (G_UNLIKELY (!g_atomic_pointer_get (&klass->pool))) {
|
||||
GThreadPool *t_pool;
|
||||
t_pool = g_thread_pool_new ((GFunc) do_loop, klass, -1, FALSE, NULL);
|
||||
if (!g_atomic_pointer_compare_and_exchange (&klass->pool,
|
||||
(GThreadPool *) NULL, t_pool))
|
||||
g_thread_pool_free (t_pool, FALSE, TRUE);
|
||||
}
|
||||
|
||||
if (klass->get_thread)
|
||||
result = klass->get_thread (pool, type, ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_pool_cleanup:
|
||||
*
|
||||
* Wait for all tasks to be stopped and free all allocated resources. This is
|
||||
* mainly used in test suites to ensure proper cleanup of internal data
|
||||
* structures.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_thread_pool_cleanup (void)
|
||||
{
|
||||
GstRTSPThreadPoolClass *klass;
|
||||
|
||||
klass =
|
||||
GST_RTSP_THREAD_POOL_CLASS (g_type_class_ref
|
||||
(gst_rtsp_thread_pool_get_type ()));
|
||||
if (klass->pool != NULL) {
|
||||
g_thread_pool_free (klass->pool, FALSE, TRUE);
|
||||
klass->pool = NULL;
|
||||
}
|
||||
g_type_class_unref (klass);
|
||||
}
|
191
gst/rtsp-server/rtsp-thread-pool.h
Normal file
191
gst/rtsp-server/rtsp-thread-pool.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef __GST_RTSP_THREAD_POOL_H__
|
||||
#define __GST_RTSP_THREAD_POOL_H__
|
||||
|
||||
typedef struct _GstRTSPThread GstRTSPThread;
|
||||
typedef struct _GstRTSPThreadPool GstRTSPThreadPool;
|
||||
typedef struct _GstRTSPThreadPoolClass GstRTSPThreadPoolClass;
|
||||
typedef struct _GstRTSPThreadPoolPrivate GstRTSPThreadPoolPrivate;
|
||||
|
||||
#include "rtsp-client.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTSP_THREAD_POOL (gst_rtsp_thread_pool_get_type ())
|
||||
#define GST_IS_RTSP_THREAD_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_THREAD_POOL))
|
||||
#define GST_IS_RTSP_THREAD_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_THREAD_POOL))
|
||||
#define GST_RTSP_THREAD_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_THREAD_POOL, GstRTSPThreadPoolClass))
|
||||
#define GST_RTSP_THREAD_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_THREAD_POOL, GstRTSPThreadPool))
|
||||
#define GST_RTSP_THREAD_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_THREAD_POOL, GstRTSPThreadPoolClass))
|
||||
#define GST_RTSP_THREAD_POOL_CAST(obj) ((GstRTSPThreadPool*)(obj))
|
||||
#define GST_RTSP_THREAD_POOL_CLASS_CAST(klass) ((GstRTSPThreadPoolClass*)(klass))
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_thread_get_type (void);
|
||||
|
||||
#define GST_TYPE_RTSP_THREAD (gst_rtsp_thread_get_type ())
|
||||
#define GST_IS_RTSP_THREAD(obj) (GST_IS_MINI_OBJECT_TYPE (obj, GST_TYPE_RTSP_THREAD))
|
||||
#define GST_RTSP_THREAD_CAST(obj) ((GstRTSPThread*)(obj))
|
||||
#define GST_RTSP_THREAD(obj) (GST_RTSP_THREAD_CAST(obj))
|
||||
|
||||
/**
|
||||
* GstRTSPThreadType:
|
||||
* @GST_RTSP_THREAD_TYPE_CLIENT: a thread to handle the client communication
|
||||
* @GST_RTSP_THREAD_TYPE_MEDIA: a thread to handle media
|
||||
*
|
||||
* Different thread types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_RTSP_THREAD_TYPE_CLIENT,
|
||||
GST_RTSP_THREAD_TYPE_MEDIA
|
||||
} GstRTSPThreadType;
|
||||
|
||||
/**
|
||||
* GstRTSPThread:
|
||||
* @mini_object: parent #GstMiniObject
|
||||
* @type: the thread type
|
||||
* @context: a #GMainContext
|
||||
* @loop: a #GMainLoop
|
||||
*
|
||||
* Structure holding info about a mainloop running in a thread
|
||||
*/
|
||||
struct _GstRTSPThread {
|
||||
GstMiniObject mini_object;
|
||||
|
||||
GstRTSPThreadType type;
|
||||
GMainContext *context;
|
||||
GMainLoop *loop;
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPThread * gst_rtsp_thread_new (GstRTSPThreadType type);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gboolean gst_rtsp_thread_reuse (GstRTSPThread * thread);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_thread_stop (GstRTSPThread * thread);
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_ref:
|
||||
* @thread: The thread to refcount
|
||||
*
|
||||
* Increase the refcount of this thread.
|
||||
*
|
||||
* Returns: (transfer full): @thread (for convenience when doing assignments)
|
||||
*/
|
||||
static inline GstRTSPThread *
|
||||
gst_rtsp_thread_ref (GstRTSPThread * thread)
|
||||
{
|
||||
return (GstRTSPThread *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (thread));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_thread_unref:
|
||||
* @thread: (transfer full): the thread to refcount
|
||||
*
|
||||
* Decrease the refcount of an thread, freeing it if the refcount reaches 0.
|
||||
*/
|
||||
static inline void
|
||||
gst_rtsp_thread_unref (GstRTSPThread * thread)
|
||||
{
|
||||
gst_mini_object_unref (GST_MINI_OBJECT_CAST (thread));
|
||||
}
|
||||
|
||||
/**
|
||||
* GstRTSPThreadPool:
|
||||
*
|
||||
* The thread pool structure.
|
||||
*/
|
||||
struct _GstRTSPThreadPool {
|
||||
GObject parent;
|
||||
|
||||
/*< private >*/
|
||||
GstRTSPThreadPoolPrivate *priv;
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstRTSPThreadPoolClass:
|
||||
* @pool: a #GThreadPool used internally
|
||||
* @get_thread: this function should make or reuse an existing thread that runs
|
||||
* a mainloop.
|
||||
* @configure_thread: configure a thread object. this vmethod is called when
|
||||
* a new thread has been created and should be configured.
|
||||
* @thread_enter: called from the thread when it is entered
|
||||
* @thread_leave: called from the thread when it is left
|
||||
*
|
||||
* Class for managing threads.
|
||||
*/
|
||||
struct _GstRTSPThreadPoolClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
GThreadPool *pool;
|
||||
|
||||
GstRTSPThread * (*get_thread) (GstRTSPThreadPool *pool,
|
||||
GstRTSPThreadType type,
|
||||
GstRTSPContext *ctx);
|
||||
void (*configure_thread) (GstRTSPThreadPool *pool,
|
||||
GstRTSPThread * thread,
|
||||
GstRTSPContext *ctx);
|
||||
|
||||
void (*thread_enter) (GstRTSPThreadPool *pool,
|
||||
GstRTSPThread *thread);
|
||||
void (*thread_leave) (GstRTSPThreadPool *pool,
|
||||
GstRTSPThread *thread);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GType gst_rtsp_thread_pool_get_type (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPThreadPool * gst_rtsp_thread_pool_new (void);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_thread_pool_set_max_threads (GstRTSPThreadPool * pool, gint max_threads);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
gint gst_rtsp_thread_pool_get_max_threads (GstRTSPThreadPool * pool);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
GstRTSPThread * gst_rtsp_thread_pool_get_thread (GstRTSPThreadPool *pool,
|
||||
GstRTSPThreadType type,
|
||||
GstRTSPContext *ctx);
|
||||
|
||||
GST_RTSP_SERVER_API
|
||||
void gst_rtsp_thread_pool_cleanup (void);
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPThread, gst_rtsp_thread_unref)
|
||||
#endif
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPThreadPool, gst_object_unref)
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTSP_THREAD_POOL_H__ */
|
302
gst/rtsp-server/rtsp-token.c
Normal file
302
gst/rtsp-server/rtsp-token.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2010 Wim Taymans <wim.taymans at gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* SECTION:rtsp-token
|
||||
* @short_description: Roles and permissions for a client
|
||||
* @see_also: #GstRTSPClient, #GstRTSPPermissions, #GstRTSPAuth
|
||||
*
|
||||
* A #GstRTSPToken contains the permissions and roles of the user
|
||||
* performing the current request. A token is usually created when a user is
|
||||
* authenticated by the #GstRTSPAuth object and is then placed as the current
|
||||
* token for the current request.
|
||||
*
|
||||
* #GstRTSPAuth can use the token and its contents to check authorization for
|
||||
* various operations by comparing the token to the #GstRTSPPermissions of the
|
||||
* object.
|
||||
*
|
||||
* The accepted values of the token are entirely defined by the #GstRTSPAuth
|
||||
* object that implements the security policy.
|
||||
*
|
||||
* Last reviewed on 2013-07-15 (1.0.0)
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-token.h"
|
||||
|
||||
typedef struct _GstRTSPTokenImpl
|
||||
{
|
||||
GstRTSPToken token;
|
||||
|
||||
GstStructure *structure;
|
||||
} GstRTSPTokenImpl;
|
||||
|
||||
#define GST_RTSP_TOKEN_STRUCTURE(t) (((GstRTSPTokenImpl *)(t))->structure)
|
||||
|
||||
//GST_DEBUG_CATEGORY_STATIC (rtsp_token_debug);
|
||||
//#define GST_CAT_DEFAULT rtsp_token_debug
|
||||
|
||||
GST_DEFINE_MINI_OBJECT_TYPE (GstRTSPToken, gst_rtsp_token);
|
||||
|
||||
static void gst_rtsp_token_init (GstRTSPTokenImpl * token,
|
||||
GstStructure * structure);
|
||||
|
||||
static void
|
||||
_gst_rtsp_token_free (GstRTSPToken * token)
|
||||
{
|
||||
GstRTSPTokenImpl *impl = (GstRTSPTokenImpl *) token;
|
||||
|
||||
gst_structure_set_parent_refcount (impl->structure, NULL);
|
||||
gst_structure_free (impl->structure);
|
||||
|
||||
g_slice_free1 (sizeof (GstRTSPTokenImpl), token);
|
||||
}
|
||||
|
||||
static GstRTSPToken *
|
||||
_gst_rtsp_token_copy (GstRTSPTokenImpl * token)
|
||||
{
|
||||
GstRTSPTokenImpl *copy;
|
||||
GstStructure *structure;
|
||||
|
||||
structure = gst_structure_copy (token->structure);
|
||||
|
||||
copy = g_slice_new0 (GstRTSPTokenImpl);
|
||||
gst_rtsp_token_init (copy, structure);
|
||||
|
||||
return (GstRTSPToken *) copy;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtsp_token_init (GstRTSPTokenImpl * token, GstStructure * structure)
|
||||
{
|
||||
gst_mini_object_init (GST_MINI_OBJECT_CAST (token), 0,
|
||||
GST_TYPE_RTSP_TOKEN,
|
||||
(GstMiniObjectCopyFunction) _gst_rtsp_token_copy, NULL,
|
||||
(GstMiniObjectFreeFunction) _gst_rtsp_token_free);
|
||||
|
||||
token->structure = structure;
|
||||
gst_structure_set_parent_refcount (token->structure,
|
||||
&token->token.mini_object.refcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_new_empty: (rename-to gst_rtsp_token_new)
|
||||
*
|
||||
* Create a new empty Authorization token.
|
||||
*
|
||||
* Returns: (transfer full): a new empty authorization token.
|
||||
*/
|
||||
GstRTSPToken *
|
||||
gst_rtsp_token_new_empty (void)
|
||||
{
|
||||
GstRTSPTokenImpl *token;
|
||||
GstStructure *s;
|
||||
|
||||
s = gst_structure_new_empty ("GstRTSPToken");
|
||||
g_return_val_if_fail (s != NULL, NULL);
|
||||
|
||||
token = g_slice_new0 (GstRTSPTokenImpl);
|
||||
gst_rtsp_token_init (token, s);
|
||||
|
||||
return (GstRTSPToken *) token;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_new: (skip)
|
||||
* @firstfield: the first fieldname
|
||||
* @...: additional arguments
|
||||
*
|
||||
* Create a new Authorization token with the given fieldnames and values.
|
||||
* Arguments are given similar to gst_structure_new().
|
||||
*
|
||||
* Returns: (transfer full): a new authorization token.
|
||||
*/
|
||||
GstRTSPToken *
|
||||
gst_rtsp_token_new (const gchar * firstfield, ...)
|
||||
{
|
||||
GstRTSPToken *result;
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, firstfield);
|
||||
result = gst_rtsp_token_new_valist (firstfield, var_args);
|
||||
va_end (var_args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_new_valist: (skip)
|
||||
* @firstfield: the first fieldname
|
||||
* @var_args: additional arguments
|
||||
*
|
||||
* Create a new Authorization token with the given fieldnames and values.
|
||||
* Arguments are given similar to gst_structure_new_valist().
|
||||
*
|
||||
* Returns: (transfer full): a new authorization token.
|
||||
*/
|
||||
GstRTSPToken *
|
||||
gst_rtsp_token_new_valist (const gchar * firstfield, va_list var_args)
|
||||
{
|
||||
GstRTSPToken *token;
|
||||
GstStructure *s;
|
||||
|
||||
g_return_val_if_fail (firstfield != NULL, NULL);
|
||||
|
||||
token = gst_rtsp_token_new_empty ();
|
||||
s = GST_RTSP_TOKEN_STRUCTURE (token);
|
||||
gst_structure_set_valist (s, firstfield, var_args);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_set_string:
|
||||
* @token: The #GstRTSPToken.
|
||||
* @field: field to set
|
||||
* @string_value: string value to set
|
||||
*
|
||||
* Sets a string value on @token.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_token_set_string (GstRTSPToken * token, const gchar * field,
|
||||
const gchar * string_value)
|
||||
{
|
||||
GstStructure *s;
|
||||
|
||||
g_return_if_fail (token != NULL);
|
||||
g_return_if_fail (field != NULL);
|
||||
g_return_if_fail (string_value != NULL);
|
||||
|
||||
s = gst_rtsp_token_writable_structure (token);
|
||||
if (s != NULL)
|
||||
gst_structure_set (s, field, G_TYPE_STRING, string_value, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_set_bool:
|
||||
* @token: The #GstRTSPToken.
|
||||
* @field: field to set
|
||||
* @bool_value: boolean value to set
|
||||
*
|
||||
* Sets a boolean value on @token.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
void
|
||||
gst_rtsp_token_set_bool (GstRTSPToken * token, const gchar * field,
|
||||
gboolean bool_value)
|
||||
{
|
||||
GstStructure *s;
|
||||
|
||||
g_return_if_fail (token != NULL);
|
||||
g_return_if_fail (field != NULL);
|
||||
|
||||
s = gst_rtsp_token_writable_structure (token);
|
||||
if (s != NULL)
|
||||
gst_structure_set (s, field, G_TYPE_BOOLEAN, bool_value, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_get_structure:
|
||||
* @token: The #GstRTSPToken.
|
||||
*
|
||||
* Access the structure of the token.
|
||||
*
|
||||
* Returns: (transfer none): The structure of the token. The structure is still
|
||||
* owned by the token, which means that you should not free it and that the
|
||||
* pointer becomes invalid when you free the token.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
const GstStructure *
|
||||
gst_rtsp_token_get_structure (GstRTSPToken * token)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_TOKEN (token), NULL);
|
||||
|
||||
return GST_RTSP_TOKEN_STRUCTURE (token);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_writable_structure:
|
||||
* @token: The #GstRTSPToken.
|
||||
*
|
||||
* Get a writable version of the structure.
|
||||
*
|
||||
* Returns: (transfer none): The structure of the token. The structure is still
|
||||
* owned by the token, which means that you should not free it and that the
|
||||
* pointer becomes invalid when you free the token. This function checks if
|
||||
* @token is writable and will never return %NULL.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
GstStructure *
|
||||
gst_rtsp_token_writable_structure (GstRTSPToken * token)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_TOKEN (token), NULL);
|
||||
g_return_val_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
|
||||
(token)), NULL);
|
||||
|
||||
return GST_RTSP_TOKEN_STRUCTURE (token);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_get_string:
|
||||
* @token: a #GstRTSPToken
|
||||
* @field: a field name
|
||||
*
|
||||
* Get the string value of @field in @token.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the string value of @field in
|
||||
* @token or %NULL when @field is not defined in @token. The string
|
||||
* becomes invalid when you free @token.
|
||||
*/
|
||||
const gchar *
|
||||
gst_rtsp_token_get_string (GstRTSPToken * token, const gchar * field)
|
||||
{
|
||||
return gst_structure_get_string (GST_RTSP_TOKEN_STRUCTURE (token), field);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_token_is_allowed:
|
||||
* @token: a #GstRTSPToken
|
||||
* @field: a field name
|
||||
*
|
||||
* Check if @token has a boolean @field and if it is set to %TRUE.
|
||||
*
|
||||
* Returns: %TRUE if @token has a boolean field named @field set to %TRUE.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_token_is_allowed (GstRTSPToken * token, const gchar * field)
|
||||
{
|
||||
gboolean result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_TOKEN (token), FALSE);
|
||||
g_return_val_if_fail (field != NULL, FALSE);
|
||||
|
||||
if (!gst_structure_get_boolean (GST_RTSP_TOKEN_STRUCTURE (token), field,
|
||||
&result))
|
||||
result = FALSE;
|
||||
|
||||
return result;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue