Merging gst-rtsp-server

This commit is contained in:
Thibault Saunier 2021-09-24 16:15:18 -03:00
commit a9d9189aa2
128 changed files with 73730 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*~
/build
/_build
/b/

1
.gitlab-ci.yml Normal file
View file

@ -0,0 +1 @@
include: "https://gitlab.freedesktop.org/gstreamer/gst-ci/raw/master/gitlab/ci_template.yml"

1
AUTHORS Normal file
View file

@ -0,0 +1 @@
Wim Taymans <wim.taymans@collabora.co.uk>

503
COPYING Normal file
View 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
View 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!

14309
ChangeLog Normal file

File diff suppressed because it is too large Load diff

299
NEWS Normal file
View 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 thats 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 dont 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
- use a config file to configure the server
- error recovery

498
docs/README Normal file
View 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.

View 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
View 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
View file

@ -0,0 +1 @@
# GStreamer RTSP Server

99
docs/meson.build Normal file
View 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
View file

@ -0,0 +1 @@
# rtspclientsink

1
docs/plugin-sitemap.txt Normal file
View file

@ -0,0 +1 @@
gst-index

2
docs/sitemap.md Normal file
View file

@ -0,0 +1,2 @@
gi-index
gst-index

1
docs/sitemap.txt Normal file
View file

@ -0,0 +1 @@
gi-index

40
examples/meson.build Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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
View 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
View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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
View 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
View 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
View 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;
}

View 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;
}

View 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
View 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
View 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;
}
}

View 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
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
subdir('rtsp-server')
if not get_option('rtspclientsink').disabled()
subdir('rtsp-sink')
endif

View 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)

View 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;
}

View 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

File diff suppressed because it is too large Load diff

230
gst/rtsp-server/rtsp-auth.h Normal file
View 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__ */

File diff suppressed because it is too large Load diff

View 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__ */

View 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 (&current_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 (&current_context);
l = g_slist_prepend (l, ctx);
g_private_set (&current_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 (&current_context);
g_return_if_fail (l != NULL);
g_return_if_fail (l->data == ctx);
l = g_slist_delete_link (l, l);
g_private_set (&current_context, l);
}

View 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__ */

View 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;
}

View 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__ */

View 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;
}
}

View 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__ */

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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__ */

View 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);
}

View 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__ */

View 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;
}

View 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__ */

View 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);
}

View 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__ */

View 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;
}

View 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__ */

View 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)
{
}

View 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__ */

View 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;
}

View 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__ */

View 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,
&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;
}

View 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
View 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;
}

View 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__ */

View 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__ */

View 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__ */

View 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__ */

File diff suppressed because it is too large Load diff

View 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__ */

View 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;
}

View 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__ */

View 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;
}

View 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__ */

View 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

View 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__ */

View 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);
}

View 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__ */

File diff suppressed because it is too large Load diff

View 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__ */

View 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);
}

View 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__ */

View 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