Loading...
Searching...
No Matches
GStreamerPipelineParse.hpp
Go to the documentation of this file.
1#pragma once
2
14#include <Video/GStreamerCompatibility.hpp>
15
16#include <ossia/detail/parse_strict.hpp>
17
18extern "C" {
19#include <libavutil/pixfmt.h>
20}
21
22#include <ctre.hpp>
23
24#include <algorithm>
25#include <functional>
26#include <string>
27#include <string_view>
28#include <vector>
29
30namespace Gfx::GStreamer
31{
32
34{
35 std::string name;
36 bool is_video{};
37 int width{};
38 int height{};
39 AVPixelFormat pixfmt{AV_PIX_FMT_NONE};
40 // The GStreamer name the caps carried: several GStreamer formats share one
41 // AVPixelFormat but not its plane order (see Video::gstreamerPlaneOrder).
42 std::string gst_format;
43 int channels{};
44 int rate{};
45 // Set when the negotiated caps named a format with no AVPixelFormat: the
46 // samples are dropped, and the reason is logged once rather than per frame.
47 bool unsupported_format{};
48 // Set when a buffer arrived with no GstVideoMeta and a size that matches
49 // neither the tightly packed nor the row-aligned layout: its plane layout is
50 // unknowable, so the samples are dropped and the reason logged once.
51 bool unsupported_layout{};
52 std::function<void(int, int, AVPixelFormat)> on_format_change;
53};
54
55inline constexpr auto appsink_name_rexp
56 = ctll::fixed_string{R"(appsink\b[^!]*?\bname\s*=\s*([A-Za-z0-9_]+))"};
57inline constexpr auto elem_name_rexp
58 = ctll::fixed_string{R"(\bname\s*=\s*([A-Za-z0-9_]+))"};
59
60inline std::vector<std::string> find_appsink_names(const std::string& pipeline)
61{
62 std::vector<std::string> names;
63 for(auto m : ctre::search_all<appsink_name_rexp>(pipeline))
64 names.push_back(m.get<1>().to_string());
65 return names;
66}
67
68// Find all name=<identifier> patterns in the pipeline string
69// (covers any element, not just appsink/appsrc)
70inline std::vector<std::string> find_all_named_elements(const std::string& pipeline)
71{
72 std::vector<std::string> names;
73 for(auto m : ctre::search_all<elem_name_rexp>(pipeline))
74 names.push_back(m.get<1>().to_string());
75 return names;
76}
77
78inline constexpr auto channels_rexp
79 = ctll::fixed_string{R"(channels=(?:\‍(int\))?\s*([0-9]+))"};
80inline constexpr auto rate_rexp
81 = ctll::fixed_string{R"((?:^|[^A-Za-z0-9_\-])rate=(?:\‍(int\))?\s*([0-9]+))"};
82inline constexpr auto width_rexp
83 = ctll::fixed_string{R"(width=(?:\‍(int\))?\s*([0-9]+))"};
84inline constexpr auto height_rexp
85 = ctll::fixed_string{R"(height=(?:\‍(int\))?\s*([0-9]+))"};
86inline constexpr auto format_rexp
87 = ctll::fixed_string{R"(format=(?:\‍(string\))?\s*([A-Za-z0-9]+))"};
88
89template <ctll::fixed_string Rexp>
90inline int last_int_of(std::string_view str, int fallback)
91{
92 int ret = fallback;
93 for(auto m : ctre::search_all<Rexp>(str))
94 if(auto v = ossia::parse_strict<int>(m.template get<1>().to_view()))
95 ret = *v;
96 return ret;
97}
98
99// Guess an appsink's media type from the pipeline string when caps are
100// not negotiated yet: look for the last audio/video-ish token occurring
101// before "appsink ... name=<sink_name>".
102inline void classify_from_pipeline_string(
103 const std::string& pipeline, const std::string& sink_name, AppsinkInfo& info)
104{
105 std::size_t sink_pos = std::string::npos;
106 for(auto m : ctre::search_all<appsink_name_rexp>(pipeline))
107 {
108 if(m.get<1>().to_view() == sink_name)
109 {
110 sink_pos = std::distance(pipeline.begin(), m.get<0>().begin());
111 break;
112 }
113 }
114 const std::string_view before
115 = std::string_view{pipeline}.substr(0, sink_pos);
116
117 static constexpr std::string_view audio_tokens[]
118 = {"audio/x-raw", "audioconvert", "audioresample", "audiotestsrc"};
119 static constexpr std::string_view video_tokens[]
120 = {"video/x-raw", "videoconvert", "videoscale", "videotestsrc", "videorate"};
121
122 std::size_t last_audio = std::string::npos, last_video = std::string::npos;
123 for(auto tok : audio_tokens)
124 if(auto p = before.rfind(tok); p != std::string::npos)
125 last_audio = (last_audio == std::string::npos) ? p : std::max(last_audio, p);
126 for(auto tok : video_tokens)
127 if(auto p = before.rfind(tok); p != std::string::npos)
128 last_video = (last_video == std::string::npos) ? p : std::max(last_video, p);
129
130 const bool is_audio = last_audio != std::string::npos
131 && (last_video == std::string::npos || last_audio > last_video);
132 if(is_audio)
133 {
134 info.is_video = false;
135 info.channels = last_int_of<channels_rexp>(before, 2);
136 info.rate = last_int_of<rate_rexp>(before, 48000);
137 }
138 else
139 {
140 info.is_video = true;
141 info.width = last_int_of<width_rexp>(before, 640);
142 info.height = last_int_of<height_rexp>(before, 480);
143 info.pixfmt = AV_PIX_FMT_RGBA;
144 std::string format;
145 for(auto m : ctre::search_all<format_rexp>(before))
146 format = m.get<1>().to_string();
147 if(!format.empty())
148 {
149 auto& map = ::Video::gstreamerToLibav();
150 if(auto it = map.find(format); it != map.end())
151 {
152 info.pixfmt = it->second;
153 info.gst_format = format;
154 }
155 }
156 }
157}
158
159}
Definition GStreamerPipelineParse.hpp:34