framework2
460 строк · 12.7 Кб
1/*
2* ofGstVideoUtils.cpp
3*
4* Created on: 16/01/2011
5* Author: arturo
6*/
7
8#include "ofGstVideoPlayer.h"
9#include <gst/gst.h>
10#include <gst/video/video.h>
11#include <gst/app/gstappsink.h>
12#include "ofConstants.h"
13#include "ofGstUtils.h"
14
15ofGstVideoPlayer::ofGstVideoPlayer(){
16nFrames = 0;
17internalPixelFormat = OF_PIXELS_RGB;
18bIsStream = false;
19bIsAllocated = false;
20threadAppSink = false;
21bAsyncLoad = false;
22videoUtils.setSinkListener(this);
23fps_d = 1;
24fps_n = 1;
25}
26
27ofGstVideoPlayer::~ofGstVideoPlayer(){
28close();
29}
30
31bool ofGstVideoPlayer::setPixelFormat(ofPixelFormat pixelFormat){
32internalPixelFormat = pixelFormat;
33return true;
34}
35
36ofPixelFormat ofGstVideoPlayer::getPixelFormat() const {
37return internalPixelFormat;
38}
39
40
41bool ofGstVideoPlayer::createPipeline(std::string name){
42#ifndef OF_USE_GST_GL
43#if GST_VERSION_MAJOR==0
44GstCaps *caps;
45int bpp;
46switch(internalPixelFormat){
47case OF_PIXELS_GRAY:
48bpp = 8;
49caps = gst_caps_new_simple("video/x-raw-gray",
50"bpp", G_TYPE_INT, bpp,
51"depth", G_TYPE_INT, 8,
52NULL);
53break;
54case OF_PIXELS_RGB:
55bpp = 24;
56caps = gst_caps_new_simple("video/x-raw-rgb",
57"bpp", G_TYPE_INT, bpp,
58"depth", G_TYPE_INT, 24,
59"endianness",G_TYPE_INT,4321,
60"red_mask",G_TYPE_INT,0xff0000,
61"green_mask",G_TYPE_INT,0x00ff00,
62"blue_mask",G_TYPE_INT,0x0000ff,
63NULL);
64break;
65case OF_PIXELS_RGBA:
66bpp = 32;
67caps = gst_caps_new_simple("video/x-raw-rgb",
68"bpp", G_TYPE_INT, bpp,
69"depth", G_TYPE_INT, 32,
70"endianness",G_TYPE_INT,4321,
71"red_mask",G_TYPE_INT,0xff000000,
72"green_mask",G_TYPE_INT,0x00ff0000,
73"blue_mask",G_TYPE_INT,0x0000ff00,
74"alpha_mask",G_TYPE_INT,0x000000ff,
75NULL);
76break;
77case OF_PIXELS_BGRA:
78bpp = 32;
79caps = gst_caps_new_simple("video/x-raw-rgb",
80"bpp", G_TYPE_INT, bpp,
81"depth", G_TYPE_INT, 32,
82"endianness",G_TYPE_INT,4321,
83"red_mask",G_TYPE_INT,0x0000ff00,
84"green_mask",G_TYPE_INT,0x00ff0000,
85"blue_mask",G_TYPE_INT,0xff000000,
86"alpha_mask",G_TYPE_INT,0x000000ff,
87NULL);
88break;
89default:
90bpp = 32;
91caps = gst_caps_new_simple("video/x-raw-rgb",
92"bpp", G_TYPE_INT, bpp,
93"depth", G_TYPE_INT, 24,
94"endianness",G_TYPE_INT,4321,
95"red_mask",G_TYPE_INT,0xff0000,
96"green_mask",G_TYPE_INT,0x00ff00,
97"blue_mask",G_TYPE_INT,0x0000ff,
98NULL);
99break;
100}
101#else
102std::string mime="video/x-raw";
103
104GstCaps *caps;
105if(internalPixelFormat==OF_PIXELS_NATIVE){
106caps = gst_caps_from_string((mime + ",format={RGBA,BGRA,RGB,BGR,RGB16,GRAY8,YV12,I420,NV12,NV21,YUY2}").c_str());
107}else{
108std::string format = ofGstVideoUtils::getGstFormatName(internalPixelFormat);
109caps = gst_caps_new_simple(mime.c_str(),
110"format", G_TYPE_STRING, format.c_str(),
111NULL);
112}
113
114#endif
115
116#if GST_VERSION_MAJOR==0
117GstElement * gstPipeline = gst_element_factory_make("playbin2","player");
118#else
119#if GST_VERSION_MAJOR==1 && GST_VERSION_MINOR > 18
120//TODO: Fedora 35 onwards has issues with playbin - so using playbin3
121//TODO: Check future GStreamer versions and potentially return to "playbin", or use a different approach to create the pipeline.
122GstElement * gstPipeline = gst_element_factory_make("playbin3","player");
123#else
124GstElement * gstPipeline = gst_element_factory_make("playbin","player");
125#endif
126#endif
127g_object_ref_sink(gstPipeline);
128g_object_set(G_OBJECT(gstPipeline), "uri", name.c_str(), (void*)NULL);
129
130// create the oF appsink for video rgb without sync to clock
131GstElement * gstSink = gst_element_factory_make("appsink", "app_sink");
132gst_app_sink_set_caps(GST_APP_SINK(gstSink), caps);
133gst_caps_unref(caps);
134
135if(threadAppSink){
136GstElement * appQueue = gst_element_factory_make("queue","appsink_queue");
137g_object_set(G_OBJECT(appQueue), "leaky", 0, "silent", 1, (void*)NULL);
138GstElement* appBin = gst_bin_new("app_bin");
139gst_bin_add(GST_BIN(appBin), appQueue);
140GstPad* appQueuePad = gst_element_get_static_pad(appQueue, "sink");
141GstPad* ghostPad = gst_ghost_pad_new("app_bin_sink", appQueuePad);
142gst_object_unref(appQueuePad);
143gst_element_add_pad(appBin, ghostPad);
144
145gst_bin_add(GST_BIN(appBin), gstSink);
146gst_element_link(appQueue, gstSink);
147
148g_object_set (G_OBJECT(gstPipeline),"video-sink",appBin,(void*)NULL);
149}else{
150g_object_set (G_OBJECT(gstPipeline),"video-sink",gstSink,(void*)NULL);
151}
152
153#ifdef TARGET_WIN32
154GstElement *audioSink = gst_element_factory_make("directsoundsink", NULL);
155g_object_set (G_OBJECT(gstPipeline),"audio-sink",audioSink,(void*)NULL);
156#endif
157
158return videoUtils.setPipelineWithSink(gstPipeline,gstSink,bIsStream);
159
160#else
161/*auto gstPipeline = gst_parse_launch(("uridecodebin uri=" + name + " ! glcolorscale name=gl_filter ! appsink name=app_sink").c_str(),NULL);
162auto gstSink = gst_bin_get_by_name(GST_BIN(gstPipeline),"app_sink");
163auto glfilter = gst_bin_get_by_name(GST_BIN(gstPipeline),"gl_filter");
164gst_app_sink_set_caps(GST_APP_SINK(gstSink), caps);
165gst_caps_unref(caps);
166
167glXMakeCurrent (ofGetX11Display(), None, 0);
168glDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display(ofGetX11Display());
169glContext = gst_gl_context_new_wrapped (glDisplay, (guintptr) ofGetGLXContext(),
170GST_GL_PLATFORM_GLX, GST_GL_API_OPENGL);
171
172g_object_set (G_OBJECT (glfilter), "other-context", glContext, NULL);
173
174// FIXME: this seems to be the way to add the context in 1.4.5
175//
176// GstBus * bus = gst_pipeline_get_bus (GST_PIPELINE(gstPipeline));
177// gst_bus_enable_sync_message_emission (bus);
178// g_signal_connect (bus, "sync-message", G_CALLBACK (sync_bus_call), this);
179// gst_object_unref(bus);
180
181auto ret = videoUtils.setPipelineWithSink(gstPipeline,gstSink,bIsStream);
182glXMakeCurrent (ofGetX11Display(), ofGetX11Window(), ofGetGLXContext());
183return ret;*/
184
185return videoUtils.setPipeline("uridecodebin uri=" + name,internalPixelFormat,bIsStream,-1,-1);
186//return videoUtils.setPipeline("filesrc location=" + name + " ! qtdemux ",internalPixelFormat,bIsStream,-1,-1);
187#endif
188}
189
190void ofGstVideoPlayer::loadAsync(std::string name){
191bAsyncLoad = true;
192load(name);
193}
194
195bool ofGstVideoPlayer::load(std::string name){
196if( name.find( "file://",0 ) != std::string::npos){
197bIsStream = bAsyncLoad;
198}else if( name.find( "://",0 ) == std::string::npos){
199GError * err = NULL;
200gchar* name_ptr = gst_filename_to_uri(ofToDataPath(name).c_str(),&err);
201name = name_ptr;
202g_free(name_ptr);
203if(err) g_free(err);
204//name = ofToDataPath(name);
205bIsStream = bAsyncLoad;
206}else{
207bIsStream = true;
208}
209ofLogVerbose("ofGstVideoPlayer") << "loadMovie(): loading \"" << name << "\"";
210
211if(isInitialized()){
212gst_element_set_state (videoUtils.getPipeline(), GST_STATE_READY);
213if(!bIsStream){
214gst_element_get_state (videoUtils.getPipeline(), NULL, NULL, -1);
215}
216internalPixelFormat = OF_PIXELS_NATIVE;
217bIsAllocated = false;
218videoUtils.reallocateOnNextFrame();
219g_object_set(G_OBJECT(videoUtils.getPipeline()), "uri", name.c_str(), (void*)NULL);
220gst_element_set_state (videoUtils.getPipeline(), GST_STATE_PAUSED);
221if(!bIsStream){
222gst_element_get_state (videoUtils.getPipeline(), NULL, NULL, -1);
223return allocate();
224}else{
225return true;
226}
227}else{
228ofGstUtils::startGstMainLoop();
229return createPipeline(name) &&
230videoUtils.startPipeline() &&
231(bIsStream || allocate());
232}
233}
234
235void ofGstVideoPlayer::setThreadAppSink(bool threaded){
236threadAppSink = threaded;
237}
238
239
240bool ofGstVideoPlayer::allocate(){
241if(bIsAllocated){
242return true;
243}
244
245guint64 durationNanos = videoUtils.getDurationNanos();
246
247nFrames = 0;
248if(GstPad* pad = gst_element_get_static_pad(videoUtils.getSink(), "sink")){
249#if GST_VERSION_MAJOR==0
250int width,height;
251if(gst_video_get_size(GST_PAD(pad), &width, &height)){
252if(!videoUtils.allocate(width,height,internalPixelFormat)) return false;
253}else{
254ofLogError("ofGstVideoPlayer") << "allocate(): couldn't query width and height";
255return false;
256}
257
258const GValue *framerate = gst_video_frame_rate(pad);
259fps_n=0;
260fps_d=0;
261if(framerate && GST_VALUE_HOLDS_FRACTION (framerate)){
262fps_n = gst_value_get_fraction_numerator (framerate);
263fps_d = gst_value_get_fraction_denominator (framerate);
264nFrames = (float)(durationNanos / (float)GST_SECOND) * (float)fps_n/(float)fps_d;
265ofLogVerbose("ofGstVideoPlayer") << "allocate(): framerate: " << fps_n << "/" << fps_d;
266}else{
267ofLogWarning("ofGstVideoPlayer") << "allocate(): cannot get framerate, frame seek won't work";
268}
269bIsAllocated = true;
270#else
271if(GstCaps *caps = gst_pad_get_current_caps (GST_PAD (pad))){
272GstVideoInfo info;
273gst_video_info_init (&info);
274if (gst_video_info_from_caps (&info, caps)){
275ofPixelFormat format = ofGstVideoUtils::getOFFormat(GST_VIDEO_INFO_FORMAT(&info));
276if(format!=internalPixelFormat){
277ofLogVerbose("ofGstVideoPlayer") << "allocating as " << info.width << "x" << info.height << " " << info.finfo->description << " " << info.finfo->name;
278internalPixelFormat = format;
279}
280if(!videoUtils.allocate(info.width,info.height,format)) return false;
281}else{
282ofLogError("ofGstVideoPlayer") << "allocate(): couldn't query width and height";
283return false;
284}
285
286fps_n = info.fps_n;
287fps_d = info.fps_d;
288nFrames = (float)(durationNanos / (float)GST_SECOND) * (float)fps_n/(float)fps_d;
289gst_caps_unref(caps);
290bIsAllocated = true;
291}else{
292ofLogError("ofGstVideoPlayer") << "allocate(): cannot get pipeline caps";
293bIsAllocated = false;
294}
295#endif
296gst_object_unref(GST_OBJECT(pad));
297}else{
298ofLogError("ofGstVideoPlayer") << "allocate(): cannot get sink pad";
299bIsAllocated = false;
300}
301return bIsAllocated;
302}
303
304void ofGstVideoPlayer::on_stream_prepared(){
305if(!bIsAllocated) allocate();
306}
307
308int ofGstVideoPlayer::getCurrentFrame() const {
309int frame = 0;
310
311// zach I think this may fail on variable length frames...
312float pos = getPosition();
313if(pos == -1) return -1;
314
315
316float framePosInFloat = ((float)getTotalNumFrames() * pos);
317int framePosInInt = (int)framePosInFloat;
318float floatRemainder = (framePosInFloat - framePosInInt);
319if (floatRemainder > 0.5f) framePosInInt = framePosInInt + 1;
320//frame = (int)ceil((getTotalNumFrames() * getPosition()));
321frame = framePosInInt;
322
323return frame;
324}
325
326int ofGstVideoPlayer::getTotalNumFrames() const {
327return nFrames;
328}
329
330void ofGstVideoPlayer::firstFrame(){
331setFrame(0);
332}
333
334void ofGstVideoPlayer::nextFrame(){
335gint64 currentFrame = getCurrentFrame();
336if(currentFrame!=-1) setFrame(currentFrame + 1);
337}
338
339void ofGstVideoPlayer::previousFrame(){
340gint64 currentFrame = getCurrentFrame();
341if(currentFrame!=-1) setFrame(currentFrame - 1);
342}
343
344void ofGstVideoPlayer::setFrame(int frame){ // frame 0 = first frame...
345float pct = (float)frame / (float)nFrames;
346setPosition(pct);
347}
348
349bool ofGstVideoPlayer::isStream() const {
350return bIsStream;
351}
352
353void ofGstVideoPlayer::update(){
354videoUtils.update();
355}
356
357void ofGstVideoPlayer::play(){
358videoUtils.play();
359}
360
361void ofGstVideoPlayer::stop(){
362videoUtils.stop();
363}
364
365void ofGstVideoPlayer::setPaused(bool bPause){
366videoUtils.setPaused(bPause);
367}
368
369bool ofGstVideoPlayer::isPaused() const {
370return videoUtils.isPaused();
371}
372
373bool ofGstVideoPlayer::isLoaded() const {
374return videoUtils.isLoaded();
375}
376
377bool ofGstVideoPlayer::isPlaying() const {
378return videoUtils.isPlaying();
379}
380
381float ofGstVideoPlayer::getPosition() const {
382return videoUtils.getPosition();
383}
384
385float ofGstVideoPlayer::getSpeed() const {
386return videoUtils.getSpeed();
387}
388
389float ofGstVideoPlayer::getDuration() const {
390return videoUtils.getDuration();
391}
392
393bool ofGstVideoPlayer::getIsMovieDone() const {
394return videoUtils.getIsMovieDone();
395}
396
397void ofGstVideoPlayer::setPosition(float pct){
398videoUtils.setPosition(pct);
399}
400
401void ofGstVideoPlayer::setVolume(float volume){
402videoUtils.setVolume(volume);
403}
404
405void ofGstVideoPlayer::setLoopState(ofLoopType state){
406videoUtils.setLoopState(state);
407}
408
409ofLoopType ofGstVideoPlayer::getLoopState() const {
410return videoUtils.getLoopState();
411}
412
413void ofGstVideoPlayer::setSpeed(float speed){
414videoUtils.setSpeed(speed);
415}
416
417void ofGstVideoPlayer::close(){
418bIsAllocated = false;
419videoUtils.close();
420}
421
422bool ofGstVideoPlayer::isFrameNew() const {
423return videoUtils.isFrameNew();
424}
425
426ofPixels& ofGstVideoPlayer::getPixels(){
427return videoUtils.getPixels();
428}
429
430const ofPixels& ofGstVideoPlayer::getPixels() const {
431return videoUtils.getPixels();
432}
433
434ofTexture * ofGstVideoPlayer::getTexturePtr(){
435return videoUtils.getTexture();
436}
437
438float ofGstVideoPlayer::getHeight() const {
439return videoUtils.getHeight();
440}
441
442float ofGstVideoPlayer::getWidth() const {
443return videoUtils.getWidth();
444}
445
446ofGstVideoUtils * ofGstVideoPlayer::getGstVideoUtils(){
447return &videoUtils;
448}
449
450void ofGstVideoPlayer::setFrameByFrame(bool frameByFrame){
451videoUtils.setFrameByFrame(frameByFrame);
452}
453
454bool ofGstVideoPlayer::isThreadedAppSink() const{
455return threadAppSink;
456}
457
458bool ofGstVideoPlayer::isFrameByFrame() const{
459return videoUtils.isFrameByFrame();
460}
461