mediagst.c (7588B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2026 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/mediagst.c 22 * @brief plumbing between an extraction context and a GStreamer pipeline 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "mediagst.h" 27 28 #include <gst/app/gstappsrc.h> 29 30 31 /** 32 * Implementation of GstAppSrc's "need-data" callback. Reads data from 33 * the extraction context and passes it to GStreamer. 34 * 35 * @param appsrc the source that ran out of data 36 * @param size number of bytes requested 37 * @param io our execution context 38 */ 39 static void 40 feed_data (GstElement *appsrc, 41 guint size, 42 struct EXTRACTOR_GstIO *io) 43 { 44 GstBuffer *buffer; 45 GstMemory *mem; 46 GstMapInfo mi; 47 guint accumulated; 48 ssize_t data_len; 49 uint8_t *le_data; 50 51 if ( (0 != io->length) && 52 (io->offset >= io->length) ) 53 { 54 gst_app_src_end_of_stream (GST_APP_SRC (appsrc)); 55 return; 56 } 57 if ( (0 != io->length) && 58 (io->offset + size > io->length) ) 59 size = io->length - io->offset; 60 if (0 == size) 61 { 62 gst_app_src_end_of_stream (GST_APP_SRC (appsrc)); 63 return; 64 } 65 mem = gst_allocator_alloc (NULL, size, NULL); 66 if (! gst_memory_map (mem, &mi, GST_MAP_WRITE)) 67 { 68 gst_memory_unref (mem); 69 gst_app_src_end_of_stream (GST_APP_SRC (appsrc)); 70 return; 71 } 72 accumulated = 0; 73 data_len = 1; 74 while ( (accumulated < size) && 75 (data_len > 0) ) 76 { 77 data_len = io->ec->read (io->ec->cls, 78 (void **) &le_data, 79 size - accumulated); 80 if (data_len > 0) 81 { 82 memcpy (&mi.data[accumulated], 83 le_data, 84 data_len); 85 accumulated += data_len; 86 } 87 } 88 gst_memory_unmap (mem, &mi); 89 if (0 == accumulated) 90 { 91 gst_memory_unref (mem); 92 gst_app_src_end_of_stream (GST_APP_SRC (appsrc)); 93 return; 94 } 95 gst_memory_resize (mem, 0, accumulated); 96 buffer = gst_buffer_new (); 97 gst_buffer_append_memory (buffer, mem); 98 /* the offsets matter: they are what makes random access work */ 99 GST_BUFFER_OFFSET (buffer) = io->offset; 100 GST_BUFFER_OFFSET_END (buffer) = io->offset + accumulated; 101 io->offset += accumulated; 102 gst_app_src_push_buffer (GST_APP_SRC (appsrc), buffer); 103 } 104 105 106 /** 107 * Implementation of GstAppSrc's "seek-data" callback. 108 * 109 * @param appsrc the source that wants to seek 110 * @param position new desired absolute position in the file 111 * @param io our execution context 112 * @return TRUE if seeking succeeded 113 */ 114 static gboolean 115 seek_data (GstElement *appsrc, 116 guint64 position, 117 struct EXTRACTOR_GstIO *io) 118 { 119 int64_t ret; 120 121 (void) appsrc; 122 ret = io->ec->seek (io->ec->cls, 123 position, 124 SEEK_SET); 125 if (0 > ret) 126 return FALSE; 127 io->offset = (uint64_t) ret; 128 return io->offset == position; 129 } 130 131 132 /** 133 * Implementation of playbin's "source-setup" signal: configure the 134 * appsrc it created for our "appsrc://" URI. 135 * 136 * @param playbin the pipeline 137 * @param source the appsrc to configure 138 * @param io our execution context 139 */ 140 static void 141 source_setup (GstElement *playbin, 142 GstElement *source, 143 struct EXTRACTOR_GstIO *io) 144 { 145 (void) playbin; 146 if (0 != io->length) 147 g_object_set (source, 148 "size", (gint64) io->length, 149 "stream-type", GST_APP_STREAM_TYPE_RANDOM_ACCESS, 150 NULL); 151 else 152 g_object_set (source, 153 "size", (gint64) - 1, 154 "stream-type", GST_APP_STREAM_TYPE_STREAM, 155 NULL); 156 g_object_set (source, 157 "format", GST_FORMAT_BYTES, 158 NULL); 159 g_signal_connect (source, 160 "need-data", 161 G_CALLBACK (&feed_data), 162 io); 163 g_signal_connect (source, 164 "seek-data", 165 G_CALLBACK (&seek_data), 166 io); 167 } 168 169 170 GstElement * 171 EXTRACTOR_gst_playbin_ (struct EXTRACTOR_GstIO *io, 172 struct EXTRACTOR_ExtractContext *ec, 173 unsigned int flags, 174 GstElement *video_sink, 175 GstElement *audio_sink) 176 { 177 GstElement *pipeline; 178 uint64_t size; 179 180 if (! gst_is_initialized ()) 181 gst_init (NULL, NULL); 182 io->ec = ec; 183 io->offset = 0; 184 size = ec->get_size (ec->cls); 185 io->length = (UINT64_MAX == size) ? 0 : size; 186 pipeline = gst_element_factory_make ("playbin", NULL); 187 if ( (NULL == pipeline) || 188 (NULL == video_sink) || 189 (NULL == audio_sink) ) 190 { 191 /* gst-plugins-base is not installed, or not completely */ 192 if (NULL != video_sink) 193 gst_object_unref (video_sink); 194 if (NULL != audio_sink) 195 gst_object_unref (audio_sink); 196 if (NULL != pipeline) 197 gst_object_unref (pipeline); 198 return NULL; 199 } 200 g_object_set (pipeline, 201 "uri", "appsrc://", 202 "video-sink", video_sink, 203 "audio-sink", audio_sink, 204 "flags", flags, 205 NULL); 206 g_signal_connect (pipeline, 207 "source-setup", 208 G_CALLBACK (&source_setup), 209 io); 210 return pipeline; 211 } 212 213 214 int 215 EXTRACTOR_gst_wait_async_ (GstElement *pipeline, 216 GstClockTime timeout) 217 { 218 GstBus *bus; 219 GstMessage *msg; 220 int ret = 0; 221 222 if (0 == timeout) 223 return 0; 224 if (NULL == (bus = gst_element_get_bus (pipeline))) 225 return 0; 226 while (1) 227 { 228 msg = gst_bus_timed_pop_filtered (bus, 229 timeout, 230 GST_MESSAGE_ASYNC_DONE 231 | GST_MESSAGE_ERROR 232 | GST_MESSAGE_EOS); 233 if (NULL == msg) 234 break; /* timed out */ 235 if (GST_MESSAGE_ASYNC_DONE != GST_MESSAGE_TYPE (msg)) 236 { 237 /* an error, or the file ended before we ever saw a frame */ 238 gst_message_unref (msg); 239 break; 240 } 241 if (GST_OBJECT (pipeline) == GST_MESSAGE_SRC (msg)) 242 { 243 gst_message_unref (msg); 244 ret = 1; 245 break; 246 } 247 /* ASYNC_DONE from some element inside the pipeline; keep waiting */ 248 gst_message_unref (msg); 249 } 250 gst_object_unref (bus); 251 return ret; 252 } 253 254 255 gint64 256 EXTRACTOR_gst_sample_position_ (GstElement *pipeline, 257 unsigned int offset, 258 gint64 *duration) 259 { 260 gint64 target; 261 gint64 dur = 0; 262 263 target = ((gint64) offset) * GST_SECOND; 264 if (! gst_element_query_duration (pipeline, 265 GST_FORMAT_TIME, 266 &dur)) 267 dur = 0; 268 if (dur < 0) 269 dur = 0; 270 if ( (0 != dur) && 271 (target > dur / 3) ) 272 target = dur / 3; 273 *duration = dur; 274 return target; 275 } 276 277 278 /* end of mediagst.c */