framework2
1457 строк · 38.9 Кб
1#include "ofGstUtils.h"
2#ifndef TARGET_ANDROID
3#include <gst/app/gstappsink.h>
4#include <gst/video/video.h>
5
6#if GST_VERSION_MAJOR>0
7#include <gst/video/gstvideometa.h>
8#endif
9
10#ifdef OF_USE_GST_GL
11#define GST_USE_UNSTABLE_API
12#include <gst/gl/gl.h>
13#endif
14
15#include <glib-object.h>
16#include <glib.h>
17#include <algorithm>
18#include "ofVideoPlayer.h"
19
20#ifdef OF_USE_GST_GL
21#ifndef TARGET_OPENGLES
22#include <gst/gl/x11/gstgldisplay_x11.h>
23#endif
24#include <gst/gl/egl/gstgldisplay_egl.h>
25#endif
26#ifdef TARGET_WIN32
27#include <winbase.h> // to use SetEnvironmentVariableA
28#endif
29
30using std::shared_ptr;
31using std::string;
32
33ofGstUtils::ofGstMainLoopThread * ofGstUtils::mainLoop = nullptr;
34
35void ofGstUtils::startGstMainLoop(){
36static bool initialized = false;
37if(!initialized){
38mainLoop = new ofGstMainLoopThread;
39mainLoop->start();
40initialized=true;
41}
42}
43
44GMainLoop * ofGstUtils::getGstMainLoop(){
45return mainLoop->getMainLoop();
46}
47
48void ofGstUtils::quitGstMainLoop(){
49if(mainLoop){
50mainLoop->quit();
51delete mainLoop;
52mainLoop = nullptr;
53}
54}
55
56
57
58//-------------------------------------------------
59//----------------------------------------- gstUtils
60//-------------------------------------------------
61
62static bool plugin_registered = false;
63static bool gst_inited = false;
64
65
66// called when the appsink notifies us that there is a new buffer ready for
67// processing
68
69static GstFlowReturn on_new_buffer_from_source (GstAppSink * elt, void * data){
70#if GST_VERSION_MAJOR==0
71shared_ptr<GstBuffer> buffer(gst_app_sink_pull_buffer (GST_APP_SINK (elt)),&gst_buffer_unref);
72#else
73shared_ptr<GstSample> buffer(gst_app_sink_pull_sample (GST_APP_SINK (elt)),&gst_sample_unref);
74#endif
75return ((ofGstUtils*)data)->buffer_cb(buffer);
76}
77
78static GstFlowReturn on_new_preroll_from_source (GstAppSink * elt, void * data){
79#if GST_VERSION_MAJOR==0
80shared_ptr<GstBuffer> buffer(gst_app_sink_pull_preroll(GST_APP_SINK (elt)),&gst_buffer_unref);
81#else
82shared_ptr<GstSample> buffer(gst_app_sink_pull_preroll(GST_APP_SINK (elt)),&gst_sample_unref);
83#endif
84return ((ofGstUtils*)data)->preroll_cb(buffer);
85}
86
87static void on_eos_from_source (GstAppSink * elt, void * data){
88((ofGstUtils*)data)->eos_cb();
89}
90
91static gboolean appsink_plugin_init (GstPlugin * plugin)
92{
93gst_element_register (plugin, "appsink", GST_RANK_NONE, GST_TYPE_APP_SINK);
94
95return TRUE;
96}
97
98
99
100ofGstUtils::ofGstUtils() {
101bLoaded = false;
102speed = 1;
103bPaused = false;
104bIsMovieDone = false;
105bPlaying = false;
106loopMode = OF_LOOP_NONE;
107bFrameByFrame = false;
108
109gstPipeline = NULL;
110gstSink = NULL;
111
112durationNanos = 0;
113
114isAppSink = false;
115isStream = false;
116
117appsink = NULL;
118closing = false;
119
120busWatchID = 0;
121
122#if GLIB_MINOR_VERSION<32
123if(!g_thread_supported()){
124g_thread_init(NULL);
125}
126#endif
127
128if(!gst_inited){
129#ifdef TARGET_WIN32
130string gst_path;
131if (sizeof(int) == 32){
132auto env_path = g_getenv("GSTREAMER_1_0_ROOT_X86");
133if (env_path == NULL) {
134env_path = (ofGetTargetPlatform() == OF_TARGET_MINGW) ? g_getenv("GSTREAMER_1_0_ROOT_MINGW_X86") : g_getenv("GSTREAMER_1_0_ROOT_MSVC_X86");
135}
136gst_path = env_path;
137}
138else {
139auto env_path = g_getenv("GSTREAMER_1_0_ROOT_X86_64");
140if (env_path == NULL) {
141env_path = (ofGetTargetPlatform() == OF_TARGET_MINGW) ? g_getenv("GSTREAMER_1_0_ROOT_MINGW_X86_64") : g_getenv("GSTREAMER_1_0_ROOT_MSVC_X86_64");
142}
143gst_path = env_path;
144}
145//putenv(("GST_PLUGIN_PATH_1_0=" + ofFilePath::join(gst_path, "lib\\gstreamer-1.0") + ";.").c_str());
146// to make it compatible with gcc and C++11 standard
147SetEnvironmentVariableA("GST_PLUGIN_PATH_1_0", ofFilePath::join(gst_path, "lib\\gstreamer-1.0").c_str());
148#endif
149gst_init (NULL, NULL);
150gst_inited=true;
151ofLogVerbose("ofGstUtils") << "gstreamer inited";
152}
153if(!plugin_registered){
154gst_plugin_register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR,
155"appsink", (char*)"Element application sink",
156appsink_plugin_init, "0.1", "LGPL", "ofVideoPlayer", "openFrameworks",
157"http://openframeworks.cc/");
158plugin_registered=true;
159}
160
161}
162
163ofGstUtils::~ofGstUtils() {
164close();
165}
166
167#if GST_VERSION_MAJOR==0
168GstFlowReturn ofGstUtils::preroll_cb(shared_ptr<GstBuffer> buffer){
169#else
170GstFlowReturn ofGstUtils::preroll_cb(shared_ptr<GstSample> buffer){
171#endif
172bIsMovieDone = false;
173if(appsink) return appsink->on_preroll(buffer);
174else return GST_FLOW_OK;
175}
176
177
178#if GST_VERSION_MAJOR==0
179GstFlowReturn ofGstUtils::buffer_cb(shared_ptr<GstBuffer> buffer){
180#else
181GstFlowReturn ofGstUtils::buffer_cb(shared_ptr<GstSample> buffer){
182#endif
183bIsMovieDone = false;
184if(appsink) return appsink->on_buffer(buffer);
185else return GST_FLOW_OK;
186}
187
188void ofGstUtils::eos_cb(){
189bIsMovieDone = true;
190if(appsink && !isAppSink) appsink->on_eos();
191if(closing){
192std::unique_lock<std::mutex> lck(eosMutex);
193eosCondition.notify_all();
194}
195}
196
197bool ofGstUtils::setPipelineWithSink(string pipeline, string sinkname, bool isStream){
198ofGstUtils::startGstMainLoop();
199
200GError * error = NULL;
201gstPipeline = gst_parse_launch (pipeline.c_str(), &error);
202g_object_ref_sink(gstPipeline);
203ofLogNotice("ofGstUtils") << "setPipelineWithSink(): gstreamer pipeline: " << pipeline;
204
205if(error!=NULL){
206ofLogError("ofGstUtils") << "setPipelineWithSink(): couldn't create pipeline: " << error->message;
207return false;
208}
209
210if(sinkname!=""){
211gstSink = gst_bin_get_by_name(GST_BIN(gstPipeline),sinkname.c_str());
212
213if(!gstSink){
214ofLogError("ofGstUtils") << "setPipelineWithSink(): couldn't get sink from string pipeline";
215}
216}
217
218return setPipelineWithSink(gstPipeline,gstSink,isStream);
219}
220
221bool ofGstUtils::setPipelineWithSink(GstElement * pipeline, GstElement * sink, bool isStream_){
222ofGstUtils::startGstMainLoop();
223
224gstPipeline = pipeline;
225gstSink = sink;
226isStream = isStream_;
227
228if(gstSink){
229gst_base_sink_set_sync(GST_BASE_SINK(gstSink), true);
230}
231
232if(gstSink && string(gst_plugin_feature_get_name( GST_PLUGIN_FEATURE(gst_element_get_factory(gstSink))))=="appsink"){
233isAppSink = true;
234}else{
235isAppSink = false;
236}
237
238return true;
239}
240
241void ofGstUtils::setFrameByFrame(bool _bFrameByFrame){
242bFrameByFrame = _bFrameByFrame;
243if(gstSink){
244g_object_set (G_OBJECT (gstSink), "sync", !bFrameByFrame, (void*)NULL);
245}
246}
247
248bool ofGstUtils::isFrameByFrame() const{
249return bFrameByFrame;
250}
251
252bool ofGstUtils::startPipeline(){
253
254bPaused = true;
255speed = 1.0f;
256
257
258GstBus * bus = gst_pipeline_get_bus (GST_PIPELINE(gstPipeline));
259
260if(bus){
261busWatchID = gst_bus_add_watch (bus, (GstBusFunc) busFunction, this);
262}
263
264gst_object_unref(bus);
265
266// pause the pipeline
267//GstState targetState;
268GstState state;
269auto ret = gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_PAUSED);
270switch (ret) {
271case GST_STATE_CHANGE_FAILURE:
272ofLogError("ofGstUtils") << "startPipeline(): unable to pause pipeline";
273return false;
274break;
275case GST_STATE_CHANGE_NO_PREROLL:
276ofLogVerbose() << "Pipeline is live and does not need PREROLL waiting PLAY";
277gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_PLAYING);
278break;
279case GST_STATE_CHANGE_ASYNC:
280ofLogVerbose() << "Pipeline is PREROLLING";
281//targetState = GST_STATE_PAUSED;
282if(!isStream && gst_element_get_state(gstPipeline,&state,NULL,5*GST_SECOND)!=GST_STATE_CHANGE_SUCCESS){
283ofLogError("ofGstUtils") << "startPipeline(): unable to pause pipeline after 5s";
284return false;
285}else{
286ofLogVerbose() << "Pipeline is PREROLLED";
287}
288break;
289case GST_STATE_CHANGE_SUCCESS:
290ofLogVerbose() << "Pipeline is PREROLLED";
291break;
292}
293
294if(isAppSink){
295ofLogVerbose("ofGstUtils") << "startPipeline(): attaching callbacks";
296
297bool bSignals = false;
298
299#if GST_VERSION_MAJOR==0
300
301GstAppSinkCallbacks gstCallbacks;
302gstCallbacks.eos = &on_eos_from_source;
303gstCallbacks.new_preroll = &on_new_preroll_from_source;
304gstCallbacks.new_buffer = &on_new_buffer_from_source;
305gst_app_sink_set_callbacks(GST_APP_SINK(gstSink), &gstCallbacks, this, NULL);
306
307#elseif GST_VERSION_MINOR <= 18
308
309GstAppSinkCallbacks gstCallbacks;
310gstCallbacks.eos = &on_eos_from_source;
311gstCallbacks.new_preroll = &on_new_preroll_from_source;
312gstCallbacks.new_sample = &on_new_buffer_from_source;
313gst_app_sink_set_callbacks(GST_APP_SINK(gstSink), &gstCallbacks, this, NULL);
314
315#else
316
317//GStreamer 1.20 onwards crashes with the callback approach above.
318//Using signals makes it work! This might be a GStreamer bug - but we'll do this for now
319g_signal_connect(GST_APP_SINK(gstSink), "eos", G_CALLBACK(on_eos_from_source), this);
320g_signal_connect(GST_APP_SINK(gstSink), "new-sample", G_CALLBACK(on_new_buffer_from_source), this);
321g_signal_connect(GST_APP_SINK(gstSink), "new-preroll", G_CALLBACK(on_new_preroll_from_source), this);
322
323bSignals = true;
324#endif
325
326// set the appsink to not emit signals, we are using callbacks instead
327// and frameByFrame to get buffers by polling instead of callback
328g_object_set (G_OBJECT (gstSink), "emit-signals", bSignals, "sync", !bFrameByFrame, (void*)NULL);
329
330gst_app_sink_set_max_buffers(GST_APP_SINK(gstSink),3);
331gst_app_sink_set_drop (GST_APP_SINK(gstSink),true);
332}
333
334// wait for paused state to query the duration
335if(!isStream){
336bPlaying = true;
337bLoaded = true;
338}
339
340return true;
341}
342
343void ofGstUtils::play(){
344setPaused(false);
345
346//this is if we set the speed first but it only can be set when we are playing.
347if(!isStream) setSpeed(speed);
348}
349
350void ofGstUtils::setPaused(bool _bPause){
351bPaused = _bPause;
352//timeLastIdle = ofGetElapsedTimeMillis();
353if(bLoaded){
354if(bPlaying){
355if(bPaused){
356gst_element_set_state (gstPipeline, GST_STATE_PAUSED);
357}else{
358gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
359}
360}else{
361GstState state = GST_STATE_PAUSED;
362gst_element_set_state (gstPipeline, state);
363gst_element_get_state(gstPipeline,&state,NULL,2*GST_SECOND);
364if(!bPaused){
365gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
366}
367}
368bPlaying = !bPaused;
369}
370}
371
372void ofGstUtils::stop(){
373if(!bLoaded) return;
374GstState state;
375
376if(!bPaused){
377state = GST_STATE_PAUSED;
378gst_element_set_state (gstPipeline, state);
379gst_element_get_state(gstPipeline,&state,NULL,2*GST_SECOND);
380}
381
382state = GST_STATE_READY;
383gst_element_set_state (gstPipeline, state);
384gst_element_get_state(gstPipeline,&state,NULL,2*GST_SECOND);
385bPlaying = false;
386bPaused = false;
387}
388
389float ofGstUtils::getPosition() const{
390if(gstPipeline){
391gint64 pos=0;
392#if GST_VERSION_MAJOR==0
393GstFormat format=GST_FORMAT_TIME;
394if(!gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos)){
395ofLogVerbose("ofGstUtils") << "getPosition(): couldn't query position";
396return -1;
397}
398#else
399if(!gst_element_query_position(GST_ELEMENT(gstPipeline),GST_FORMAT_TIME,&pos)){
400ofLogVerbose("ofGstUtils") << "getPosition(): couldn't query position";
401return -1;
402}
403#endif
404return (float)pos/(float)durationNanos;
405}else{
406return -1;
407}
408}
409
410int64_t ofGstUtils::getPositionNanos() const{
411if(gstPipeline){
412gint64 pos=0;
413#if GST_VERSION_MAJOR==0
414GstFormat format=GST_FORMAT_TIME;
415if(!gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos)){
416ofLogVerbose("ofGstUtils") << "getPosition(): couldn't query position";
417return -1;
418}
419#else
420if(!gst_element_query_position(GST_ELEMENT(gstPipeline),GST_FORMAT_TIME,&pos)){
421ofLogVerbose("ofGstUtils") << "getPosition(): couldn't query position";
422return -1;
423}
424#endif
425return pos;
426}else{
427return -1;
428}
429}
430
431
432float ofGstUtils::getSpeed() const{
433return speed;
434}
435
436float ofGstUtils::getDuration() const{
437return (float)getDurationNanos()/(float)GST_SECOND;
438}
439
440int64_t ofGstUtils::getDurationNanos() const{
441GstFormat format = GST_FORMAT_TIME;
442
443#if GST_VERSION_MAJOR==0
444if(!gst_element_query_duration(getPipeline(),&format,&durationNanos))
445ofLogWarning("ofGstUtils") << "getDurationNanos(): couldn't query time duration";
446#else
447if(!gst_element_query_duration(getPipeline(),format,&durationNanos))
448ofLogWarning("ofGstUtils") << "getDurationNanos(): couldn't query time duration";
449#endif
450return durationNanos;
451
452}
453
454bool ofGstUtils::getIsMovieDone() const{
455if(isAppSink){
456return gst_app_sink_is_eos(GST_APP_SINK(gstSink));
457}else{
458return bIsMovieDone;
459}
460}
461
462void ofGstUtils::setPosition(float pct){
463//pct = CLAMP(pct, 0,1);// check between 0 and 1;
464GstFormat format = GST_FORMAT_TIME;
465GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH);
466if(speed > 1 || speed < -1){
467flags = (GstSeekFlags)(flags | GST_SEEK_FLAG_SKIP);
468}
469gint64 pos = (guint64)((double)pct*(double)durationNanos);
470
471/*if(bPaused){
472seek_lock();
473gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
474posChangingPaused=true;
475seek_unlock();
476}*/
477if(speed>0){
478if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format,
479flags,
480GST_SEEK_TYPE_SET,
481pos,
482GST_SEEK_TYPE_SET,
483-1)) {
484ofLogWarning("ofGstUtils") << "setPosition(): unable to seek";
485}
486}else{
487if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format,
488flags,
489GST_SEEK_TYPE_SET,
4900,
491GST_SEEK_TYPE_SET,
492pos)) {
493ofLogWarning("ofGstUtils") << "setPosition(): unable to seek";
494}
495}
496}
497
498void ofGstUtils::setVolume(float volume){
499gdouble gvolume = volume;
500g_object_set(G_OBJECT(gstPipeline), "volume", gvolume, (void*)NULL);
501}
502
503void ofGstUtils::setLoopState(ofLoopType state){
504loopMode = state;
505}
506
507void ofGstUtils::setSpeed(float _speed){
508if(_speed == speed) return;
509
510GstFormat format = GST_FORMAT_TIME;
511GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH);
512
513bool needPos = true;
514#if GST_VERSION_MAJOR >= 1 && GST_VERSION_MINOR >= 18
515//if requested speed is the same direction as we are already going we can do a fast rate change
516if( speed * _speed > 0 ){
517flags = GST_SEEK_FLAG_INSTANT_RATE_CHANGE;
518needPos = false;
519}
520#endif
521
522if(_speed > 1 || _speed < -1){
523flags = (GstSeekFlags)(flags | GST_SEEK_FLAG_SKIP);
524}
525
526if(_speed==0){
527gst_element_set_state (gstPipeline, GST_STATE_PAUSED);
528return;
529}
530
531
532gint64 pos = 0;
533GstSeekType seekType = GST_SEEK_TYPE_NONE;
534if( needPos ){
535seekType = GST_SEEK_TYPE_SET;
536
537pos = getPositionNanos();
538if( pos == -1 ){
539ofLogError("ofGstUtils") << "setSpeed(): couldn't query position";
540return;
541}
542
543}
544
545speed = _speed;
546
547if(!bPaused)
548gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
549
550if(speed>0){
551if(!gst_element_seek(GST_ELEMENT(gstSink), speed, format,
552flags,
553seekType,
554pos,
555seekType,
556-1)) {
557ofLogWarning("ofGstUtils") << "setSpeed(): unable to change speed";
558}
559}else{
560if(!gst_element_seek(GST_ELEMENT(gstSink), speed, format,
561flags,
562seekType,
5630,
564seekType,
565pos)) {
566ofLogWarning("ofGstUtils") << "setSpeed(): unable to change speed";
567}
568}
569
570ofLogVerbose("ofGstUtils") << "setSpeed(): speed changed to " << speed;
571
572}
573
574void ofGstUtils::close(){
575if(bPlaying){
576if(!bIsMovieDone && !bPaused && !isStream){
577std::unique_lock<std::mutex> lck(eosMutex);
578closing = true;
579gst_element_send_event(gstPipeline,gst_event_new_eos());
580if(eosCondition.wait_for(lck,std::chrono::milliseconds(5000))==std::cv_status::timeout){
581ofLogWarning("ofGstUtils") << "didn't received EOS in 5s, closing pipeline anyway";
582}
583closing = false;
584}
585}
586stop();
587
588if(bLoaded){
589gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_NULL);
590gst_element_get_state(gstPipeline,NULL,NULL,2*GST_SECOND);
591
592if(busWatchID!=0) g_source_remove(busWatchID);
593
594gst_object_unref(gstPipeline);
595gstPipeline = NULL;
596gstSink = NULL;
597}
598
599bLoaded = false;
600}
601
602/*static string getName(GstState state){
603switch(state){
604case GST_STATE_VOID_PENDING:
605return "void pending";
606case GST_STATE_NULL:
607return "null";
608case GST_STATE_READY:
609return "ready";
610case GST_STATE_PAUSED:
611return "paused";
612case GST_STATE_PLAYING:
613return "playing";
614default:
615return "";
616}
617}*/
618
619bool ofGstUtils::busFunction(GstBus * bus, GstMessage * message, ofGstUtils * gstUtils){
620return gstUtils->gstHandleMessage(bus,message);
621}
622
623bool ofGstUtils::gstHandleMessage(GstBus * bus, GstMessage * msg){
624if(appsink && appsink->on_message(msg)) return true;
625
626/*ofLogVerbose("ofGstUtils") << "gstHandleMessage(): got " << GST_MESSAGE_TYPE_NAME(msg)
627<< " message from " << GST_MESSAGE_SRC_NAME(msg);*/
628
629switch (GST_MESSAGE_TYPE (msg)) {
630
631case GST_MESSAGE_BUFFERING:
632gint pctBuffered;
633gst_message_parse_buffering(msg,&pctBuffered);
634ofLogVerbose("ofGstUtils") << "gstHandleMessage(): buffering " << pctBuffered;
635if(pctBuffered<100){
636gst_element_set_state (gstPipeline, GST_STATE_PAUSED);
637}else if(!bPaused){
638gst_element_set_state (gstPipeline, GST_STATE_PLAYING);
639}
640break;
641
642#if GST_VERSION_MAJOR==0
643case GST_MESSAGE_DURATION:{
644GstFormat format=GST_FORMAT_TIME;
645gst_element_query_duration(gstPipeline,&format,&durationNanos);
646}break;
647#else
648case GST_MESSAGE_DURATION_CHANGED:
649gst_element_query_duration(gstPipeline,GST_FORMAT_TIME,&durationNanos);
650break;
651
652#endif
653
654case GST_MESSAGE_STATE_CHANGED:{
655GstState oldstate, newstate, pendstate;
656gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate);
657if(isStream && newstate==GST_STATE_PAUSED && !bPlaying ){
658bLoaded = true;
659bPlaying = true;
660if(!bPaused){
661//ofLogVerbose("ofGstUtils") << "gstHandleMessage(): setting stream pipeline to play";
662play();
663}
664}
665
666/*ofLogVerbose("ofGstUtils") << "gstHandleMessage(): " << GST_MESSAGE_SRC_NAME(msg) << " state changed from "
667<< getName(oldstate) << " to " << getName(newstate) << " (" + getName(pendstate) << ")";*/
668}break;
669
670case GST_MESSAGE_ASYNC_DONE:
671ofLogVerbose("ofGstUtils") << "gstHandleMessage(): async done";
672break;
673
674case GST_MESSAGE_ERROR: {
675GError *err;
676gchar *debug;
677gst_message_parse_error(msg, &err, &debug);
678gchar * name = gst_element_get_name(GST_MESSAGE_SRC (msg));
679
680ofLogError("ofGstUtils") << "gstHandleMessage(): embedded video playback halted for plugin, module "
681<< name << " reported: " << err->message;
682
683g_free(name);
684g_error_free(err);
685g_free(debug);
686
687gst_element_set_state(GST_ELEMENT(gstPipeline), GST_STATE_NULL);
688
689}break;
690
691case GST_MESSAGE_EOS:{
692ofLogVerbose("ofGstUtils") << "gstHandleMessage(): end of the stream";
693bool isClosing = closing;
694eos_cb();
695
696if(isClosing){
697busWatchID = 0;
698return false;
699}
700
701switch(loopMode){
702
703case OF_LOOP_NORMAL:{
704GstFormat format = GST_FORMAT_TIME;
705GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT);
706if(speed>0){
707if(!gst_element_seek(GST_ELEMENT(gstPipeline),
708speed,
709format,
710flags,
711GST_SEEK_TYPE_SET,
7120,
713GST_SEEK_TYPE_SET,
714-1)) {
715ofLogWarning("ofGstUtils") << "gstHandleMessage(): unable to seek";
716}
717}else if(speed<0){
718if(!gst_element_seek(GST_ELEMENT(gstPipeline),speed, format,
719flags,
720GST_SEEK_TYPE_SET,
7210,
722GST_SEEK_TYPE_SET,
723durationNanos-1000000)) {
724ofLogWarning("ofGstUtils") << "gstHandleMessage(): unable to seek";
725}
726}
727}break;
728
729case OF_LOOP_PALINDROME:{
730GstFormat format = GST_FORMAT_TIME;
731GstSeekFlags flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH |GST_SEEK_FLAG_KEY_UNIT);
732gint64 pos;
733#if GST_VERSION_MAJOR==0
734gst_element_query_position(GST_ELEMENT(gstPipeline),&format,&pos);
735#else
736gst_element_query_position(GST_ELEMENT(gstPipeline),format,&pos);
737#endif
738float loopSpeed;
739if(pos>0)
740loopSpeed=-speed;
741else
742loopSpeed=speed;
743if(!gst_element_seek(GST_ELEMENT(gstPipeline),
744loopSpeed,
745GST_FORMAT_UNDEFINED,
746flags,
747GST_SEEK_TYPE_NONE,
7480,
749GST_SEEK_TYPE_NONE,
7500)) {
751ofLogWarning("ofGstUtils") << "gstHandleMessage(): unable to seek";
752}
753}break;
754
755default:
756break;
757}
758
759}break;
760case GST_MESSAGE_LATENCY:
761gst_bin_recalculate_latency (GST_BIN (getPipeline()));
762break;
763case GST_MESSAGE_REQUEST_STATE: {
764GstState state;
765gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (msg));
766
767gst_message_parse_request_state (msg, &state);
768gst_element_set_state (getPipeline(), state);
769
770g_free (name);
771break;
772}
773
774#if GST_VERSION_MAJOR==1 && GST_VERSION_MINOR>=2
775case GST_MESSAGE_HAVE_CONTEXT:{
776GstContext *context;
777const gchar *context_type;
778gchar *context_str;
779
780gst_message_parse_have_context (msg, &context);
781
782context_type = gst_context_get_context_type (context);
783context_str = gst_structure_to_string (gst_context_get_structure (context));
784ofLogNotice("ofGstUtils","Got context from element '%s': %s=%s\n",
785GST_ELEMENT_NAME (GST_MESSAGE_SRC (msg)), context_type,
786context_str);
787g_free (context_str);
788gst_context_unref (context);
789break;
790}
791#endif
792default:
793ofLogVerbose("ofGstUtils") << "gstHandleMessage(): unhandled message from " << GST_MESSAGE_SRC_NAME(msg);
794break;
795}
796
797return true;
798}
799
800GstElement * ofGstUtils::getPipeline() const{
801return gstPipeline;
802}
803
804GstElement * ofGstUtils::getSink() const{
805return gstSink;
806}
807
808GstElement * ofGstUtils::getGstElementByName(const string & name) const{
809return gst_bin_get_by_name(GST_BIN(gstPipeline),name.c_str());
810}
811
812void ofGstUtils::setSinkListener(ofGstAppSink * appsink_){
813appsink = appsink_;
814}
815
816uint64_t ofGstUtils::getMinLatencyNanos() const{
817GstClockTime minlat=0, maxlat=0;
818GstQuery * q = gst_query_new_latency();
819if (gst_element_query (gstPipeline, q)) {
820gboolean live;
821gst_query_parse_latency (q, &live, &minlat, &maxlat);
822}
823gst_query_unref (q);
824return minlat;
825}
826
827uint64_t ofGstUtils::getMaxLatencyNanos() const{
828GstClockTime minlat=0, maxlat=0;
829GstQuery * q = gst_query_new_latency();
830if (gst_element_query (gstPipeline, q)) {
831gboolean live;
832gst_query_parse_latency (q, &live, &minlat, &maxlat);
833}
834gst_query_unref (q);
835return maxlat;
836}
837
838
839
840//-------------------------------------------------
841//----------------------------------------- videoUtils
842//-------------------------------------------------
843
844
845
846ofGstVideoUtils::ofGstVideoUtils(){
847bIsFrameNew = false;
848bHavePixelsChanged = false;
849bBackPixelsChanged = false;
850#if GST_VERSION_MAJOR==1
851GstMapInfo initMapinfo = {0,};
852mapinfo = initMapinfo;
853#endif
854internalPixelFormat = OF_PIXELS_RGB;
855#ifdef OF_USE_GST_GL
856glDisplay = NULL;
857glContext = NULL;
858#endif
859copyPixels = false;
860}
861
862ofGstVideoUtils::~ofGstVideoUtils(){
863close();
864}
865
866void ofGstVideoUtils::close(){
867ofGstUtils::close();
868std::unique_lock<std::mutex> lock(mutex);
869pixels.clear();
870backPixels.clear();
871eventPixels.clear();
872bIsFrameNew = false;
873bHavePixelsChanged = false;
874bBackPixelsChanged = false;
875frontBuffer.reset();
876backBuffer.reset();
877
878#if GST_VERSION_MAJOR==1
879while(!bufferQueue.empty()) bufferQueue.pop();
880#endif
881}
882
883bool ofGstVideoUtils::isInitialized() const{
884return isLoaded();
885}
886
887bool ofGstVideoUtils::isFrameNew() const{
888return bIsFrameNew;
889}
890
891ofPixels& ofGstVideoUtils::getPixels(){
892return pixels;
893}
894
895const ofPixels & ofGstVideoUtils::getPixels() const{
896return pixels;
897}
898
899ofTexture * ofGstVideoUtils::getTexture(){
900#ifdef OF_USE_GST_GL
901if(frontTexture.isAllocated()){
902return &frontTexture;
903}else{
904return NULL;
905}
906#else
907return NULL;
908#endif
909}
910
911void ofGstVideoUtils::update(){
912if (isLoaded()){
913if(!isFrameByFrame()){
914std::unique_lock<std::mutex> lock(mutex);
915bHavePixelsChanged = bBackPixelsChanged;
916if (bHavePixelsChanged){
917bBackPixelsChanged=false;
918std::swap(pixels,backPixels);
919#ifdef OF_USE_GST_GL
920if(backTexture.isAllocated()){
921frontTexture.getTextureData() = backTexture.getTextureData();
922frontTexture.setTextureMinMagFilter(GL_LINEAR,GL_LINEAR);
923frontTexture.setTextureWrap(GL_CLAMP_TO_EDGE,GL_CLAMP_TO_EDGE);
924}
925#endif
926if(!copyPixels){
927frontBuffer = backBuffer;
928}
929}
930}else{
931#if GST_VERSION_MAJOR==0
932ofLogError() << "frame by frame doesn't work any more in 0.10";
933#else
934GstBuffer * buffer;
935GstSample * sample;
936
937//get the buffer from appsink
938if(isPaused()){
939sample = gst_app_sink_pull_preroll (GST_APP_SINK (getSink()));
940}else{
941sample = gst_app_sink_pull_sample (GST_APP_SINK (getSink()));
942}
943buffer = gst_sample_get_buffer(sample);
944
945if(buffer){
946if(pixels.isAllocated()){
947gst_buffer_map (buffer, &mapinfo, GST_MAP_READ);
948//TODO: stride = mapinfo.size / height;
949pixels.setFromExternalPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getNumChannels());
950backBuffer = shared_ptr<GstSample>(sample,gst_sample_unref);
951bHavePixelsChanged=true;
952gst_buffer_unmap(buffer,&mapinfo);
953}
954}
955#endif
956}
957}else{
958ofLogWarning("ofGstVideoUtils") << "update(): ofGstVideoUtils not loaded";
959}
960bIsFrameNew = bHavePixelsChanged;
961bHavePixelsChanged = false;
962}
963
964float ofGstVideoUtils::getHeight() const{
965return pixels.getHeight();
966}
967
968float ofGstVideoUtils::getWidth() const{
969return pixels.getWidth();
970}
971
972
973#if GST_VERSION_MAJOR>0
974string ofGstVideoUtils::getGstFormatName(ofPixelFormat format){
975switch(format){
976case OF_PIXELS_GRAY:
977return "GRAY8";
978case OF_PIXELS_RGBA:
979return "RGBA";
980case OF_PIXELS_BGRA:
981return "BGRA";
982case OF_PIXELS_RGB565:
983return "RGB16";
984case OF_PIXELS_NV12:
985return "NV12";
986case OF_PIXELS_NV21:
987return "NV21";
988case OF_PIXELS_YV12:
989return "YV12";
990case OF_PIXELS_I420:
991return "I420";
992case OF_PIXELS_YUY2:
993return "YUY2";
994case OF_PIXELS_RGB:
995return "RGB";
996case OF_PIXELS_BGR:
997return "BGR";
998default:
999return "UNKNOWN";
1000}
1001}
1002
1003GstVideoFormat ofGstVideoUtils::getGstFormat(ofPixelFormat format){
1004switch(format){
1005case OF_PIXELS_GRAY:
1006return GST_VIDEO_FORMAT_GRAY8;
1007
1008case OF_PIXELS_RGB:
1009return GST_VIDEO_FORMAT_RGB;
1010
1011case OF_PIXELS_BGR:
1012return GST_VIDEO_FORMAT_BGR;
1013
1014case OF_PIXELS_RGBA:
1015return GST_VIDEO_FORMAT_RGBA;
1016
1017case OF_PIXELS_BGRA:
1018return GST_VIDEO_FORMAT_BGRA;
1019
1020case OF_PIXELS_RGB565:
1021return GST_VIDEO_FORMAT_RGB16;
1022
1023case OF_PIXELS_NV12:
1024return GST_VIDEO_FORMAT_NV12;
1025
1026case OF_PIXELS_NV21:
1027return GST_VIDEO_FORMAT_NV21;
1028
1029case OF_PIXELS_YV12:
1030return GST_VIDEO_FORMAT_YV12;
1031
1032case OF_PIXELS_I420:
1033return GST_VIDEO_FORMAT_I420;
1034
1035case OF_PIXELS_YUY2:
1036return GST_VIDEO_FORMAT_YUY2;
1037
1038case OF_PIXELS_UNKNOWN:
1039case OF_PIXELS_GRAY_ALPHA:
1040case OF_PIXELS_Y:
1041case OF_PIXELS_U:
1042case OF_PIXELS_V:
1043case OF_PIXELS_UV:
1044case OF_PIXELS_VU:
1045default:
1046return GST_VIDEO_FORMAT_UNKNOWN;
1047}
1048}
1049
1050ofPixelFormat ofGstVideoUtils::getOFFormat(GstVideoFormat format){
1051switch(format){
1052case GST_VIDEO_FORMAT_GRAY8:
1053return OF_PIXELS_GRAY;
1054
1055case GST_VIDEO_FORMAT_RGB:
1056return OF_PIXELS_RGB;
1057
1058case GST_VIDEO_FORMAT_BGR:
1059return OF_PIXELS_BGR;
1060
1061case GST_VIDEO_FORMAT_RGBA:
1062return OF_PIXELS_RGBA;
1063
1064case GST_VIDEO_FORMAT_BGRA:
1065return OF_PIXELS_BGRA;
1066
1067case GST_VIDEO_FORMAT_RGB16:
1068return OF_PIXELS_RGB565;
1069
1070case GST_VIDEO_FORMAT_NV12:
1071return OF_PIXELS_NV12;
1072
1073case GST_VIDEO_FORMAT_NV21:
1074return OF_PIXELS_NV21;
1075
1076case GST_VIDEO_FORMAT_YV12:
1077return OF_PIXELS_YV12;
1078
1079case GST_VIDEO_FORMAT_I420:
1080return OF_PIXELS_I420;
1081
1082case GST_VIDEO_FORMAT_YUY2:
1083return OF_PIXELS_YUY2;
1084
1085default:
1086ofLogError() << "non supported format " << format;
1087return OF_PIXELS_UNKNOWN;
1088}
1089}
1090#endif
1091
1092/*
1093gboolean ofGstVideoUtils::sync_bus_call (GstBus * bus, GstMessage * msg, gpointer data)
1094{
1095switch (GST_MESSAGE_TYPE (msg)) {
1096case GST_MESSAGE_NEED_CONTEXT:
1097{
1098ofGstVideoPlayer * player = (ofGstVideoPlayer*)data;
1099const gchar *context_type;
1100
1101gst_message_parse_context_type (msg, &context_type);
1102ofLogNotice("ofGstVideoPlayer","got need context %s\n", context_type);
1103
1104if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
1105GstContext *display_context =
1106gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
1107gst_context_set_gl_display (display_context, player->glDisplay);
1108//GstStructure *s = gst_context_writable_structure (display_context);
1109//gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, player->glContext, NULL);
1110gst_element_set_context (GST_ELEMENT (msg->src), display_context);
1111return TRUE;
1112} else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
1113GstContext *app_context = gst_context_new ("gst.gl.app_context", TRUE);
1114GstStructure *s = gst_context_writable_structure (app_context);
1115gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, player->glContext, NULL);
1116gst_element_set_context (GST_ELEMENT (msg->src), app_context);
1117return TRUE;
1118}
1119break;
1120}
1121default:
1122break;
1123}
1124return FALSE;
1125}*/
1126
1127void ofGstVideoUtils::setCopyPixels(bool copy){
1128copyPixels = copy;
1129}
1130
1131bool ofGstVideoUtils::setPipeline(string pipeline, ofPixelFormat pixelFormat, bool isStream, int w, int h){
1132internalPixelFormat = pixelFormat;
1133#ifndef OF_USE_GST_GL
1134string caps;
1135#if GST_VERSION_MAJOR==0
1136switch(pixelFormat){
1137case OF_PIXELS_MONO:
1138caps="video/x-raw-gray, depth=8, bpp=8";
1139break;
1140case OF_PIXELS_RGBA:
1141caps="video/x-raw-rgb, depth=24, bpp=32, endianness=4321, red_mask=0xff0000, green_mask=0x00ff00, blue_mask=0x0000ff, alpha_mask=0x000000ff";
1142break;
1143case OF_PIXELS_BGRA:
1144caps="video/x-raw-rgb, depth=24, bpp=32, endianness=4321, red_mask=0x0000ff, green_mask=0x00ff00, blue_mask=0xff0000, alpha_mask=0x000000ff";
1145break;
1146case OF_PIXELS_RGB:
1147default:
1148caps="video/x-raw-rgb, depth=24, bpp=24, endianness=4321, red_mask=0xff0000, green_mask=0x00ff00, blue_mask=0x0000ff, alpha_mask=0x000000ff";
1149break;
1150}
1151#else
1152if(pixelFormat!=OF_PIXELS_NATIVE){
1153caps="video/x-raw, format="+getGstFormatName(pixelFormat);
1154}else{
1155caps = "video/x-raw,format={RGBA,BGRA,RGB,BGR,RGB16,GRAY8,YV12,I420,NV12,NV21,YUY2}";
1156}
1157#endif
1158
1159if(w!=-1 && h!=-1){
1160caps+=", width=" + ofToString(w) + ", height=" + ofToString(h);
1161}
1162
1163string pipeline_string =
1164pipeline + " ! appsink name=ofappsink enable-last-sample=0 caps=\"" + caps + "\"";
1165
1166if((w==-1 || h==-1) || pixelFormat==OF_PIXELS_NATIVE || allocate(w,h,pixelFormat)){
1167return setPipelineWithSink(pipeline_string,"ofappsink",isStream);
1168}else{
1169return false;
1170}
1171#else
1172string pipeline_string =
1173pipeline + " ! glcolorscale name=gl_filter ! appsink name=ofappsink enable-last-sample=0 caps=\"video/x-raw,format=RGBA\"";
1174
1175bool ret;
1176if((w==-1 || h==-1) || pixelFormat==OF_PIXELS_NATIVE || allocate(w,h,pixelFormat)){
1177ret = setPipelineWithSink(pipeline_string,"ofappsink",isStream);
1178}else{
1179ret = false;
1180}
1181
1182auto glfilter = gst_bin_get_by_name(GST_BIN(getPipeline()),"gl_filter");
1183
1184#if defined(TARGET_LINUX) && !defined(TARGET_OPENGLES)
1185glXMakeCurrent (ofGetX11Display(), None, 0);
1186glDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display(ofGetX11Display());
1187glContext = gst_gl_context_new_wrapped (glDisplay, (guintptr) ofGetGLXContext(),
1188GST_GL_PLATFORM_GLX, GST_GL_API_OPENGL);
1189
1190g_object_set (G_OBJECT (glfilter), "other-context", glContext, NULL);
1191// FIXME: this seems to be the way to add the context in 1.4.5
1192//
1193// GstBus * bus = gst_pipeline_get_bus (GST_PIPELINE(gstPipeline));
1194// gst_bus_enable_sync_message_emission (bus);
1195// g_signal_connect (bus, "sync-message", G_CALLBACK (sync_bus_call), this);
1196// gst_object_unref(bus);
1197
1198glXMakeCurrent (ofGetX11Display(), ofGetX11Window(), ofGetGLXContext());
1199#elif defined(TARGET_OPENGLES)
1200cout << "current display " << ofGetEGLDisplay() << endl;
1201eglMakeCurrent (eglGetDisplay(EGL_DEFAULT_DISPLAY), 0,0, 0);
1202glDisplay = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display(eglGetDisplay(EGL_DEFAULT_DISPLAY));
1203glContext = gst_gl_context_new_wrapped (glDisplay, (guintptr) ofGetEGLContext(),
1204GST_GL_PLATFORM_EGL, GST_GL_API_GLES2);
1205
1206g_object_set (G_OBJECT (glfilter), "other-context", glContext, NULL);
1207// FIXME: this seems to be the way to add the context in 1.4.5
1208//
1209// GstBus * bus = gst_pipeline_get_bus (GST_PIPELINE(gstPipeline));
1210// gst_bus_enable_sync_message_emission (bus);
1211// g_signal_connect (bus, "sync-message", G_CALLBACK (sync_bus_call), this);
1212// gst_object_unref(bus);
1213
1214eglMakeCurrent (ofGetEGLDisplay(), ofGetEGLSurface(), ofGetEGLSurface(), ofGetEGLContext());
1215
1216#endif
1217
1218return ret;
1219#endif
1220}
1221
1222bool ofGstVideoUtils::setPixelFormat(ofPixelFormat pixelFormat){
1223internalPixelFormat = pixelFormat;
1224return true;
1225}
1226
1227ofPixelFormat ofGstVideoUtils::getPixelFormat() const{
1228return internalPixelFormat;
1229}
1230
1231bool ofGstVideoUtils::allocate(int w, int h, ofPixelFormat pixelFormat){
1232std::unique_lock<std::mutex> lock(mutex);
1233#if GST_VERSION_MAJOR>0
1234if(pixelFormat!=internalPixelFormat){
1235ofLogNotice("ofGstVideoUtils") << "allocating with " << w << "x" << h << " " << getGstFormatName(pixelFormat);
1236}
1237#endif
1238pixels.allocate(w,h,pixelFormat);
1239backPixels.allocate(w,h,pixelFormat);
1240pixels.set(0);
1241backPixels.set(0);
1242
1243bHavePixelsChanged = false;
1244bBackPixelsChanged = true;
1245
1246internalPixelFormat = pixelFormat;
1247return pixels.isAllocated();
1248}
1249
1250void ofGstVideoUtils::reallocateOnNextFrame(){
1251std::unique_lock<std::mutex> lock(mutex);
1252pixels.clear();
1253backPixels.clear();
1254bIsFrameNew = false;
1255bHavePixelsChanged = false;
1256bBackPixelsChanged = false;
1257frontBuffer.reset();
1258backBuffer.reset();
1259#if GST_VERSION_MAJOR==1
1260while(!bufferQueue.empty()) bufferQueue.pop();
1261#endif
1262}
1263
1264#if GST_VERSION_MAJOR==0
1265GstFlowReturn ofGstVideoUtils::process_buffer(shared_ptr<GstBuffer> _buffer){
1266guint size = GST_BUFFER_SIZE (_buffer.get());
1267int stride = 0;
1268if(pixels.isAllocated() && pixels.getTotalBytes()!=(int)size){
1269stride = gst_video_format_get_row_stride( GST_VIDEO_FORMAT_RGB,0, pixels.getWidth());
1270if(stride == (pixels.getWidth() * pixels.getHeight() * pixels.getBytesPerPixel())) {
1271ofLogError("ofGstVideoUtils") << "buffer_cb(): error on new buffer, buffer size: " << size << "!= init size: " << pixels.getTotalBytes();
1272return GST_FLOW_ERROR;
1273}
1274}
1275mutex.lock();
1276if(pixels.isAllocated()){
1277backBuffer = _buffer;
1278if(stride > 0) {
1279backPixels.setFromAlignedPixels(GST_BUFFER_DATA (backBuffer.get()),pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat(),stride);
1280}
1281else {
1282backPixels.setFromExternalPixels(GST_BUFFER_DATA (backBuffer.get()),pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat());
1283eventPixels.setFromExternalPixels(GST_BUFFER_DATA (backBuffer.get()),pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat());
1284}
1285bBackPixelsChanged=true;
1286mutex.unlock();
1287if(stride == 0) {
1288ofNotifyEvent(prerollEvent,eventPixels);
1289}
1290}else{
1291if(isStream && appsink){
1292appsink->on_stream_prepared();
1293}else{
1294ofLogError("ofGstVideoUtils") << "preroll_cb(): received a preroll without allocation";
1295}
1296mutex.unlock();
1297}
1298return GST_FLOW_OK;
1299}
1300#else
1301
1302static GstVideoInfo getVideoInfo(GstSample * sample){
1303GstCaps *caps = gst_sample_get_caps(sample);
1304GstVideoInfo vinfo;
1305if(caps){
1306gst_video_info_from_caps (&vinfo, caps);
1307}else{
1308ofLogError() << "couldn't get sample caps";
1309}
1310return vinfo;
1311}
1312
1313GstFlowReturn ofGstVideoUtils::process_sample(shared_ptr<GstSample> sample){
1314GstBuffer * _buffer = gst_sample_get_buffer(sample.get());
1315
1316#ifdef OF_USE_GST_GL
1317if (gst_buffer_map (_buffer, &mapinfo, (GstMapFlags)(GST_MAP_READ | GST_MAP_GL))){
1318if (gst_is_gl_memory (mapinfo.memory)) {
1319bufferQueue.push(sample);
1320gst_buffer_unmap(_buffer, &mapinfo);
1321bool newTexture=false;
1322std::unique_lock<std::mutex> lock(mutex);
1323while(bufferQueue.size()>2){
1324backBuffer = bufferQueue.front();
1325bufferQueue.pop();
1326newTexture = true;
1327}
1328if(newTexture){
1329GstBuffer * _buffer = gst_sample_get_buffer(backBuffer.get());
1330gst_buffer_map (_buffer, &mapinfo, (GstMapFlags)(GST_MAP_READ | GST_MAP_GL));
1331auto texId = *(guint*)mapinfo.data;
1332backTexture.setUseExternalTextureID(texId);
1333ofTextureData & texData = backTexture.getTextureData();
1334texData.bAllocated = true;
1335texData.bFlipTexture = false;
1336texData.glInternalFormat = GL_RGBA;
1337texData.height = getHeight();
1338texData.width = getWidth();
1339texData.magFilter = GL_LINEAR;
1340texData.minFilter = GL_LINEAR;
1341texData.tex_h = getHeight();
1342texData.tex_w = getWidth();
1343texData.tex_u = 1;
1344texData.tex_t = 1;
1345texData.textureID = texId;
1346texData.textureTarget = GL_TEXTURE_2D;
1347texData.wrapModeHorizontal = GL_CLAMP_TO_EDGE;
1348texData.wrapModeVertical = GL_CLAMP_TO_EDGE;
1349bBackPixelsChanged=true;
1350gst_buffer_unmap(_buffer,&mapinfo);
1351}
1352return GST_FLOW_OK;
1353}
1354}
1355#endif
1356
1357// video frame has normal texture
1358gst_buffer_map (_buffer, &mapinfo, GST_MAP_READ);
1359guint size = mapinfo.size;
1360
1361size_t stride = 0;
1362if(pixels.isAllocated() && (pixels.getTotalBytes() != size_t(size))){
1363GstVideoInfo v_info = getVideoInfo(sample.get());
1364stride = v_info.stride[0];
1365
1366if(stride == (pixels.getWidth() * pixels.getBytesPerPixel())) {
1367ofLogError("ofGstVideoUtils") << "buffer_cb(): error on new buffer, buffer size: " << size << "!= init size: " << pixels.getTotalBytes();
1368return GST_FLOW_ERROR;
1369}
1370}
1371mutex.lock();
1372if(!copyPixels){
1373backBuffer = sample;
1374}
1375
1376if(pixels.isAllocated()){
1377if(stride > 0) {
1378if(pixels.getPixelFormat() == OF_PIXELS_I420){
1379GstVideoInfo v_info = getVideoInfo(sample.get());
1380std::vector<size_t> strides{size_t(v_info.stride[0]),size_t(v_info.stride[1]),size_t(v_info.stride[2])};
1381backPixels.setFromAlignedPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat(),strides);
1382} else {
1383backPixels.setFromAlignedPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat(),stride);
1384}
1385} else if(!copyPixels){
1386backPixels.setFromExternalPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat());
1387eventPixels.setFromExternalPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat());
1388}else{
1389backPixels.setFromPixels(mapinfo.data,pixels.getWidth(),pixels.getHeight(),pixels.getPixelFormat());
1390}
1391
1392bBackPixelsChanged=true;
1393mutex.unlock();
1394if(stride == 0) {
1395ofNotifyEvent(prerollEvent,eventPixels);
1396}
1397}else{
1398mutex.unlock();
1399if(appsink){
1400appsink->on_stream_prepared();
1401}else{
1402GstVideoInfo v_info = getVideoInfo(sample.get());
1403allocate(v_info.width,v_info.height,getOFFormat(v_info.finfo->format));
1404}
1405}
1406gst_buffer_unmap(_buffer, &mapinfo);
1407return GST_FLOW_OK;
1408}
1409#endif
1410
1411#if GST_VERSION_MAJOR==0
1412GstFlowReturn ofGstVideoUtils::preroll_cb(shared_ptr<GstBuffer> buffer){
1413GstFlowReturn ret = process_buffer(buffer);
1414if(ret==GST_FLOW_OK){
1415return ofGstUtils::preroll_cb(buffer);
1416}else{
1417return ret;
1418}
1419}
1420#else
1421GstFlowReturn ofGstVideoUtils::preroll_cb(shared_ptr<GstSample> sample){
1422GstFlowReturn ret = process_sample(sample);
1423if(ret==GST_FLOW_OK){
1424return ofGstUtils::preroll_cb(sample);
1425}else{
1426return ret;
1427}
1428}
1429#endif
1430
1431#if GST_VERSION_MAJOR==0
1432GstFlowReturn ofGstVideoUtils::buffer_cb(shared_ptr<GstBuffer> buffer){
1433GstFlowReturn ret = process_buffer(buffer);
1434if(ret==GST_FLOW_OK){
1435return ofGstUtils::buffer_cb(buffer);
1436}else{
1437return ret;
1438}
1439}
1440#else
1441GstFlowReturn ofGstVideoUtils::buffer_cb(shared_ptr<GstSample> sample){
1442GstFlowReturn ret = process_sample(sample);
1443if(ret==GST_FLOW_OK){
1444return ofGstUtils::buffer_cb(sample);
1445}else{
1446return ret;
1447}
1448}
1449#endif
1450
1451void ofGstVideoUtils::eos_cb(){
1452ofGstUtils::eos_cb();
1453ofEventArgs args;
1454ofNotifyEvent(eosEvent,args);
1455}
1456
1457#endif
1458