LCOV - code coverage report
Current view: top level - filters - out_video.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 575 984 58.4 %
Date: 2021-04-29 23:48:07 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / video output filter
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : 
      27             : #include <gpac/filters.h>
      28             : #include <gpac/constants.h>
      29             : #include <gpac/modules/video_out.h>
      30             : #include <gpac/color.h>
      31             : 
      32             : #ifndef GPAC_DISABLE_PLAYER
      33             : 
      34             : //#define GPAC_DISABLE_3D
      35             : 
      36             : #if !defined(GPAC_DISABLE_3D) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
      37             : #define VOUT_USE_OPENGL
      38             : 
      39             : //include openGL
      40             : #include "../compositor/gl_inc.h"
      41             : 
      42             : #define DEL_SHADER(_a) if (_a) { glDeleteShader(_a); _a = 0; }
      43             : #define DEL_PROGRAM(_a) if (_a) { glDeleteProgram(_a); _a = 0; }
      44             : 
      45             : 
      46             : #define TEXTURE_TYPE GL_TEXTURE_2D
      47             : 
      48             : 
      49             : #if defined( _LP64 ) && defined(CONFIG_DARWIN_GL)
      50             : #define GF_SHADERID u64
      51             : #else
      52             : #define GF_SHADERID u32
      53             : #endif
      54             : 
      55             : 
      56             : static char *default_glsl_vertex = "\
      57             :         attribute vec4 gfVertex;\
      58             :         attribute vec2 gfTexCoord;\
      59             :         varying vec2 TexCoord;\
      60             :         void main(void)\
      61             :         {\
      62             :                 gl_Position = gl_ModelViewProjectionMatrix * gfVertex;\
      63             :                 TexCoord = gfTexCoord;\
      64             :         }";
      65             : 
      66             : #endif
      67             : 
      68             : typedef enum
      69             : {
      70             :         MODE_GL,
      71             :         MODE_GL_PBO,
      72             :         MODE_2D,
      73             :         MODE_2D_SOFT,
      74             : } GF_VideoOutMode;
      75             : 
      76             : 
      77             : enum
      78             : {
      79             :         FLIP_NO,
      80             :         FLIP_VERT,
      81             :         FLIP_HORIZ,
      82             :         FLIP_BOTH,
      83             :         FLIP_BOTH2,
      84             : };
      85             : 
      86             : 
      87             : typedef struct
      88             : {
      89             :         //options
      90             :         char *drv;
      91             :         GF_VideoOutMode disp;
      92             :         Bool vsync, linear, fullscreen, drop, hide, step;
      93             :         GF_Fraction64 dur;
      94             :         Double speed, hold;
      95             :         u32 back, vflip, vrot;
      96             :         GF_PropVec2i wsize, owsize;
      97             :         GF_PropVec2i wpos;
      98             :         Double start;
      99             :         u32 buffer, mbuffer, rbuffer;
     100             :         GF_Fraction vdelay;
     101             :         const char *out;
     102             :         GF_PropUIntList dumpframes;
     103             : 
     104             :         GF_PropVec4i olwnd;
     105             :         GF_PropVec2i olsize;
     106             :         GF_PropData oldata;
     107             : 
     108             :         GF_Filter *filter;
     109             :         GF_FilterPid *pid;
     110             :         u32 width, height, stride, pfmt, timescale;
     111             :         GF_Fraction fps;
     112             : 
     113             :         GF_VideoOutput *video_out;
     114             : 
     115             :         u64 first_cts_plus_one;
     116             :         u64 clock_at_first_cts, last_frame_clock, clock_at_first_frame;
     117             :         u32 last_pck_dur_us;
     118             :         Bool aborted;
     119             :         u32 display_width, display_height;
     120             :         Bool display_changed;
     121             :         Float dh, dw, oh, ow;
     122             :         Bool has_alpha;
     123             :         u32 nb_frames;
     124             : 
     125             :         u32 key_states;
     126             : 
     127             :         //if source is raw live grab (webcam/etc), we don't trust cts and always draw the frame
     128             :         //this is needed for cases where we have a sudden jump in timestamps as is the case with ffmpeg: not doing so would
     129             :         //hold the frame until its CTS is reached, triggering drops at capture time
     130             :         Bool raw_grab;
     131             : 
     132             : #ifdef VOUT_USE_OPENGL
     133             :         GLint glsl_program;
     134             :         GF_SHADERID vertex_shader;
     135             :         GF_SHADERID fragment_shader;
     136             : 
     137             :         GF_GLTextureWrapper tx;
     138             : 
     139             :         GLuint overlay_tx;
     140             : #endif // VOUT_USE_OPENGL
     141             : 
     142             :         u32 num_textures;
     143             : 
     144             :         u32 uv_w, uv_h, uv_stride, bit_depth;
     145             :         Bool is_yuv, in_fullscreen;
     146             :         u32 nb_drawn;
     147             : 
     148             :         GF_Fraction sar;
     149             : 
     150             :         Bool force_release;
     151             :         GF_FilterPacket *last_pck;
     152             : 
     153             :         s64 pid_delay;
     154             :         Bool buffer_done;
     155             :         Bool no_buffering;
     156             :         Bool dump_done;
     157             : 
     158             :         u32 dump_f_idx;
     159             :         char *dump_buffer;
     160             : 
     161             :         Bool force_vout;
     162             : 
     163             :         Bool do_seek;
     164             :         Bool update_oldata;
     165             : 
     166             :         Bool full_range;
     167             :         s32 cmx;
     168             : 
     169             :         u64 rebuffer;
     170             : 
     171             :         Bool force_reconfig_pid;
     172             : } GF_VideoOutCtx;
     173             : 
     174             : static GF_Err vout_draw_frame(GF_VideoOutCtx *ctx);
     175             : 
     176             : 
     177             : static void vout_reset_overlay(GF_VideoOutCtx *ctx)
     178             : {
     179             : #ifdef VOUT_USE_OPENGL
     180           2 :         if (ctx->overlay_tx) {
     181           0 :                 glDeleteTextures(1, &ctx->overlay_tx);
     182           0 :                 ctx->overlay_tx = 0;
     183             :         }
     184             : #endif
     185             : }
     186             : 
     187             : #ifdef VOUT_USE_OPENGL
     188             : 
     189             : static void vout_make_gl_current(GF_VideoOutCtx *ctx)
     190             : {
     191             :         GF_Event evt;
     192         157 :         evt.type = GF_EVENT_SET_GL;
     193         157 :         ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     194             : }
     195             : 
     196          50 : static Bool vout_compile_shader(GF_SHADERID shader_id, const char *name, const char *source)
     197             : {
     198          50 :         GLint blen = 0;
     199          50 :         GLsizei slen = 0;
     200             :         u32 len;
     201          50 :         if (!source || !shader_id) return 0;
     202          50 :         len = (u32) strlen(source);
     203          50 :         glShaderSource(shader_id, 1, &source, &len);
     204          50 :         glCompileShader(shader_id);
     205             : 
     206          50 :         glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH , &blen);
     207          50 :         if (blen > 1) {
     208           0 :                 char* compiler_log = (char*) gf_malloc(blen);
     209             : #if defined(GPAC_USE_GLES2)
     210             :                 glGetShaderInfoLog(shader_id, blen, &slen, compiler_log);
     211             : #elif defined(CONFIG_DARWIN_GL)
     212             :                 glGetInfoLogARB((GLhandleARB) shader_id, blen, &slen, compiler_log);
     213             : #else
     214           0 :                 glGetInfoLogARB(shader_id, blen, &slen, compiler_log);
     215             : #endif
     216           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[GLSL] Failed to compile shader %s: %s\n", name, compiler_log));
     217           0 :                 gf_free (compiler_log);
     218           0 :                 return 0;
     219             :         }
     220             :         return 1;
     221             : }
     222             : #endif // VOUT_USE_OPENGL
     223             : 
     224             : 
     225         425 : static void vout_set_caption(GF_VideoOutCtx *ctx)
     226             : {
     227             :         GF_Event evt;
     228             :         memset(&evt, 0, sizeof(GF_Event));
     229         425 :         evt.type = GF_EVENT_SET_CAPTION;
     230         425 :         evt.caption.caption = gf_filter_pid_orig_src_args(ctx->pid, GF_FALSE);
     231         425 :         if (!evt.caption.caption) evt.caption.caption = gf_filter_pid_get_source_filter_name(ctx->pid);
     232         425 :         if (evt.caption.caption) {
     233         425 :                 if (!strncmp(evt.caption.caption, "src=", 4)) evt.caption.caption += 4;
     234         425 :                 if (!strncmp(evt.caption.caption, "./", 2)) evt.caption.caption += 2;
     235         425 :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     236             :         }
     237         425 : }
     238             : 
     239          29 : static GF_Err resize_video_output(GF_VideoOutCtx *ctx, u32 dw, u32 dh)
     240             : {
     241             :         GF_Event evt;
     242             : 
     243             :         memset(&evt, 0, sizeof(GF_Event));
     244          29 :         evt.type = GF_EVENT_VIDEO_SETUP;
     245          29 :         evt.setup.width = dw;
     246          29 :         evt.setup.height = dh;
     247             : 
     248             : #ifdef VOUT_USE_OPENGL
     249          29 :         if (ctx->disp<MODE_2D) {
     250          25 :                 evt.setup.use_opengl = GF_TRUE;
     251             :                 //always double buffer
     252          25 :                 evt.setup.back_buffer = gf_opts_get_bool("core", "gl-doublebuf");
     253             :         } else
     254             : #endif
     255             :         {
     256           4 :                 evt.setup.back_buffer = 1;
     257             :         }
     258          29 :         evt.setup.disable_vsync = !ctx->vsync;
     259          29 :         ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     260          29 :         if (evt.setup.use_opengl) {
     261          25 :                 gf_opengl_init();
     262             :         }
     263          29 :         if (!ctx->in_fullscreen) {
     264          29 :                 ctx->display_width = evt.setup.width;
     265          29 :                 ctx->display_height = evt.setup.height;
     266             :         }
     267          29 :         ctx->display_changed = GF_TRUE;
     268             : 
     269             : #if defined(VOUT_USE_OPENGL) && defined(WIN32)
     270             :         if (evt.setup.use_opengl)
     271             :                 gf_opengl_init();
     272             : 
     273             :         if ((ctx->disp<MODE_2D) && (glCompileShader == NULL)) {
     274             :                 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[VideoOut] Failed to load openGL, fallback to 2D blit\n"));
     275             :                 evt.setup.use_opengl = GF_FALSE;
     276             :                 evt.setup.back_buffer = 1;
     277             :                 ctx->disp = MODE_2D;
     278             :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     279             :         }
     280             : #endif
     281             : 
     282          29 :         if (!ctx->in_fullscreen) {
     283             :                 memset(&evt, 0, sizeof(GF_Event));
     284          29 :                 evt.type = GF_EVENT_SIZE;
     285          29 :                 evt.size.width = dw;
     286          29 :                 evt.size.height = dh;
     287          29 :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     288             :         }
     289             : 
     290          29 :         if (ctx->pid) {
     291             :                 GF_FilterEvent fevt;
     292          29 :                 GF_FEVT_INIT(fevt, GF_FEVT_VISIBILITY_HINT, ctx->pid);
     293          29 :                 fevt.visibility_hint.max_x = dw;
     294          29 :                 fevt.visibility_hint.max_y = dh;
     295          29 :                 gf_filter_pid_send_event(ctx->pid, &fevt);
     296             :         }
     297          29 :         return GF_OK;
     298             : }
     299             : 
     300          37 : static GF_Err vout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     301             : {
     302             :         GF_Event evt;
     303             :         const GF_PropertyValue *p;
     304             :         u32 w, h, pfmt, stride, stride_uv, timescale, dw, dh, hw, hh;
     305             :         Bool full_range;
     306             :         Bool sar_changed = GF_FALSE;
     307             :         s32 cmx;
     308          37 :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
     309             : 
     310             :         //if we have a pending packet, draw it now
     311          37 :         if (ctx->last_pck) {
     312           0 :                 vout_draw_frame(ctx);
     313           0 :                 gf_filter_pck_unref(ctx->last_pck);
     314           0 :                 ctx->last_pck = NULL;
     315             :         }
     316             : 
     317             : 
     318          37 :         if (is_remove) {
     319             :                 assert(ctx->pid==pid);
     320           0 :                 ctx->pid=NULL;
     321           0 :                 return GF_OK;
     322             :         }
     323             :         assert(!ctx->pid || (ctx->pid==pid));
     324          37 :         if (!gf_filter_pid_check_caps(pid))
     325             :                 return GF_NOT_SUPPORTED;
     326             : 
     327          37 :         w = h = pfmt = timescale = stride = 0;
     328          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
     329          37 :         if (p) timescale = p->value.uint;
     330             : 
     331          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
     332          37 :         if (p) w = p->value.uint;
     333          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
     334          37 :         if (p) h = p->value.uint;
     335          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
     336          37 :         if (p) pfmt = p->value.uint;
     337             : 
     338          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
     339          37 :         if (p) ctx->fps = p->value.frac;
     340          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
     341          37 :         if (p) stride = p->value.uint;
     342          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE_UV);
     343          37 :         stride_uv = p ? p->value.uint : 0;
     344             : 
     345          37 :         ctx->sar.num = ctx->sar.den = 1;
     346          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
     347          37 :         if (p && p->value.frac.den && p->value.frac.num) {
     348           0 :                 if (ctx->sar.num * p->value.frac.den != p->value.frac.num * ctx->sar.den)
     349             :                         sar_changed = GF_TRUE;
     350           0 :                 ctx->sar = p->value.frac;
     351             :         }
     352             : 
     353          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
     354          37 :         ctx->pid_delay = p ? p->value.longsint : 0;
     355             : 
     356          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAY_BUFFER);
     357          37 :         ctx->no_buffering = (p && !p->value.sint) ? GF_TRUE : GF_FALSE;
     358          37 :         if (ctx->no_buffering) {
     359          19 :                 ctx->buffer_done = GF_TRUE;
     360          19 :                 ctx->rebuffer = 0;
     361             :         }
     362          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_RAWGRAB);
     363          37 :         ctx->raw_grab = (p && p->value.boolean) ? GF_TRUE : GF_FALSE;
     364             : 
     365          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_RANGE);
     366          37 :         full_range = (p && p->value.boolean) ? GF_TRUE : GF_FALSE;
     367          37 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_COLR_MX);
     368          37 :         cmx = p ? (s32) p->value.uint : GF_CICP_MX_UNSPECIFIED;
     369             : 
     370             : 
     371          37 :         if (!ctx->pid) {
     372             :                 GF_FilterEvent fevt;
     373             : 
     374          28 :                 GF_FEVT_INIT(fevt, GF_FEVT_BUFFER_REQ, pid);
     375          28 :                 fevt.buffer_req.max_buffer_us = ctx->buffer * 1000;
     376             :                 //we have a max buffer, move our computed max to playout and setup max buffer
     377          28 :                 if (ctx->mbuffer > ctx->buffer) {
     378           0 :                         fevt.buffer_req.max_playout_us = fevt.buffer_req.max_buffer_us;
     379           0 :                         fevt.buffer_req.max_buffer_us = ctx->mbuffer * 1000;
     380             :                 }
     381             : 
     382          28 :                 gf_filter_pid_send_event(pid, &fevt);
     383             : 
     384          28 :                 gf_filter_pid_init_play_event(pid, &fevt, ctx->start, ctx->speed, "VideoOut");
     385          28 :                 if (ctx->dur.num && ctx->dur.den) {
     386           0 :                         fevt.play.end_range = ((Double)ctx->dur.num) / ctx->dur.den;
     387           0 :                         fevt.play.end_range += fevt.play.start_range;
     388             :                 }
     389          28 :                 gf_filter_pid_send_event(pid, &fevt);
     390             : 
     391          28 :                 ctx->start = fevt.play.start_range;
     392          28 :                 if ((ctx->speed<0) && (fevt.play.speed>=0))
     393           0 :                         ctx->speed = fevt.play.speed;
     394             : 
     395          28 :                 ctx->pid = pid;
     396             :         }
     397          37 :         vout_set_caption(ctx);
     398             : 
     399          37 :         if (ctx->first_cts_plus_one && ctx->timescale && (ctx->timescale != timescale) ) {
     400           0 :                 ctx->first_cts_plus_one-=1;
     401           0 :                 ctx->first_cts_plus_one *= timescale;
     402           0 :                 ctx->first_cts_plus_one /= ctx->timescale;
     403           0 :                 ctx->first_cts_plus_one+=1;
     404             :         }
     405          37 :         if (!timescale) timescale = 1;
     406          37 :         ctx->timescale = timescale;
     407             : 
     408             :         //pid not yet ready
     409          37 :         if (!pfmt || !w || !h) return GF_OK;
     410             : 
     411          37 :         if ((ctx->width==w) && (ctx->height == h) && (ctx->pfmt == pfmt) && (full_range==ctx->full_range) && (cmx==ctx->cmx) && !sar_changed && !ctx->force_reconfig_pid) return GF_OK;
     412             : 
     413          29 :         ctx->full_range = full_range;
     414          29 :         ctx->cmx = cmx;
     415             :         dw = w;
     416             :         dh = h;
     417             : 
     418          29 :         if ((ctx->disp<MODE_2D) && (ctx->vrot % 2)) {
     419             :                 dw = h;
     420             :                 dh = w;
     421             :         }
     422             : 
     423          29 :         if (ctx->sar.den != ctx->sar.num) {
     424           0 :                 dw = dw * ctx->sar.num / ctx->sar.den;
     425             :         }
     426          29 :         if (ctx->wsize.x==0) ctx->wsize.x = w;
     427          29 :         if (ctx->wsize.y==0) ctx->wsize.y = h;
     428          29 :         if ((ctx->wsize.x>0) && (ctx->wsize.y>0)) {
     429           0 :                 dw = ctx->wsize.x;
     430           0 :                 dh = ctx->wsize.y;
     431             :         }
     432             :         //in 2D mode we need to send a setup event to resize backbuffer to the video source wsize
     433          29 :         if (ctx->disp>=MODE_2D) {
     434             :                 dw = w;
     435             :                 dh = h;
     436           4 :                 if (ctx->sar.den != ctx->sar.num) {
     437           0 :                         dw = dw * ctx->sar.num / ctx->sar.den;
     438             :                 }
     439             :         }
     440             : 
     441          29 :         if ((dw != ctx->display_width) || (dh != ctx->display_height) ) {
     442          29 :                 resize_video_output(ctx, dw, dh);
     443           0 :         } else if ((ctx->wsize.x>0) && (ctx->wsize.y>0)) {
     444           0 :                 ctx->display_width = ctx->wsize.x;
     445           0 :                 ctx->display_height = ctx->wsize.y;
     446           0 :                 ctx->display_changed = GF_TRUE;
     447             :         }
     448             : 
     449          29 :         ctx->owsize.x = ctx->display_width;
     450          29 :         ctx->owsize.y = ctx->display_height;
     451             : 
     452          29 :         if (ctx->fullscreen) {
     453           2 :                 u32 nw=ctx->display_width, nh=ctx->display_height;
     454           2 :                 ctx->video_out->SetFullScreen(ctx->video_out, GF_TRUE, &nw, &nh);
     455           2 :                 ctx->in_fullscreen = GF_TRUE;
     456           2 :                 ctx->owsize.x = nw;
     457           2 :                 ctx->owsize.y = nh;
     458          27 :         } else if ((ctx->wpos.x!=-1) && (ctx->wpos.y!=-1)) {
     459             :                 memset(&evt, 0, sizeof(GF_Event));
     460           0 :                 evt.type = GF_EVENT_MOVE;
     461             :                 evt.move.relative = 0;
     462           0 :                 evt.move.x = ctx->wpos.x;
     463           0 :                 evt.move.y = ctx->wpos.y;
     464           0 :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     465             :         }
     466             : 
     467          29 :         ctx->timescale = timescale;
     468          29 :         ctx->width = w;
     469          29 :         ctx->height = h;
     470          29 :         ctx->pfmt = pfmt;
     471          29 :         ctx->uv_w = ctx->uv_h = 0;
     472          29 :         ctx->uv_stride = stride_uv;
     473          29 :         ctx->bit_depth = 0;
     474          29 :         ctx->has_alpha = GF_FALSE;
     475             : 
     476          29 :         if (!stride) {
     477          10 :                 gf_pixel_get_size_info(ctx->pfmt, w, h, NULL, &stride, &ctx->uv_stride, NULL, &ctx->uv_h);
     478             :         }
     479          29 :         ctx->stride = stride ? stride : w;
     480          29 :         if (ctx->dump_buffer) {
     481           0 :                 gf_free(ctx->dump_buffer);
     482           0 :                 ctx->dump_buffer = NULL;
     483             :         }
     484             : 
     485          29 :         hw = ctx->width/2;
     486          29 :         if (ctx->width %2) hw++;
     487          29 :         hh = ctx->height/2;
     488          29 :         if (ctx->height %2) hh++;
     489             : 
     490          29 :         ctx->is_yuv = GF_FALSE;
     491          29 :         switch (ctx->pfmt) {
     492           1 :         case GF_PIXEL_YUV444_10:
     493           1 :                 ctx->bit_depth = 10;
     494           2 :         case GF_PIXEL_YUV444:
     495             :         case GF_PIXEL_YUVA444:
     496           2 :                 ctx->uv_w = ctx->width;
     497           2 :                 ctx->uv_h = ctx->height;
     498           2 :                 ctx->uv_stride = ctx->stride;
     499           2 :                 ctx->is_yuv = GF_TRUE;
     500           2 :                 if (ctx->pfmt==GF_PIXEL_YUVA444) {
     501           0 :                         ctx->has_alpha = GF_TRUE;
     502             :                         //ctx->pfmt = GF_PIXEL_YUV444;
     503             :                 }
     504             :                 break;
     505           1 :         case GF_PIXEL_YUV422_10:
     506           1 :                 ctx->bit_depth = 10;
     507           2 :         case GF_PIXEL_YUV422:
     508           2 :                 ctx->uv_w = ctx->width/2;
     509           2 :                 ctx->uv_h = ctx->height;
     510           2 :                 ctx->uv_stride = ctx->stride/2;
     511           2 :                 ctx->is_yuv = GF_TRUE;
     512           2 :                 break;
     513           1 :         case GF_PIXEL_YUV_10:
     514           1 :                 ctx->bit_depth = 10;
     515          10 :         case GF_PIXEL_YUV:
     516             :         case GF_PIXEL_YVU:
     517          10 :                 ctx->uv_w = ctx->width/2;
     518          10 :                 if (ctx->width % 2) ctx->uv_w++;
     519          10 :                 ctx->uv_h = ctx->height/2;
     520          10 :                 if (ctx->height % 2) ctx->uv_h++;
     521          10 :                 if (!ctx->uv_stride) {
     522           0 :                         ctx->uv_stride = ctx->stride/2;
     523           0 :                         if (ctx->stride%2) ctx->uv_stride ++;
     524             :                 }
     525          10 :                 ctx->is_yuv = GF_TRUE;
     526          10 :                 break;
     527           0 :         case GF_PIXEL_NV12_10:
     528             :         case GF_PIXEL_NV21_10:
     529           0 :                 ctx->bit_depth = 10;
     530           2 :         case GF_PIXEL_NV12:
     531             :         case GF_PIXEL_NV21:
     532           2 :                 ctx->uv_w = ctx->width/2;
     533           2 :                 ctx->uv_h = ctx->height/2;
     534           2 :                 ctx->uv_stride = ctx->stride;
     535           2 :                 ctx->is_yuv = GF_TRUE;
     536           2 :                 break;
     537           0 :         case GF_PIXEL_UYVY_10:
     538             :         case GF_PIXEL_YUYV_10:
     539             :         case GF_PIXEL_YVYU_10:
     540             :         case GF_PIXEL_VYUY_10:
     541           0 :                 ctx->bit_depth = 10;
     542           4 :         case GF_PIXEL_UYVY:
     543             :         case GF_PIXEL_YUYV:
     544             :         case GF_PIXEL_YVYU:
     545             :         case GF_PIXEL_VYUY:
     546           4 :                 ctx->uv_w = ctx->width/2;
     547           4 :                 ctx->uv_h = ctx->height;
     548           4 :                 if (!ctx->uv_stride) {
     549           4 :                         ctx->uv_stride = ctx->stride/2;
     550           4 :                         if (ctx->stride%2) ctx->uv_stride ++;
     551             :                 }
     552           4 :                 ctx->is_yuv = GF_TRUE;
     553           4 :                 break;
     554           0 :         case GF_PIXEL_YUV444_PACK:
     555             :         case GF_PIXEL_YUVA444_PACK:
     556             :         case GF_PIXEL_YUV444_10_PACK:
     557           0 :                 ctx->uv_w = ctx->width;
     558           0 :                 ctx->uv_h = ctx->height;
     559           0 :                 ctx->uv_stride = ctx->stride;
     560           0 :                 ctx->is_yuv = GF_TRUE;
     561           0 :                 if (ctx->pfmt==GF_PIXEL_YUVA444_PACK) {
     562           0 :                         ctx->has_alpha = GF_TRUE;
     563             :                         //ctx->pfmt = GF_PIXEL_YUV444;
     564             :                 }
     565             :                 break;
     566             : 
     567           0 :         case GF_PIXEL_ALPHAGREY:
     568             :         case GF_PIXEL_GREYALPHA:
     569             :         case GF_PIXEL_BGRA:
     570             :         case GF_PIXEL_ARGB:
     571             :         case GF_PIXEL_ABGR:
     572             :         case GF_PIXEL_RGBA:
     573           0 :                 ctx->has_alpha = GF_TRUE;
     574           0 :                 break;
     575             :         case GF_PIXEL_GREYSCALE:
     576             :         case GF_PIXEL_RGB:
     577             :         case GF_PIXEL_BGR:
     578             :         case GF_PIXEL_BGRX:
     579             :         case GF_PIXEL_XRGB:
     580             :         case GF_PIXEL_XBGR:
     581             :         case GF_PIXEL_RGBX:
     582             :         case GF_PIXEL_RGB_444:
     583             :         case GF_PIXEL_RGB_555:
     584             :         case GF_PIXEL_RGB_565:
     585             :                 break;
     586             : 
     587             :         case 0:
     588             :                 //not yet set, happens with some decoders/stream settings - wait until first frame is available and PF is known
     589             :                 return GF_OK;
     590           0 :         default:
     591           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[VideoOut] Pixel format %s unknown\n", gf_4cc_to_str(ctx->pfmt)));
     592             :                 return GF_OK;
     593             :         }
     594             : 
     595             : #ifdef VOUT_USE_OPENGL
     596          29 :         if (ctx->disp<MODE_2D) {
     597             :                 memset(&evt, 0, sizeof(GF_Event));
     598          25 :                 evt.type = GF_EVENT_SET_GL;
     599          25 :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     600             :         }
     601             :         
     602          29 :         vout_make_gl_current(ctx);
     603          29 :         DEL_SHADER(ctx->vertex_shader);
     604          29 :         DEL_SHADER(ctx->fragment_shader);
     605          29 :         DEL_PROGRAM(ctx->glsl_program );
     606          29 :         gf_gl_txw_reset(&ctx->tx);
     607             : 
     608          29 :         if (ctx->disp<MODE_2D) {
     609          25 :                 char *frag_shader_src = NULL;
     610             :                 GF_Matrix mx;
     611             :                 Float ft_hw, ft_hh;
     612             : 
     613          25 :                 ctx->glsl_program = glCreateProgram();
     614          25 :                 ctx->vertex_shader = glCreateShader(GL_VERTEX_SHADER);
     615          25 :                 vout_compile_shader(ctx->vertex_shader, "vertex", default_glsl_vertex);
     616             : 
     617          25 :                 ctx->fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
     618          25 :                 gf_gl_txw_setup(&ctx->tx, ctx->pfmt, ctx->width, ctx->height, ctx->stride, ctx->uv_stride, ctx->linear, NULL, ctx->full_range, ctx->cmx);
     619             : 
     620          25 :                 gf_dynstrcat(&frag_shader_src, "#version 120\n", NULL);
     621             : 
     622          25 :                 gf_gl_txw_insert_fragment_shader(ctx->tx.pix_fmt, "maintx", &frag_shader_src);
     623          25 :                 gf_dynstrcat(&frag_shader_src, "varying vec2 TexCoord;\n"
     624             :                                                                                 "void main(void) {\n"
     625             :                                                                                 "gl_FragColor = maintx_sample(TexCoord.st);\n"
     626             :                                                                                 "}"
     627             :                                                                 , NULL);
     628          25 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] Fragment shader is %s\n", frag_shader_src));
     629             : 
     630          25 :                 vout_compile_shader(ctx->fragment_shader, "fragment", frag_shader_src);
     631          25 :                 gf_free(frag_shader_src);
     632             : 
     633          25 :                 glAttachShader(ctx->glsl_program, ctx->vertex_shader);
     634          25 :                 glAttachShader(ctx->glsl_program, ctx->fragment_shader);
     635          25 :                 glLinkProgram(ctx->glsl_program);
     636             : 
     637             : #ifdef WIN32
     638             :                 if (glMapBuffer == NULL) {
     639             :                         if (ctx->disp == MODE_GL_PBO) {
     640             :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[VideoOut] GL PixelBufferObject extensions not found, fallback to regular GL\n"));
     641             :                                 ctx->disp = MODE_GL;
     642             :                         }
     643             :                 }
     644             : #endif
     645             : 
     646          25 :                 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
     647          25 :                 glViewport(0, 0, ctx->width, ctx->height);
     648             : 
     649          25 :                 gf_mx_init(mx);
     650          25 :                 ft_hw = ((Float)ctx->width)/2;
     651          25 :                 ft_hh = ((Float)ctx->height)/2;
     652          25 :                 gf_mx_ortho(&mx, -ft_hw, ft_hw, -ft_hh, ft_hh, 50, -50);
     653          25 :                 glMatrixMode(GL_PROJECTION);
     654          25 :                 glLoadMatrixf(mx.m);
     655             : 
     656          25 :                 glMatrixMode(GL_MODELVIEW);
     657          25 :                 glLoadIdentity();
     658             : 
     659          25 :                 glClear(GL_DEPTH_BUFFER_BIT);
     660          25 :                 glDisable(GL_NORMALIZE);
     661          25 :                 glDisable(GL_DEPTH_TEST);
     662          25 :                 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
     663          25 :                 glDisable(GL_LINE_SMOOTH);
     664          25 :                 glDisable(GL_LINE_SMOOTH);
     665          25 :                 glDisable(GL_LIGHTING);
     666          25 :                 glDisable(GL_CULL_FACE);
     667          25 :                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     668          25 :                 if (ctx->has_alpha) {
     669           0 :                         glEnable(GL_BLEND);
     670             :                 } else {
     671          25 :                         glDisable(GL_BLEND);
     672             :                 }
     673             :         } else
     674             : #endif //VOUT_USE_OPENGL
     675             :         {
     676           4 :                 switch (ctx->pfmt) {
     677           0 :                 case GF_PIXEL_NV12:
     678             :                 case GF_PIXEL_NV21:
     679             :                 case GF_PIXEL_NV12_10:
     680             :                 case GF_PIXEL_NV21_10:
     681           0 :                         ctx->num_textures = 2;
     682           0 :                         break;
     683           0 :                 case GF_PIXEL_UYVY:
     684             :                 case GF_PIXEL_VYUY:
     685             :                 case GF_PIXEL_YVYU:
     686             :                 case GF_PIXEL_YUYV:
     687           0 :                         ctx->num_textures = 1;
     688           0 :                         break;
     689           4 :                 default:
     690           4 :                         if (ctx->is_yuv) {
     691           4 :                                 ctx->num_textures = 3;
     692             :                         } else {
     693           0 :                                 ctx->num_textures = 1;
     694             :                         }
     695             :                         break;
     696             :                 }
     697             :         }
     698          29 :         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[VideoOut] Reconfig input wsize %d x %d, %d textures\n", ctx->width, ctx->height, ctx->num_textures));
     699             :         return GF_OK;
     700             : }
     701             : 
     702         101 : static Bool vout_on_event(void *cbk, GF_Event *evt)
     703             : {
     704             :         GF_FilterEvent fevt;
     705             :         Bool is_down=GF_FALSE;
     706             :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) cbk;
     707             : 
     708         101 :         fevt.base.type = 0;
     709         101 :         switch (evt->type) {
     710           8 :         case GF_EVENT_SIZE:
     711           8 :                 if ((ctx->display_width != evt->size.width) || (ctx->display_height != evt->size.height)) {
     712           2 :                         ctx->display_width = evt->size.width;
     713           2 :                         ctx->display_height = evt->size.height;
     714           2 :                         ctx->display_changed = GF_TRUE;
     715           2 :                         ctx->owsize.x = ctx->display_width;
     716           2 :                         ctx->owsize.y = ctx->display_height;
     717             :                         vout_reset_overlay(ctx);
     718             : 
     719           2 :                         if (ctx->pid) {
     720           2 :                                 GF_FEVT_INIT(fevt, GF_FEVT_VISIBILITY_HINT, ctx->pid);
     721           2 :                                 fevt.visibility_hint.max_x = ctx->display_width;
     722           2 :                                 fevt.visibility_hint.max_y = ctx->display_height;
     723           2 :                                 gf_filter_pid_send_event(ctx->pid, &fevt);
     724             :                         }
     725             :                 }
     726             :                 break;
     727             :         case GF_EVENT_CLICK:
     728             :         case GF_EVENT_MOUSEUP:
     729             :         case GF_EVENT_MOUSEDOWN:
     730             :         case GF_EVENT_MOUSEOVER:
     731             :         case GF_EVENT_MOUSEOUT:
     732             :         case GF_EVENT_MOUSEMOVE:
     733             :         case GF_EVENT_MOUSEWHEEL:
     734             :         case GF_EVENT_LONGKEYPRESS:
     735             :         case GF_EVENT_TEXTINPUT:
     736           0 :                 GF_FEVT_INIT(fevt, GF_FEVT_USER, ctx->pid);
     737           0 :                 fevt.user_event.event = *evt;
     738           0 :                 break;
     739             : 
     740           0 :         case GF_EVENT_KEYDOWN:
     741             :                 is_down = GF_TRUE;
     742           0 :         case GF_EVENT_KEYUP:
     743           0 :                 switch (evt->key.key_code) {
     744           0 :                 case GF_KEY_SHIFT:
     745           0 :                         if (is_down) {
     746           0 :                                 ctx->key_states |= GF_KEY_MOD_SHIFT;
     747             :                         } else {
     748           0 :                                 ctx->key_states &= ~GF_KEY_MOD_SHIFT;
     749             :                         }
     750             :                         break;
     751           0 :                 case GF_KEY_CONTROL:
     752           0 :                         if (is_down) {
     753           0 :                                 ctx->key_states |= GF_KEY_MOD_CTRL;
     754             :                         } else {
     755           0 :                                 ctx->key_states &= ~GF_KEY_MOD_CTRL;
     756             :                         }
     757             :                         break;
     758           0 :                 case GF_KEY_ALT:
     759           0 :                         if (is_down) {
     760           0 :                                 ctx->key_states |= GF_KEY_MOD_ALT;
     761             :                         } else {
     762           0 :                                 ctx->key_states &= ~GF_KEY_MOD_ALT;
     763             :                         }
     764             :                         break;
     765             :                 }
     766           0 :                 evt->key.flags = ctx->key_states;
     767           0 :                 GF_FEVT_INIT(fevt, GF_FEVT_USER, ctx->pid);
     768           0 :                 fevt.user_event.event = *evt;
     769           0 :                 break;
     770             :         }
     771         101 :         if (gf_filter_ui_event(ctx->filter, evt))
     772             :                 return GF_TRUE;
     773             : 
     774         101 :         if (fevt.base.type)
     775           2 :                 gf_filter_pid_send_event(ctx->pid, &fevt);
     776             : 
     777             :          return GF_TRUE;
     778             : }
     779             : 
     780             : GF_VideoOutput *gf_filter_claim_opengl_provider(GF_Filter *filter);
     781             : Bool gf_filter_unclaim_opengl_provider(GF_Filter *filter, GF_VideoOutput *vout);
     782             : 
     783          28 : static GF_Err vout_initialize(GF_Filter *filter)
     784             : {
     785             :         const char *sOpt;
     786             :         void *os_wnd_handler, *os_disp_handler;
     787             :         u32 init_flags;
     788             :         GF_Err e;
     789          28 :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
     790             : 
     791          28 :         ctx->filter = filter;
     792             : 
     793             : #ifdef VOUT_USE_OPENGL
     794          28 :         if (ctx->disp <= MODE_GL_PBO) {
     795          24 :                 ctx->video_out = (GF_VideoOutput *) gf_filter_claim_opengl_provider(filter);
     796             :         }
     797             : #endif
     798             : 
     799          28 :         if (!ctx->video_out)
     800          27 :                 ctx->video_out = (GF_VideoOutput *) gf_module_load(GF_VIDEO_OUTPUT_INTERFACE, ctx->drv);
     801             : 
     802             : 
     803          28 :         if (ctx->dumpframes.nb_items) {
     804          19 :                 ctx->hide = GF_TRUE;
     805          19 :                 ctx->vsync = GF_FALSE;
     806             :         }
     807             : 
     808             :         /*if not init we run with a NULL audio compositor*/
     809          28 :         if (!ctx->video_out) {
     810           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] No output modules found, cannot load video output\n"));
     811             :                 return GF_IO_ERR;
     812             :         }
     813          28 :         if (!gf_opts_get_key("core", "video-output")) {
     814           0 :                 gf_opts_set_key("core", "video-output", ctx->video_out->module_name);
     815             :         }
     816             : 
     817          28 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] Setting up video module %s\n", ctx->video_out->module_name));
     818          28 :         ctx->video_out->on_event = vout_on_event;
     819          28 :         ctx->video_out->evt_cbk_hdl = ctx;
     820             : 
     821          28 :         os_wnd_handler = os_disp_handler = NULL;
     822          28 :         init_flags = 0;
     823          28 :         sOpt = gf_opts_get_key("Temp", "OSWnd");
     824          28 :         if (sOpt) sscanf(sOpt, "%p", &os_wnd_handler);
     825          28 :         sOpt = gf_opts_get_key("Temp", "OSDisp");
     826          28 :         if (sOpt) sscanf(sOpt, "%p", &os_disp_handler);
     827          28 :         sOpt = gf_opts_get_key("Temp", "InitFlags");
     828          28 :         if (sOpt) sscanf(sOpt, "%d", &init_flags);
     829             : 
     830          28 :         if (ctx->hide)
     831          19 :                 init_flags |= GF_TERM_INIT_HIDE;
     832             : 
     833          28 :         e = ctx->video_out->Setup(ctx->video_out, os_wnd_handler, os_disp_handler, init_flags);
     834          28 :         if (e!=GF_OK) {
     835           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("Failed to Setup Video Driver %s!\n", ctx->video_out->module_name));
     836           0 :                 gf_modules_close_interface((GF_BaseInterface *)ctx->video_out);
     837           0 :                 ctx->video_out = NULL;
     838           0 :                 return e;
     839             :         }
     840             : 
     841             : #ifdef VOUT_USE_OPENGL
     842          28 :         if ( !(ctx->video_out->hw_caps & GF_VIDEO_HW_OPENGL) )
     843             : #endif
     844             :         {
     845           0 :                 if (ctx->disp < MODE_2D) {
     846           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("No openGL support - using 2D rasterizer!\n", ctx->video_out->module_name));
     847           0 :                         ctx->disp = MODE_2D;
     848             :                 }
     849             :         }
     850             : 
     851             : #ifdef VOUT_USE_OPENGL
     852          28 :         if (ctx->disp <= MODE_GL_PBO) {
     853             :                 GF_Event evt;
     854             :                 memset(&evt, 0, sizeof(GF_Event));
     855          24 :                 evt.type = GF_EVENT_VIDEO_SETUP;
     856          24 :                 evt.setup.width = 320;
     857          24 :                 evt.setup.height = 240;
     858          24 :                 evt.setup.use_opengl = GF_TRUE;
     859          24 :                 evt.setup.back_buffer = 1;
     860          24 :                 evt.setup.disable_vsync = !ctx->vsync;
     861          24 :                 ctx->video_out->ProcessEvent(ctx->video_out, &evt);
     862             : 
     863          24 :                 if (evt.setup.use_opengl) {
     864          24 :                         gf_opengl_init();
     865             :                 }
     866          24 :                 gf_filter_register_opengl_provider(filter, GF_TRUE);
     867             :         }
     868             : #endif
     869             : 
     870          28 :         gf_filter_set_event_target(filter, GF_TRUE);
     871          28 :         return GF_OK;
     872             : }
     873             : 
     874          28 : static void vout_finalize(GF_Filter *filter)
     875             : {
     876          28 :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
     877             : 
     878          28 :         if (ctx->last_pck) {
     879           0 :                 gf_filter_pck_unref(ctx->last_pck);
     880           0 :                 ctx->last_pck = NULL;
     881             :         }
     882             : 
     883          28 :         if ((ctx->nb_frames==1) || (ctx->hold<0)) {
     884           0 :                 u32 holdms = (u32) (((ctx->hold>0) ? ctx->hold : -ctx->hold) * 1000);
     885           0 :                 gf_sleep(holdms);
     886             :         }
     887             : 
     888             : #ifdef VOUT_USE_OPENGL
     889          28 :         DEL_SHADER(ctx->vertex_shader);
     890          28 :         DEL_SHADER(ctx->fragment_shader);
     891          28 :         DEL_PROGRAM(ctx->glsl_program );
     892          28 :         gf_gl_txw_reset(&ctx->tx);
     893             : #endif //VOUT_USE_OPENGL
     894             : 
     895             :         /*stop and shutdown*/
     896          28 :         if (ctx->video_out) {
     897          28 :                 if (! gf_filter_unclaim_opengl_provider(filter, ctx->video_out)) {
     898          27 :                         ctx->video_out->Shutdown(ctx->video_out);
     899          27 :                         gf_modules_close_interface((GF_BaseInterface *)ctx->video_out);
     900             :                 }
     901          28 :                 ctx->video_out = NULL;
     902             :         }
     903          28 :         if (ctx->dump_buffer) gf_free(ctx->dump_buffer);
     904             : 
     905          28 : }
     906             : 
     907             : #ifdef VOUT_USE_OPENGL
     908             : 
     909           0 : static void vout_draw_overlay(GF_VideoOutCtx *ctx)
     910             : {
     911             :         Float dw, dh, ox, oy;
     912             : 
     913           0 :         dw = ((Float)ctx->olwnd.z) / 2;
     914           0 :         dh = ((Float)ctx->olwnd.w) / 2;
     915           0 :         ox = ((Float)ctx->olwnd.x);
     916           0 :         oy = ((Float)ctx->olwnd.y);
     917             : 
     918           0 :         GLfloat squareVertices[] = {
     919           0 :                 ox+dw, oy+dh,
     920           0 :                 ox+dw, oy-dh,
     921           0 :                 ox-dw, oy-dh,
     922             :                 ox-dw, oy+dh,
     923             :         };
     924             : 
     925           0 :         GLfloat textureVertices[] = {
     926             :                 1.0f, 0.0f,
     927             :                 1.0f, 1.0f,
     928             :                 0.0f,  1.0f,
     929             :                 0.0f,  0.0f,
     930             :         };
     931             : 
     932             : #if 0
     933             :         if (flip_texture) {
     934             :                 textureVertices[1] = textureVertices[7] = 1.0f;
     935             :                 textureVertices[3] = textureVertices[5] = 0.0f;
     936             :         }
     937             : #endif
     938             : 
     939           0 :         u16 indices[4] = {0, 1, 2, 3};
     940             : 
     941           0 :         glEnable(GL_TEXTURE_2D);
     942           0 :         glEnable(GL_BLEND);
     943           0 :         glBindTexture(GL_TEXTURE_2D, ctx->overlay_tx);
     944             : 
     945           0 :         glEnableClientState(GL_VERTEX_ARRAY);
     946           0 :         glVertexPointer(2, GL_FLOAT, 0, squareVertices);
     947           0 :         glClientActiveTexture(GL_TEXTURE0);
     948           0 :         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     949           0 :         glTexCoordPointer(2, GL_FLOAT, 0, textureVertices);
     950             : 
     951           0 :         glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, indices);
     952           0 :         glDisableClientState(GL_VERTEX_ARRAY);
     953           0 :         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     954             : 
     955           0 :         glActiveTexture(GL_TEXTURE0);
     956           0 :         glBindTexture(TEXTURE_TYPE, 0);
     957           0 :         glDisable(TEXTURE_TYPE);
     958           0 : }
     959             : 
     960         128 : static void vout_draw_gl_quad(GF_VideoOutCtx *ctx, Bool flip_texture)
     961             : {
     962             :         Float dw, dh;
     963             :         Bool flip_h = GF_FALSE;
     964             :         Bool flip_v = GF_FALSE;
     965             :         u32 i;
     966             : 
     967         128 :         gf_gl_txw_bind(&ctx->tx, "maintx", ctx->glsl_program, 0);
     968             : 
     969         128 :         dw = ((Float)ctx->dw) / 2;
     970         128 :         dh = ((Float)ctx->dh) / 2;
     971             : 
     972             : 
     973         384 :         GLfloat squareVertices[] = {
     974             :                 dw, dh,
     975         128 :                 dw, -dh,
     976         128 :                 -dw,  -dh,
     977             :                 -dw,  dh,
     978             :         };
     979             : 
     980         128 :         GLfloat textureVertices[] = {
     981             :                 1.0f, 0.0f,
     982             :                 1.0f, 1.0f,
     983             :                 0.0f,  1.0f,
     984             :                 0.0f,  0.0f,
     985             :         };
     986             : 
     987         128 :         if (flip_texture) {
     988          61 :                 textureVertices[1] = textureVertices[7] = 1.0f;
     989          61 :                 textureVertices[3] = textureVertices[5] = 0.0f;
     990             :         }
     991             : 
     992         128 :         switch (ctx->vflip) {
     993             :         case FLIP_VERT:
     994             :                 flip_v = GF_TRUE;
     995             :                 break;
     996             :         case FLIP_HORIZ:
     997             :                 flip_h = GF_TRUE;
     998             :                 break;
     999             :         case FLIP_BOTH:
    1000             :         case FLIP_BOTH2:
    1001             :                 flip_v = GF_TRUE;
    1002             :                 flip_h = GF_TRUE;
    1003             :                 break;
    1004             :         }
    1005             : 
    1006             :         if (flip_h) {
    1007             :                 GLfloat v = textureVertices[0];
    1008           0 :                 textureVertices[0] = textureVertices[2] = textureVertices[4];
    1009           0 :                 textureVertices[4] = textureVertices[6] = v;
    1010             :         }
    1011         128 :         if (flip_v) {
    1012           0 :                 GLfloat v = textureVertices[1];
    1013           0 :                 textureVertices[1] = textureVertices[7] = textureVertices[3];
    1014           0 :                 textureVertices[3] = textureVertices[5] = v;
    1015             :         }
    1016             : 
    1017           0 :         for (i=0; i < ctx->vrot; i++)  {
    1018           0 :                 GLfloat vx = textureVertices[0];
    1019           0 :                 GLfloat vy = textureVertices[1];
    1020             : 
    1021           0 :                 textureVertices[0] = textureVertices[2];
    1022           0 :                 textureVertices[1] = textureVertices[3];
    1023           0 :                 textureVertices[2] = textureVertices[4];
    1024           0 :                 textureVertices[3] = textureVertices[5];
    1025           0 :                 textureVertices[4] = textureVertices[6];
    1026           0 :                 textureVertices[5] = textureVertices[7];
    1027           0 :                 textureVertices[6] = vx;
    1028           0 :                 textureVertices[7] = vy;
    1029             :         }
    1030             : 
    1031         128 :         int loc = glGetAttribLocation(ctx->glsl_program, "gfVertex");
    1032         128 :         if (loc >= 0) {
    1033         128 :                 glVertexAttribPointer(loc, 2, GL_FLOAT, 0, 0, squareVertices);
    1034         128 :                 glEnableVertexAttribArray(loc);
    1035             : 
    1036             :                 //setup texcoord location
    1037         128 :                 loc = glGetAttribLocation(ctx->glsl_program, "gfTexCoord");
    1038         128 :                 if (loc >= 0) {
    1039         128 :                         glVertexAttribPointer(loc, 2, GL_FLOAT, 0, 0, textureVertices);
    1040         128 :                         glEnableVertexAttribArray(loc);
    1041             : 
    1042         128 :                         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    1043             :                 } else {
    1044           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Failed to gfTexCoord uniform in shader\n"));
    1045             :                 }
    1046             :         } else {
    1047           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Failed to gfVertex uniform in shader\n"));
    1048             :         }
    1049             : 
    1050         128 :         glActiveTexture(GL_TEXTURE0);
    1051         128 :         glBindTexture(TEXTURE_TYPE, 0);
    1052         128 :         glDisable(TEXTURE_TYPE);
    1053             : 
    1054         128 :         glUseProgram(0);
    1055             : 
    1056         128 :         if (ctx->dump_f_idx) {
    1057             :                 FILE *fout;
    1058             :                 char szFileName[1024];
    1059          19 :                 if (strchr(gf_file_basename(ctx->out), '.')) {
    1060          19 :                         snprintf(szFileName, 1024, "%s", ctx->out);
    1061             :                 } else {
    1062           0 :                         snprintf(szFileName, 1024, "%s_%d.rgb", ctx->out, ctx->dump_f_idx);
    1063             :                 }
    1064          19 :                 if (!ctx->dump_buffer)
    1065          19 :                         ctx->dump_buffer = gf_malloc(sizeof(char)*ctx->display_width*ctx->display_height*3);
    1066             : 
    1067          19 :                 glFlush();
    1068             : 
    1069          19 :                 glReadPixels(0, 0, ctx->display_width, ctx->display_height, GL_RGB, GL_UNSIGNED_BYTE, ctx->dump_buffer);
    1070             :                 GL_CHECK_ERR()
    1071          19 :                 fout = gf_fopen(szFileName, "wb");
    1072          19 :                 if (fout) {
    1073          19 :                         gf_fwrite(ctx->dump_buffer, ctx->display_width*ctx->display_height*3, fout);
    1074          19 :                         gf_fclose(fout);
    1075             :                 } else {
    1076           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error writing frame %d buffer to %s\n", ctx->nb_frames, szFileName));
    1077             :                 }
    1078             :         }
    1079         128 : }
    1080             : 
    1081          61 : static void vout_draw_gl_hw_textures(GF_VideoOutCtx *ctx, GF_FilterFrameInterface *hwf)
    1082             : {
    1083          61 :         if (ctx->tx.internal_textures) {
    1084           1 :                 glDeleteTextures(ctx->tx.nb_textures, ctx->tx.textures);
    1085           1 :                 ctx->tx.internal_textures = GF_FALSE;
    1086             :         }
    1087          61 :         ctx->tx.frame_ifce = hwf;
    1088          61 :         gf_gl_txw_bind(&ctx->tx, "maintx", ctx->glsl_program, 0);
    1089             : 
    1090          61 :         glMatrixMode(GL_TEXTURE);
    1091          61 :         glLoadIdentity();
    1092          61 :         glMatrixMode(GL_MODELVIEW);
    1093          61 :         glLoadIdentity();
    1094             : 
    1095             :         //and draw
    1096          61 :         vout_draw_gl_quad(ctx, ctx->tx.flip ? GF_TRUE : GF_FALSE);
    1097             :         GL_CHECK_ERR()
    1098          61 : }
    1099             : 
    1100         128 : static void vout_draw_gl(GF_VideoOutCtx *ctx, GF_FilterPacket *pck)
    1101             : {
    1102             :         char *data;
    1103             :         GF_Matrix mx;
    1104             :         Float hw, hh;
    1105             :         u32 wsize;
    1106             :         GF_FilterFrameInterface *frame_ifce;
    1107             : 
    1108         128 :         vout_make_gl_current(ctx);
    1109             : 
    1110         128 :         if (ctx->display_changed) {
    1111             :                 u32 v_w, v_h;
    1112          24 :                 if (ctx->vrot % 2) {
    1113           0 :                         v_h = ctx->width;
    1114           0 :                         v_w = ctx->height;
    1115             :                 } else {
    1116          24 :                         v_w = ctx->width;
    1117          24 :                         v_h = ctx->height;
    1118             :                 }
    1119             : 
    1120             :                 //if we fill width to display width and height is outside
    1121          24 :                 if (ctx->display_width * v_h / v_w > ctx->display_height) {
    1122           0 :                         ctx->dw = (Float) (ctx->display_height * v_w / v_h);
    1123           0 :                         ctx->dw *= ctx->sar.num;
    1124           0 :                         ctx->dw /= ctx->sar.den;
    1125           0 :                         ctx->dh = (Float) ctx->display_height;
    1126           0 :                         ctx->oh = (Float) 0;
    1127           0 :                         ctx->ow = (Float) (ctx->display_width - ctx->dw ) / 2;
    1128             :                 } else {
    1129          24 :                         ctx->dh = (Float) (ctx->display_width * v_h / v_w);
    1130          24 :                         ctx->dh *= ctx->sar.den;
    1131          24 :                         ctx->dh /= ctx->sar.num;
    1132          24 :                         ctx->dw = (Float) ctx->display_width;
    1133          24 :                         ctx->ow = (Float) 0;
    1134          24 :                         ctx->oh = (Float) (ctx->display_height - ctx->dh ) / 2;
    1135             :                 }
    1136          24 :                 if (ctx->dump_buffer) {
    1137           0 :                         gf_free(ctx->dump_buffer);
    1138           0 :                         ctx->dump_buffer = NULL;
    1139             :                 }
    1140             : 
    1141          24 :                 ctx->display_changed = GF_FALSE;
    1142             :         }
    1143             : 
    1144         128 :         glViewport(0, 0, ctx->display_width, ctx->display_height);
    1145             : 
    1146         128 :         gf_mx_init(mx);
    1147         128 :         hw = ((Float)ctx->display_width)/2;
    1148         128 :         hh = ((Float)ctx->display_height)/2;
    1149         128 :         gf_mx_ortho(&mx, -hw, hw, -hh, hh, 10, -5);
    1150         128 :         glMatrixMode(GL_PROJECTION);
    1151         128 :         glLoadMatrixf(mx.m);
    1152             : 
    1153         128 :         glMatrixMode(GL_TEXTURE);
    1154         128 :         glLoadIdentity();
    1155             : 
    1156         128 :         glMatrixMode(GL_MODELVIEW);
    1157         128 :         glLoadIdentity();
    1158             : 
    1159         128 :         if (ctx->has_alpha) {
    1160             :                 Float r, g, b;
    1161           0 :                 r = GF_COL_R(ctx->back); r /= 255;
    1162           0 :                 g = GF_COL_G(ctx->back); g /= 255;
    1163           0 :                 b = GF_COL_B(ctx->back); b /= 255;
    1164           0 :                 glClearColor(r, g, b, 1.0);
    1165             :         } else {
    1166         128 :                 glClearColor(0, 0, 0, 1.0);
    1167             :         }
    1168         128 :         glClear(GL_COLOR_BUFFER_BIT);
    1169             : 
    1170             :         //we are not sure if we are the only ones using the gl context, reset our defaults
    1171         128 :         glDisable(GL_LIGHTING);
    1172         128 :         glDisable(GL_CULL_FACE);
    1173         128 :         glDisable(GL_DEPTH_TEST);
    1174         128 :         if (ctx->has_alpha) {
    1175           0 :                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    1176           0 :                 glEnable(GL_BLEND);
    1177             :         } else {
    1178         128 :                 glDisable(GL_BLEND);
    1179             :         }
    1180         128 :         if (ctx->has_alpha) {
    1181             :                 Float r, g, b;
    1182           0 :                 r = GF_COL_R(ctx->back); r /= 255;
    1183           0 :                 g = GF_COL_G(ctx->back); g /= 255;
    1184           0 :                 b = GF_COL_B(ctx->back); b /= 255;
    1185           0 :                 glClearColor(r, g, b, 1.0);
    1186           0 :                 glClear(GL_COLOR_BUFFER_BIT);
    1187             :         }
    1188             : 
    1189         128 :         if (!pck)
    1190             :                 goto exit;
    1191             : 
    1192         128 :         if (!ctx->glsl_program) return;
    1193             : 
    1194         128 :         frame_ifce = gf_filter_pck_get_frame_interface(pck);
    1195         128 :         if (frame_ifce && (frame_ifce->flags & GF_FRAME_IFCE_MAIN_GLFB)) {
    1196             :                 goto exit;
    1197             :         }
    1198             : 
    1199         128 :         glUseProgram(ctx->glsl_program);
    1200             : 
    1201             : 
    1202         128 :         if (frame_ifce && frame_ifce->get_gl_texture) {
    1203          61 :                 vout_draw_gl_hw_textures(ctx, frame_ifce);
    1204             :         } else {
    1205          67 :                 data = (char*) gf_filter_pck_get_data(pck, &wsize);
    1206             : 
    1207             :                 //upload texture
    1208          67 :                 gf_gl_txw_upload(&ctx->tx, data, frame_ifce);
    1209             : 
    1210             :                 //and draw
    1211          67 :                 vout_draw_gl_quad(ctx, GF_FALSE);
    1212             :         }
    1213             : 
    1214         128 : exit:
    1215             : 
    1216             :         //we don't lock since most of the time overlay is not set
    1217         128 :         if (ctx->oldata.ptr) {
    1218             :                 // overlay is set, lock filter to make sure the data is still valid
    1219           0 :                 gf_filter_lock(ctx->filter, GF_TRUE);
    1220           0 :                 if (ctx->oldata.ptr && (ctx->oldata.size <= 4 * ctx->olsize.x * ctx->olsize.y) ) {
    1221           0 :                         if (!ctx->overlay_tx) {
    1222           0 :                                 glGenTextures(1, &ctx->overlay_tx);
    1223             : 
    1224           0 :                                 glEnable(GL_TEXTURE_2D);
    1225             : #if !defined(GPAC_USE_GLES1X)
    1226           0 :                                 glBindTexture(GL_TEXTURE_2D, ctx->overlay_tx);
    1227             : #if defined(GPAC_USE_GLES2)
    1228             :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    1229             :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    1230             : #else
    1231           0 :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    1232           0 :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    1233             : #endif
    1234           0 :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    1235           0 :                                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    1236             : #endif
    1237             : 
    1238           0 :                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ctx->olsize.x, ctx->olsize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, ctx->oldata.ptr);
    1239           0 :                                 ctx->update_oldata = GF_FALSE;
    1240           0 :                         } else if (ctx->update_oldata) {
    1241           0 :                                 glEnable(GL_TEXTURE_2D);
    1242           0 :                                 glBindTexture(GL_TEXTURE_2D, ctx->overlay_tx);
    1243           0 :                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ctx->olsize.x, ctx->olsize.y, GL_RGBA, GL_UNSIGNED_BYTE, ctx->oldata.ptr);
    1244           0 :                                 ctx->update_oldata = GF_FALSE;
    1245             :                         }
    1246           0 :                         vout_draw_overlay(ctx);
    1247             :                 }
    1248           0 :                 gf_filter_lock(ctx->filter, GF_FALSE);
    1249             :         }
    1250             : 
    1251             :         //final flush
    1252         128 :         ctx->video_out->Flush(ctx->video_out, NULL);
    1253             : }
    1254             : #endif
    1255             : 
    1256          48 : void vout_draw_2d(GF_VideoOutCtx *ctx, GF_FilterPacket *pck)
    1257             : {
    1258             :         char *data;
    1259             :         u32 wsize;
    1260             :         GF_Err e;
    1261             :         GF_VideoSurface src_surf;
    1262             :         GF_Window dst_wnd, src_wnd;
    1263             : 
    1264          48 :         if (!pck)
    1265           0 :                 return;
    1266             : 
    1267             :         memset(&src_surf, 0, sizeof(GF_VideoSurface));
    1268          48 :         src_surf.width = ctx->width;
    1269          48 :         src_surf.height = ctx->height;
    1270             :         src_surf.pitch_x = 0;
    1271          48 :         src_surf.pitch_y = ctx->stride;
    1272          48 :         src_surf.pixel_format = ctx->pfmt;
    1273          48 :         src_surf.global_alpha = 0xFF;
    1274             : 
    1275          48 :         data = (char *) gf_filter_pck_get_data(pck, &wsize);
    1276          48 :         if (!data) {
    1277             :                 u32 stride_luma;
    1278             :                 u32 stride_chroma;
    1279           0 :                 GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
    1280           0 :                 if (frame_ifce->flags & GF_FRAME_IFCE_BLOCKING)
    1281           0 :                         ctx->force_release = GF_TRUE;
    1282           0 :                 if (! frame_ifce->get_plane) {
    1283           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Hardware GL texture blit not supported with non-GL blitter\n"));
    1284           0 :                         return;
    1285             :                 }
    1286           0 :                 e = frame_ifce->get_plane(frame_ifce, 0, (const u8 **) &src_surf.video_buffer, &stride_luma);
    1287           0 :                 if (e) {
    1288           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error fetching chroma plane from hardware frame\n"));
    1289             :                         return;
    1290             :                 }
    1291           0 :                 if (ctx->is_yuv && (ctx->num_textures>1)) {
    1292           0 :                         e = frame_ifce->get_plane(frame_ifce, 1, (const u8 **) &src_surf.u_ptr, &stride_chroma);
    1293           0 :                         if (e) {
    1294           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error fetching luma U plane from hardware frame\n"));
    1295             :                                 return;
    1296             :                         }
    1297           0 :                         if ((ctx->pfmt!= GF_PIXEL_NV12) && (ctx->pfmt!= GF_PIXEL_NV21)) {
    1298           0 :                                 e = frame_ifce->get_plane(frame_ifce, 2, (const u8 **) &src_surf.v_ptr, &stride_chroma);
    1299           0 :                                 if (e) {
    1300           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error fetching luma V plane from hardware frame\n"));
    1301             :                                         return;
    1302             :                                 }
    1303             :                         }
    1304             :                 }
    1305             :         } else {
    1306          48 :                 src_surf.video_buffer = data;
    1307             :         }
    1308             : 
    1309             : 
    1310          48 :         if (ctx->display_changed) {
    1311             :                 GF_Event evt;
    1312             : 
    1313             :                 memset(&evt, 0, sizeof(GF_Event));
    1314           4 :                 evt.type = GF_EVENT_VIDEO_SETUP;
    1315           4 :                 evt.setup.back_buffer = 1;
    1316           4 :                 evt.setup.width = ctx->display_width;
    1317           4 :                 evt.setup.height = ctx->display_height;
    1318           4 :                 e = ctx->video_out->ProcessEvent(ctx->video_out, &evt);
    1319           4 :                 if (e) {
    1320           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error resizing 2D backbuffer %s\n", gf_error_to_string(e) ));
    1321             :                 }
    1322             :                 //if we fill width to display width and height is outside
    1323           4 :                 if (ctx->display_width * ctx->height / ctx->width > ctx->display_height) {
    1324           0 :                         ctx->dw = (Float) (ctx->display_height * ctx->width * ctx->sar.num / ctx->height / ctx->sar.den);
    1325           0 :                         ctx->dh = (Float) ctx->display_height;
    1326           0 :                         ctx->oh = (Float) 0;
    1327           0 :                         ctx->ow = (Float) (ctx->display_width - ctx->dw ) / 2;
    1328             :                 } else {
    1329           4 :                         ctx->dh = (Float) (ctx->display_width * ctx->height *ctx->sar.den / ctx->width / ctx->sar.num);
    1330           4 :                         ctx->dw = (Float) ctx->display_width;
    1331           4 :                         ctx->ow = (Float) 0;
    1332           4 :                         ctx->oh = (Float) (ctx->display_height - ctx->dh ) / 2;
    1333             :                 }
    1334           4 :                 ctx->display_changed = GF_FALSE;
    1335             :         }
    1336             : 
    1337          48 :         dst_wnd.x = (u32) ctx->ow;
    1338          48 :         dst_wnd.y = (u32) ctx->oh;
    1339          48 :         dst_wnd.w = (u32) ctx->dw;
    1340          48 :         dst_wnd.h = (u32) ctx->dh;
    1341             :         e = GF_EOS;
    1342             : 
    1343          48 :         src_wnd.x = src_wnd.y = 0;
    1344          48 :         src_wnd.w = ctx->width;
    1345          48 :         src_wnd.h = ctx->height;
    1346             : 
    1347          48 :         if ((ctx->disp!=MODE_2D_SOFT) && ctx->video_out->Blit) {
    1348          12 :                 e = ctx->video_out->Blit(ctx->video_out, &src_surf, &src_wnd, &dst_wnd, 0);
    1349          12 :                 if (e) {
    1350           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error bliting surface %s - retrying in software mode\n", gf_error_to_string(e) ));
    1351             :                 }
    1352             :         }
    1353          12 :         if (e) {
    1354             :                 GF_VideoSurface backbuffer;
    1355             : 
    1356          36 :                 e = ctx->video_out->LockBackBuffer(ctx->video_out, &backbuffer, GF_TRUE);
    1357          36 :                 if (!e) {
    1358          36 :                         gf_stretch_bits(&backbuffer, &src_surf, &dst_wnd, &src_wnd, 0xFF, GF_FALSE, NULL, NULL);
    1359          36 :                         ctx->video_out->LockBackBuffer(ctx->video_out, &backbuffer, GF_FALSE);
    1360             :                 } else {
    1361           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Cannot lock back buffer - Error %s\n", gf_error_to_string(e) ));
    1362             :                 }
    1363             :         }
    1364             : 
    1365          48 :         if (e != GF_OK)
    1366             :                 return;
    1367             : 
    1368             : 
    1369          48 :         if (ctx->dump_f_idx) {
    1370             :                 char szFileName[1024];
    1371             :                 GF_VideoSurface backbuffer;
    1372             : 
    1373           0 :                 e = ctx->video_out->LockBackBuffer(ctx->video_out, &backbuffer, GF_TRUE);
    1374           0 :                 if (!e) {
    1375             :                         FILE *fout;
    1376           0 :                         const char *src_ext = strchr(gf_file_basename(ctx->out), '.');
    1377           0 :                         const char *ext = gf_pixel_fmt_sname(backbuffer.pixel_format);
    1378           0 :                         if (!ext) ext = "rgb";
    1379             : 
    1380           0 :                         if (src_ext && !strcmp(ext, src_ext+1)) {
    1381           0 :                                 snprintf(szFileName, 1024, "%s", ctx->out);
    1382             :                         } else {
    1383           0 :                                 snprintf(szFileName, 1024, "%s_%d.%s", ctx->out, ctx->dump_f_idx, ext);
    1384             :                         }
    1385           0 :                         fout = gf_fopen(szFileName, "wb");
    1386           0 :                         if (fout) {
    1387             :                                 u32 i;
    1388           0 :                                 for (i=0; i<backbuffer.height; i++) {
    1389           0 :                                         gf_fwrite(backbuffer.video_buffer + i*backbuffer.pitch_y, backbuffer.pitch_y, fout);
    1390             :                                 }
    1391           0 :                                 gf_fclose(fout);
    1392             :                         } else {
    1393             :                                 e = GF_IO_ERR;
    1394             :                         }
    1395           0 :                         ctx->video_out->LockBackBuffer(ctx->video_out, &backbuffer, GF_FALSE);
    1396             :                 }
    1397             : 
    1398           0 :                 if (e) {
    1399           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error writing frame %d buffer to %s: %s\n", ctx->nb_frames, szFileName, gf_error_to_string(e) ));
    1400             :                 }
    1401             :         }
    1402             : 
    1403          48 :         dst_wnd.x = 0;
    1404          48 :         dst_wnd.y = 0;
    1405          48 :         dst_wnd.w = ctx->display_width;
    1406          48 :         dst_wnd.h = ctx->display_height;
    1407          48 :         e = ctx->video_out->Flush(ctx->video_out, &dst_wnd);
    1408          48 :         if (e) {
    1409           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] Error flushing vido output %s%s\n", gf_error_to_string(e)));
    1410             :                 return;
    1411             :         }
    1412             : }
    1413             : 
    1414         695 : static GF_Err vout_process(GF_Filter *filter)
    1415             : {
    1416             :         GF_FilterPacket *pck;
    1417         695 :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
    1418             : 
    1419         695 :         if (ctx->force_vout) {
    1420           0 :                 ctx->force_vout = GF_FALSE;
    1421           0 :                 ctx->width = ctx->display_width = ctx->olwnd.z;
    1422           0 :                 ctx->height = ctx->display_height = ctx->olwnd.w;
    1423           0 :                 resize_video_output(ctx, ctx->width, ctx->height);
    1424           0 :                 ctx->owsize.x = ctx->display_width;
    1425           0 :                 ctx->owsize.y = ctx->display_height;
    1426             :         }
    1427         695 :         if (ctx->force_reconfig_pid) {
    1428           0 :                 vout_configure_pid(filter, ctx->pid, GF_FALSE);
    1429           0 :                 ctx->force_reconfig_pid = GF_FALSE;
    1430             :         }
    1431         695 :         ctx->video_out->ProcessEvent(ctx->video_out, NULL);
    1432             : 
    1433         695 :         if (!ctx->step && !ctx->speed) {
    1434             :                 //we don't lock here since we don't access the pointer
    1435           0 :                 if (ctx->oldata.ptr && ctx->update_oldata)
    1436           0 :                         return vout_draw_frame(ctx);
    1437           0 :                 gf_filter_ask_rt_reschedule(filter, 50000);
    1438           0 :                 return GF_OK;
    1439             :         }
    1440             : 
    1441         695 :         if (ctx->do_seek) {
    1442             :                 GF_FilterEvent evt;
    1443           0 :                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
    1444           0 :                 gf_filter_pid_send_event(ctx->pid, &evt);
    1445             : 
    1446           0 :                 gf_filter_pid_init_play_event(ctx->pid, &evt, ctx->start, ctx->speed, "VideoOut");
    1447           0 :                 gf_filter_pid_send_event(ctx->pid, &evt);
    1448           0 :                 ctx->do_seek = GF_FALSE;
    1449           0 :                 if (ctx->last_pck) {
    1450           0 :                         gf_filter_pck_unref(ctx->last_pck);
    1451           0 :                         ctx->last_pck = NULL;
    1452             :                 }
    1453           0 :                 ctx->nb_frames = 0;
    1454           0 :                 ctx->buffer_done = GF_FALSE;
    1455           0 :                 ctx->no_buffering = 0;
    1456           0 :                 ctx->first_cts_plus_one = 0;
    1457           0 :                 ctx->clock_at_first_cts = ctx->last_frame_clock = ctx->clock_at_first_frame = 0;
    1458             :                 return GF_OK;
    1459             :         }
    1460             : 
    1461         695 :         if (!ctx->pid) {
    1462             :                 //we don't lock here since we don't access the pointer
    1463           0 :                 if (ctx->oldata.ptr && ctx->update_oldata)
    1464           0 :                         return vout_draw_frame(ctx);
    1465           0 :                 return ctx->oldata.ptr ? GF_OK : GF_EOS;
    1466             :         }
    1467             : 
    1468         695 :         pck = gf_filter_pid_get_packet(ctx->pid);
    1469         695 :         if (!pck) {
    1470          19 :                 if (gf_filter_pid_is_eos(ctx->pid)) {
    1471          11 :                         if (!ctx->aborted) {
    1472             :                                 GF_FilterEvent evt;
    1473           9 :                                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
    1474           9 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
    1475             : 
    1476           9 :                                 ctx->aborted = GF_TRUE;
    1477             :                         }
    1478             :                 }
    1479          19 :                 if (ctx->aborted) {
    1480          19 :                         if (ctx->last_pck) {
    1481           8 :                                 gf_filter_pck_unref(ctx->last_pck);
    1482           8 :                                 ctx->last_pck = NULL;
    1483             :                         }
    1484          19 :                         if ((ctx->nb_frames>1) && ctx->last_pck_dur_us) {
    1485           9 :                                 gf_filter_ask_rt_reschedule(filter, ctx->last_pck_dur_us);
    1486           9 :                                 ctx->last_pck_dur_us = 0;
    1487             :                                 //we don't lock here since we don't access the pointer
    1488           9 :                                 if (ctx->oldata.ptr && ctx->update_oldata)
    1489           0 :                                         return vout_draw_frame(ctx);
    1490             :                                 return GF_OK;
    1491             :                         }
    1492           0 :                 } else if (ctx->rbuffer && ctx->buffer_done) {
    1493           0 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[VideoOut] buffer empty, rebuffering\n"));
    1494           0 :                         ctx->rebuffer = gf_sys_clock_high_res();
    1495           0 :                         ctx->buffer_done = GF_FALSE;
    1496             :                 }
    1497             : 
    1498             :                 //check if all sinks are done - if not keep requesting a process to pump window event loop
    1499          10 :                 if (!gf_filter_all_sinks_done(filter)) {
    1500           0 :                         gf_filter_ask_rt_reschedule(filter, 100000);
    1501           0 :                         if (ctx->display_changed)
    1502             :                                 goto draw_frame;
    1503             : 
    1504             :                         //we don't lock here since we don't access the pointer
    1505           0 :                         if (ctx->oldata.ptr && ctx->update_oldata)
    1506           0 :                                 return vout_draw_frame(ctx);
    1507             :                         return GF_OK;
    1508             :                 }
    1509          10 :                 return ctx->aborted ? GF_EOS : GF_OK;
    1510             :         }
    1511             :         
    1512         676 :         if (!ctx->width || !ctx->height) {
    1513           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[VideoOut] pid display wsize unknown, discarding packet\n"));
    1514           0 :                 gf_filter_pid_drop_packet(ctx->pid);
    1515           0 :                 return GF_OK;
    1516             :         }
    1517             : 
    1518         676 :         if (ctx->dumpframes.nb_items) {
    1519             :                 u32 i;
    1520             : 
    1521         380 :                 ctx->nb_frames++;
    1522         380 :                 ctx->dump_f_idx = 0;
    1523             : 
    1524         380 :                 for (i=0; i<ctx->dumpframes.nb_items; i++) {
    1525         380 :                         if (ctx->dumpframes.vals[i] == 0) {
    1526           0 :                                 ctx->dump_f_idx = ctx->nb_frames;
    1527           0 :                                 break;
    1528             :                         }
    1529         380 :                         if (ctx->dumpframes.vals[i] == ctx->nb_frames) {
    1530          19 :                                 ctx->dump_f_idx = ctx->dumpframes.vals[i];
    1531          19 :                                 break;
    1532             :                         }
    1533         361 :                         if (ctx->dumpframes.vals[i] > ctx->nb_frames) {
    1534             :                                 break;
    1535             :                         }
    1536             :                 }
    1537             : 
    1538         380 :                 if (ctx->dump_f_idx) {
    1539          19 :                         ctx->last_pck = pck;
    1540          19 :                         vout_draw_frame(ctx);
    1541          19 :                         ctx->last_pck = NULL;
    1542             :                 }
    1543         380 :                 gf_filter_pid_drop_packet(ctx->pid);
    1544             : 
    1545         380 :                 if (ctx->dumpframes.vals[ctx->dumpframes.nb_items-1] <= ctx->nb_frames) {
    1546          19 :                         gf_filter_pid_set_discard(ctx->pid, GF_TRUE);
    1547             :                 }
    1548             :                 return GF_OK;
    1549             :         }
    1550             : 
    1551         296 :         if (ctx->buffer) {
    1552         177 :                 if (gf_filter_pck_is_blocking_ref(pck)) {
    1553           1 :                         ctx->buffer_done = GF_TRUE;
    1554           1 :                         ctx->rebuffer = 0;
    1555           1 :                         ctx->buffer = ctx->rbuffer = 0;
    1556             :                 } else {
    1557             :                         //query full buffer duration in us
    1558         176 :                         u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
    1559             : 
    1560         176 :                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[VideoOut] buffer %d / %d ms\r", (u32) (dur/1000), ctx->buffer));
    1561         176 :                         if (!ctx->buffer_done) {
    1562           8 :                                 if ((dur < ctx->buffer * 1000) && !gf_filter_pid_has_seen_eos(ctx->pid)) {
    1563           0 :                                         gf_filter_ask_rt_reschedule(filter, 100000);
    1564             : 
    1565           0 :                                         if (gf_filter_reporting_enabled(filter)) {
    1566             :                                                 char szStatus[1024];
    1567           0 :                                                 sprintf(szStatus, "buffering %d / %d ms", (u32) (dur/1000), ctx->buffer);
    1568           0 :                                                 gf_filter_update_status(filter, -1, szStatus);
    1569             :                                         }
    1570             :                                         //we don't lock here since we don't access the pointer
    1571           0 :                                         if (ctx->oldata.ptr && ctx->update_oldata)
    1572           0 :                                                 return vout_draw_frame(ctx);
    1573             :                                         return GF_OK;
    1574             :                                 }
    1575           8 :                                 ctx->buffer_done = GF_TRUE;
    1576           8 :                                 if (ctx->rebuffer) {
    1577           0 :                                         u64 rebuf_time = gf_sys_clock_high_res() - ctx->rebuffer;
    1578           0 :                                         ctx->rebuffer = 0;
    1579           0 :                                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[VideoOut] rebuffer done in "LLU" ms\n", (u32) (rebuf_time/1000)));
    1580           0 :                                         if (ctx->clock_at_first_cts)
    1581           0 :                                                 ctx->clock_at_first_cts += rebuf_time;
    1582             :                                 }
    1583         168 :                         } else if (ctx->rbuffer) {
    1584           0 :                                 if ((dur < ctx->rbuffer * 1000) && !gf_filter_pid_has_seen_eos(ctx->pid)) {
    1585           0 :                                         GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[VideoOut] buffer %u less than min threshold %u, rebuffering\n", (u32) (dur/1000), ctx->rbuffer));
    1586           0 :                                         ctx->rebuffer = gf_sys_clock_high_res();
    1587           0 :                                         ctx->buffer_done = GF_FALSE;
    1588           0 :                                         return GF_OK;
    1589             :                                 }
    1590             :                         }
    1591             :                 }
    1592             :         }
    1593             : 
    1594             : 
    1595         453 :         if (!ctx->step && (ctx->vsync || ctx->drop)) {
    1596             :                 u64 ref_clock = 0;
    1597         296 :                 u64 cts = gf_filter_pck_get_cts(pck);
    1598         296 :                 u64 clock_us, now = gf_sys_clock_high_res();
    1599             :                 Bool check_clock = GF_FALSE;
    1600             :                 GF_Fraction64 media_ts;
    1601             :                 s64 delay;
    1602             : 
    1603         296 :                 if (ctx->dur.num && ctx->clock_at_first_cts && ctx->first_cts_plus_one) {
    1604           0 :                         if ((cts - ctx->first_cts_plus_one + 1) * ctx->dur.den > (u64) (ctx->dur.num * ctx->timescale)) {
    1605             :                                 GF_FilterEvent evt;
    1606           0 :                                 if (ctx->last_pck) {
    1607           0 :                                         gf_filter_pck_unref(ctx->last_pck);
    1608           0 :                                         ctx->last_pck = NULL;
    1609             :                                 }
    1610           0 :                                 ctx->aborted = GF_TRUE;
    1611           0 :                                 GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->pid);
    1612           0 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
    1613           0 :                                 gf_filter_pid_set_discard(ctx->pid, GF_TRUE);
    1614             :                                 return GF_EOS;
    1615           0 :                         } else if (gf_filter_pid_has_seen_eos(ctx->pid)) {
    1616           0 :                                 gf_filter_ask_rt_reschedule(filter, 100000);
    1617             :                         }
    1618             :                 }
    1619             : 
    1620         296 :                 delay = ctx->pid_delay;
    1621         296 :                 if (ctx->vdelay.den)
    1622         296 :                         delay += ctx->vdelay.num * (s32)ctx->timescale / (s32)ctx->vdelay.den;
    1623             : 
    1624         296 :                 if (delay>=0) {
    1625         296 :                         cts += delay;
    1626           0 :                 } else if (cts < (u64) (-delay) ) {
    1627           0 :                         if (ctx->vsync) {
    1628           0 :                                 if (ctx->last_pck) {
    1629           0 :                                         gf_filter_pck_unref(ctx->last_pck);
    1630           0 :                                         ctx->last_pck = NULL;
    1631             :                                 }
    1632           0 :                                 gf_filter_pid_drop_packet(ctx->pid);
    1633           0 :                                 gf_filter_ask_rt_reschedule(filter, 50000);
    1634           0 :                                 return GF_OK;
    1635             :                         }
    1636             :                         cts = 0;
    1637             :                 } else {
    1638           0 :                         cts -= (u64) -delay;
    1639             :                 }
    1640             : 
    1641             :                 //check if we have a clock hint from an audio output
    1642         296 :                 gf_filter_get_clock_hint(filter, &clock_us, &media_ts);
    1643         296 :                 if (clock_us && media_ts.den) {
    1644             :                         u32 safety;
    1645             :                         //ref frame TS in video stream timescale
    1646           0 :                         s64 ref_ts = (s64) (media_ts.num * ctx->timescale);
    1647           0 :                         ref_ts /= media_ts.den;
    1648             :                         //compute time ellapsed since last clock ref in timescale
    1649           0 :                         s64 diff = now;
    1650           0 :                         diff -= (s64) clock_us;
    1651           0 :                         if (ctx->timescale!=1000000) {
    1652           0 :                                 diff *= ctx->timescale;
    1653           0 :                                 diff /= 1000000;
    1654             :                         }
    1655             :                         assert(diff>=0);
    1656             :                         //ref stream hypothetical timestamp at now
    1657           0 :                         ref_ts += diff;
    1658           0 :                         ctx->first_cts_plus_one = cts + 1;
    1659             : 
    1660             :                         //allow 10ms video advance
    1661             :                         #define DEF_VIDEO_AUDIO_ADVANCE_MS      15
    1662           0 :                         safety = DEF_VIDEO_AUDIO_ADVANCE_MS * ctx->timescale / 1000;
    1663           0 :                         if (!ctx->step && !ctx->raw_grab && ((s64) cts > ref_ts + safety)) {
    1664           0 :                                 u32 resched_time = (u32) ((cts-ref_ts - safety) * 1000000 / ctx->timescale);
    1665           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms display frame CTS "LLU" CTS greater than reference clock CTS "LLU" (%g sec), waiting\n", gf_sys_clock(), cts, ref_ts, ((Double)media_ts.num)/media_ts.den));
    1666             :                                 //the clock is not updated continuously, only when audio sound card writes. We therefore
    1667             :                                 //cannot know if the sampling was recent or old, so ask for a short reschedule time
    1668           0 :                                 if (resched_time>100000)
    1669             :                                         resched_time = 100000;
    1670           0 :                                 gf_filter_ask_rt_reschedule(filter, resched_time );
    1671             :                                 //not ready yet
    1672           0 :                                 return GF_OK;
    1673             :                         }
    1674           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms frame CTS "LLU" latest reference clock CTS "LLU" (%g sec)\n", gf_sys_clock(), cts, ref_ts, ((Double)media_ts.num)/media_ts.den));
    1675             :                         ref_clock = ref_ts;
    1676             :                         check_clock = GF_TRUE;
    1677         296 :                 } else if (!ctx->first_cts_plus_one) {
    1678             :                         //init clock on second frame, first frame likely will have large rendering time
    1679             :                         //due to GPU config. While this is not important for recorded media, it may impact
    1680             :                         //monitoring of live source (webcams) by consuming frame really late which may
    1681             :                         //block source sampling when blocking mode is enabled
    1682             : 
    1683             :                         //store first frame TS
    1684          18 :                         if (!ctx->clock_at_first_cts) {
    1685           9 :                                 ctx->clock_at_first_cts = 1 + cts;
    1686             :                         } else {
    1687             :                                 //comute CTS diff in ms
    1688           9 :                                 u64 diff = cts - ctx->clock_at_first_cts + 1;
    1689           9 :                                 diff *= 1000;
    1690           9 :                                 diff /= ctx->timescale;
    1691             :                                 //diff less than 100ms (eg 10fps or more), init on the second frame
    1692             :                                 //do not apply this if playing at speed lower than nominal speed
    1693           9 :                                 if ((diff<100) && (ABS(ctx->speed)>=1)) {
    1694           9 :                                         ctx->first_cts_plus_one = 1 + cts;
    1695           9 :                                         ctx->clock_at_first_cts = now;
    1696             :                                 }
    1697             :                                 //otherwise (low frame rate), init on the first frame
    1698             :                                 else {
    1699           0 :                                         ctx->first_cts_plus_one = ctx->clock_at_first_cts /* - 1 + 1*/;
    1700           0 :                                         ctx->clock_at_first_cts = ctx->last_frame_clock;
    1701             :                                         check_clock = GF_TRUE;
    1702             :                                 }
    1703             :                         }
    1704             :                         ref_clock = cts;
    1705             :                 } else {
    1706             :                         check_clock = GF_TRUE;
    1707             :                 }
    1708             : 
    1709             :                 if (check_clock) {
    1710             :                         s64 diff;
    1711         278 :                         if (ctx->speed>=0) {
    1712         278 :                                 if (cts < ctx->first_cts_plus_one) cts = ctx->first_cts_plus_one;
    1713         278 :                                 diff = (s64) ((now - ctx->clock_at_first_cts) * ctx->speed);
    1714             : 
    1715         278 :                                 if (ctx->timescale != 1000000)
    1716         278 :                                         diff -= (s64) ( (cts - ctx->first_cts_plus_one + 1) * 1000000  / ctx->timescale);
    1717             :                                 else
    1718           0 :                                         diff -= (s64) (cts - ctx->first_cts_plus_one + 1);
    1719             : 
    1720             :                         } else {
    1721           0 :                                 diff = (s64) ((now - ctx->clock_at_first_cts) * -ctx->speed);
    1722             : 
    1723           0 :                                 if (ctx->timescale != 1000000)
    1724           0 :                                         diff -= (s64) ( (ctx->first_cts_plus_one-1 - cts) * 1000000  / ctx->timescale);
    1725             :                                 else
    1726           0 :                                         diff -= (s64) (ctx->first_cts_plus_one-1 - cts);
    1727             :                         }
    1728             : 
    1729         278 :                         if (!ctx->raw_grab && (diff < -2000)) {
    1730         139 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms frame cts "LLU"/%d "LLU" us too early, waiting\n", gf_sys_clock(), cts, ctx->timescale, -diff));
    1731         139 :                                 if (diff<-100000) diff = -100000;
    1732         139 :                                 gf_filter_ask_rt_reschedule(filter, (u32) (-diff));
    1733             : 
    1734         139 :                                 if (ctx->display_changed) goto draw_frame;
    1735             :                                 //not ready yet
    1736             :                                 return GF_OK;
    1737             :                         }
    1738         139 :                         if (ABS(diff)>2000) {
    1739           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms frame cts "LLU"/%d is "LLD" us too %s\n", gf_sys_clock(), cts, ctx->timescale, diff<0 ? -diff : diff, diff<0 ? "early" : "late"));
    1740             :                         } else {
    1741             :                                 diff = 0;
    1742             :                         }
    1743             : 
    1744         139 :                         if (ctx->timescale != 1000000)
    1745         139 :                                 ref_clock = diff * ctx->timescale / 1000000 + cts;
    1746             :                         else
    1747           0 :                                 ref_clock = diff + cts;
    1748             : 
    1749         139 :                         ctx->last_pck_dur_us = gf_filter_pck_get_duration(pck);
    1750         139 :                         ctx->last_pck_dur_us *= 1000000;
    1751         139 :                         ctx->last_pck_dur_us /= ctx->timescale;
    1752             :                 }
    1753             :                 //detach packet from pid, so that we can query next cts
    1754         157 :                 gf_filter_pck_ref(&pck);
    1755         157 :                 gf_filter_pid_drop_packet(ctx->pid);
    1756             : 
    1757         157 :                 if (ctx->drop) {
    1758             :                         u64 next_ts;
    1759             :                         Bool do_drop = GF_FALSE;
    1760             : 
    1761           0 :                         if (ctx->speed > 2){
    1762           0 :                                 u32 speed = (u32) ABS(ctx->speed);
    1763             : 
    1764           0 :                                 ctx->nb_drawn++;
    1765           0 :                                 if (ctx->nb_drawn % speed) {
    1766             :                                         do_drop = GF_TRUE;
    1767             :                                 } else {
    1768           0 :                                         ctx->nb_drawn = 0;
    1769             :                                 }
    1770             :                         }
    1771             :                         //peeking next packet CTS might fail if no packet in buffer, in which case we don't drop
    1772           0 :                         else if (gf_filter_pid_get_first_packet_cts(ctx->pid, &next_ts)) {
    1773           0 :                                 if (next_ts<ref_clock) {
    1774             :                                         do_drop = GF_TRUE;
    1775             :                                 }
    1776             :                         }
    1777             : 
    1778             :                         if (do_drop) {
    1779           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms drop frame "LLU" ms reference clock "LLU" ms\n", gf_sys_clock(), (1000*cts)/ctx->timescale, (1000*ref_clock)/ctx->timescale));
    1780             : 
    1781           0 :                                 gf_filter_pck_unref(pck);
    1782           0 :                                 gf_filter_ask_rt_reschedule(filter, 10);
    1783           0 :                                 return GF_OK;
    1784             :                         }
    1785             :                 }
    1786             : 
    1787         157 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms display frame cts "LLU"/%d  "LLU" ms\n", gf_sys_clock(), cts, ctx->timescale, (1000*cts)/ctx->timescale));
    1788             :         } else {
    1789           0 :                 gf_filter_pck_ref(&pck);
    1790           0 :                 gf_filter_pid_drop_packet(ctx->pid);
    1791             : 
    1792             : #ifndef GPAC_DISABLE_LOG
    1793             : #ifdef VOUT_USE_OPENGL
    1794           0 :                 u64 cts = gf_filter_pck_get_cts(pck);
    1795           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] At %d ms display frame cts "LLU"/%d  "LLU" ms\n", gf_sys_clock(), cts, ctx->timescale, (1000*cts)/ctx->timescale));
    1796             : #endif
    1797             : #endif
    1798             : 
    1799             :         }
    1800             : 
    1801         157 :         if (ctx->last_pck)
    1802          88 :                 gf_filter_pck_unref(ctx->last_pck);
    1803         157 :         ctx->last_pck = pck;
    1804         157 :         ctx->nb_frames++;
    1805             : 
    1806             : 
    1807         157 : draw_frame:
    1808             : 
    1809         157 :         if (ctx->last_pck && gf_filter_reporting_enabled(filter)) {
    1810             :                 char szStatus[1024];
    1811           0 :                 u64 dur = gf_filter_pid_query_buffer_duration(ctx->pid, GF_FALSE);
    1812             :                 Double fps = 0;
    1813           0 :                 if (ctx->clock_at_first_frame) {
    1814           0 :                         fps = ctx->nb_frames;
    1815           0 :                         fps *= 1000000;
    1816           0 :                         fps /= (1 + gf_sys_clock_high_res() - ctx->clock_at_first_cts);
    1817             :                 } else {
    1818           0 :                         ctx->clock_at_first_frame = gf_sys_clock_high_res();
    1819             :                 }
    1820           0 :                 sprintf(szStatus, "%dx%d->%dx%d %s frame TS "LLU"/%d buffer %d / %d ms %02.02f FPS", ctx->width, ctx->height, ctx->display_width, ctx->display_height,
    1821           0 :                         gf_pixel_fmt_name(ctx->pfmt), gf_filter_pck_get_cts(ctx->last_pck), ctx->timescale, (u32) (dur/1000), ctx->buffer, fps);
    1822           0 :                 gf_filter_update_status(filter, -1, szStatus);
    1823             :         }
    1824             : 
    1825         157 :         if (ctx->step) {
    1826           0 :                 ctx->speed = 0;
    1827           0 :                 ctx->step = GF_FALSE;
    1828             :         }
    1829         157 :         return vout_draw_frame(ctx);
    1830             : }
    1831             : 
    1832         176 : static GF_Err vout_draw_frame(GF_VideoOutCtx *ctx)
    1833             : {
    1834         176 :         ctx->force_release = GF_FALSE;
    1835         176 :         if ((ctx->pfmt && ctx->last_pck) || !ctx->pid) {
    1836             : #ifdef VOUT_USE_OPENGL
    1837         176 :                 if (ctx->disp < MODE_2D) {
    1838         128 :                         gf_rmt_begin_gl(vout_draw_gl);
    1839         128 :                         glGetError();
    1840         128 :                         vout_draw_gl(ctx, ctx->last_pck);
    1841         128 :                         gf_rmt_end_gl();
    1842         128 :                         glGetError();
    1843             :                 } else
    1844             : #endif
    1845             :                 {
    1846          48 :                         vout_draw_2d(ctx, ctx->last_pck);
    1847             :                 }
    1848             :         }
    1849         176 :         if (ctx->no_buffering)
    1850          19 :                 ctx->force_release = GF_TRUE;
    1851         157 :         else if (ctx->speed && ctx->last_pck && gf_filter_pck_is_blocking_ref(ctx->last_pck) )
    1852          61 :                 ctx->force_release = GF_TRUE;
    1853             : 
    1854         176 :         if (ctx->force_release && ctx->last_pck && !ctx->dump_f_idx) {
    1855          61 :                 gf_filter_pck_unref(ctx->last_pck);
    1856          61 :                 ctx->last_pck = NULL;
    1857             :         }
    1858             :         //remember the clock right after the flush (in case of vsync)
    1859         176 :         ctx->last_frame_clock = gf_sys_clock_high_res();
    1860             : 
    1861             :         //note we don't ask RT reschedule in frame duration, we take the decision on postponing the next frame in the next process call
    1862         176 :         return GF_OK;
    1863             : }
    1864             : 
    1865         388 : static Bool vout_process_event(GF_Filter *filter, const GF_FilterEvent *fevt)
    1866             : {
    1867         388 :         if (fevt->base.type==GF_FEVT_INFO_UPDATE) {
    1868         388 :                 GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
    1869         388 :                 vout_set_caption(ctx);
    1870         388 :                 return GF_TRUE;
    1871             :         }
    1872           0 :         if (!fevt->base.on_pid && (fevt->base.type==GF_FEVT_USER)) {
    1873           0 :                 GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
    1874             :                 GF_Err e;
    1875           0 :                 if (!ctx->video_out) return GF_FALSE;
    1876           0 :                 e = ctx->video_out->ProcessEvent(ctx->video_out, (GF_Event *) &fevt->user_event.event);
    1877           0 :                 if (e) return GF_FALSE;
    1878           0 :                 return GF_TRUE;
    1879             :         }
    1880             :         //cancel
    1881             :         return GF_TRUE;
    1882             : }
    1883             : 
    1884           0 : GF_Err vout_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *new_val)
    1885             : {
    1886           0 :         GF_VideoOutCtx *ctx = (GF_VideoOutCtx *) gf_filter_get_udta(filter);
    1887             : 
    1888           0 :         if (!strcmp(arg_name, "olwnd")) {
    1889           0 :                 if (!ctx->pid) {
    1890           0 :                         if ((ctx->width != new_val->value.vec4i.z) || (ctx->height != new_val->value.vec4i.w))
    1891           0 :                                 ctx->force_vout = GF_TRUE;
    1892             :                 }
    1893             :                 return GF_OK;
    1894           0 :         } else if (!strcmp(arg_name, "oldata")) {
    1895           0 :                 if (new_val->value.data.ptr) {
    1896           0 :                         ctx->update_oldata = GF_TRUE;
    1897             :                 } else {
    1898             :                         vout_reset_overlay(ctx);
    1899             :                 }
    1900           0 :                 if (!ctx->pid) {
    1901           0 :                         if (!ctx->height)
    1902           0 :                                 ctx->force_vout = GF_TRUE;
    1903           0 :                         ctx->update_oldata = GF_TRUE;
    1904           0 :                         gf_filter_post_process_task(filter);
    1905             :                 }
    1906             :                 return GF_OK;
    1907           0 :         } else if (!strcmp(arg_name, "olsize")) {
    1908             :                 vout_reset_overlay(ctx);
    1909             :                 return GF_OK;
    1910             :         }
    1911             : 
    1912           0 :         if (!strcmp(arg_name, "fullscreen")) {
    1913           0 :                 if (new_val->value.boolean) {
    1914           0 :                         u32 nw=ctx->display_width, nh=ctx->display_height;
    1915           0 :                         ctx->video_out->SetFullScreen(ctx->video_out, GF_TRUE, &nw, &nh);
    1916           0 :                         ctx->in_fullscreen = GF_TRUE;
    1917           0 :                         ctx->owsize.x = nw;
    1918           0 :                         ctx->owsize.y = nh;
    1919             :                 } else {
    1920           0 :                         u32 nw=ctx->display_width, nh=ctx->display_height;
    1921           0 :                         ctx->video_out->SetFullScreen(ctx->video_out, GF_FALSE, &nw, &nh);
    1922           0 :                         ctx->in_fullscreen = GF_FALSE;
    1923           0 :                         ctx->owsize.x = ctx->display_width;
    1924           0 :                         ctx->owsize.y = ctx->display_height;
    1925             :                 }
    1926             :                 return GF_OK;
    1927             :         }
    1928           0 :         if (!strcmp(arg_name, "vrot")) {
    1929           0 :                 if (ctx->disp<MODE_2D)
    1930           0 :                         ctx->force_reconfig_pid = GF_TRUE;
    1931             :                 return GF_OK;
    1932             :         }
    1933           0 :         if (!strcmp(arg_name, "step")) {
    1934             :                 return GF_OK;
    1935             :         }
    1936           0 :         if (!strcmp(arg_name, "start")) {
    1937           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[VideoOut] Seek request to %f\n", new_val->value.number));
    1938           0 :                 if (!ctx->pid)
    1939             :                         return GF_OK;
    1940           0 :                 ctx->do_seek = GF_TRUE;
    1941             :         }
    1942           0 :         if (!strcmp(arg_name, "speed") && ctx->pid) {
    1943             :                 GF_FilterEvent fevt;
    1944           0 :                 GF_FEVT_INIT(fevt, 0, ctx->pid);
    1945           0 :                 if (new_val->value.number) {
    1946           0 :                         if (!ctx->speed) {
    1947           0 :                                 fevt.base.type = GF_FEVT_RESUME;
    1948             :                         } else {
    1949           0 :                                 fevt.base.type = GF_FEVT_SET_SPEED;
    1950           0 :                                 fevt.play.speed = new_val->value.number;
    1951             :                         }
    1952             :                 } else {
    1953           0 :                         fevt.base.type = GF_FEVT_PAUSE;
    1954             :                 }
    1955           0 :                 gf_filter_pid_send_event(ctx->pid, &fevt);
    1956             :         }
    1957             :         //reinit clock
    1958           0 :         ctx->first_cts_plus_one = 0;
    1959           0 :         ctx->clock_at_first_cts = ctx->last_frame_clock = ctx->clock_at_first_frame = 0;
    1960           0 :         return GF_OK;
    1961             : }
    1962             : 
    1963             : 
    1964             : 
    1965             : #define OFFS(_n)        #_n, offsetof(GF_VideoOutCtx, _n)
    1966             : 
    1967             : static const GF_FilterArgs VideoOutArgs[] =
    1968             : {
    1969             :         { OFFS(drv), "video driver name", GF_PROP_NAME, NULL, NULL, 0},
    1970             :         { OFFS(vsync), "enable video screen sync", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
    1971             :         { OFFS(drop), "enable dropping late frames", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
    1972             :         { OFFS(disp), "display mode\n"
    1973             :         "- gl: OpenGL\n"
    1974             :         "- pbo: OpenGL with PBO\n"
    1975             :         "- blit: 2D hardware blit\n"
    1976             :         "- soft: software blit", GF_PROP_UINT, "gl", "gl|pbo|blit|soft", GF_FS_ARG_HINT_ADVANCED},
    1977             :         { OFFS(start), "set playback start offset. Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, GF_FS_ARG_UPDATE},
    1978             :         { OFFS(dur), "only play the specified duration", GF_PROP_FRACTION64, "0", NULL, 0},
    1979             :         { OFFS(speed), "set playback speed when vsync is on. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, GF_FS_ARG_UPDATE},
    1980             :         { OFFS(hold), "number of seconds to hold display for single-frame streams. A negative value force a hold on last frame for single or multi-frames streams", GF_PROP_DOUBLE, "1.0", NULL, 0},
    1981             :         { OFFS(linear), "use linear filtering instead of nearest pixel for GL mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
    1982             :         { OFFS(back), "back color for transparent images", GF_PROP_UINT, "0x808080", NULL, GF_FS_ARG_HINT_ADVANCED},
    1983             :         { OFFS(wsize), "default init window size. 0x0 holds the window size of the first frame. Negative values indicate video media size", GF_PROP_VEC2I, "-1x-1", NULL, GF_FS_ARG_HINT_ADVANCED},
    1984             :         { OFFS(wpos), "default position (0,0 top-left)", GF_PROP_VEC2I, "-1x-1", NULL, GF_FS_ARG_HINT_ADVANCED},
    1985             :         { OFFS(vdelay), "set delay in sec, positive value displays after audio clock", GF_PROP_FRACTION, "0", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
    1986             :         { OFFS(hide), "hide output window", GF_PROP_BOOL, "false", NULL, 0},
    1987             :         { OFFS(fullscreen), "use fullscreen", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_UPDATE},
    1988             :         { OFFS(buffer), "set playout buffer in ms", GF_PROP_UINT, "100", NULL, 0},
    1989             :         { OFFS(mbuffer), "set max buffer occupancy in ms (if less than buffer, use buffer)", GF_PROP_UINT, "0", NULL, 0},
    1990             :         { OFFS(rbuffer), "rebuffer trigger in ms (if 0 or more than buffer, disable rebuffering", GF_PROP_UINT, "0", NULL, GF_FS_ARG_UPDATE},
    1991             :         { OFFS(dumpframes), "ordered list of frames to dump, 1 being first frame - see filter help. Special value 0 means dump all frames", GF_PROP_UINT_LIST, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
    1992             :         { OFFS(out), "radical of dump frame filenames. If no extension is provided, frames are exported as $OUT_%d.PFMT", GF_PROP_STRING, "dump", NULL, GF_FS_ARG_HINT_EXPERT},
    1993             :         { OFFS(step), "step frame", GF_PROP_BOOL, "false", NULL, GF_ARG_HINT_HIDE|GF_FS_ARG_UPDATE},
    1994             : 
    1995             :         { OFFS(olwnd), "overlay window position and size", GF_PROP_VEC4I, NULL, NULL, GF_ARG_HINT_HIDE|GF_FS_ARG_UPDATE},
    1996             :         { OFFS(olsize), "overlay texture size (must be RGBA)", GF_PROP_VEC2I, NULL, NULL, GF_ARG_HINT_HIDE|GF_FS_ARG_UPDATE_SYNC},
    1997             :         { OFFS(oldata), "overlay texture data (must be RGBA)", GF_PROP_CONST_DATA, NULL, NULL, GF_ARG_HINT_HIDE|GF_FS_ARG_UPDATE_SYNC},
    1998             :         { OFFS(owsize), "output window size (readonly)", GF_PROP_VEC2I, NULL, NULL, GF_ARG_HINT_EXPERT},
    1999             :         { OFFS(buffer_done), "buffer done indication (readonly)", GF_PROP_BOOL, NULL, NULL, GF_ARG_HINT_EXPERT},
    2000             :         { OFFS(rebuffer), "time at which rebuffer started, 0 if not rebuffering (readonly)", GF_PROP_LUINT, NULL, NULL, GF_ARG_HINT_EXPERT},
    2001             : 
    2002             :         { OFFS(vflip), "flip video (GL only)\n"
    2003             :                 "- no: no flipping\n"
    2004             :                 "- v: vertical flip\n"
    2005             :                 "- h: horizontal flip\n"
    2006             :                 "- vh: horizontal and vertical\n"
    2007             :                 "- hv: same as vh"
    2008             :                 , GF_PROP_UINT, "no", "no|v|h|vh|hv", GF_FS_ARG_UPDATE | GF_FS_ARG_HINT_ADVANCED},
    2009             :         { OFFS(vrot), "rotate video by given angle\n"
    2010             :                 "- 0: no rotation\n"
    2011             :                 "- 90: rotate 90 degree counter clockwise\n"
    2012             :                 "- 180: rotate 180 degree\n"
    2013             :                 "- 270: rotate 90 degree clockwise"
    2014             :         , GF_PROP_UINT, "0","0|90|180|270", GF_FS_ARG_UPDATE | GF_FS_ARG_HINT_ADVANCED},
    2015             :         {0}
    2016             : };
    2017             : 
    2018             : static const GF_FilterCapability VideoOutCaps[] =
    2019             : {
    2020             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
    2021             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_RAW)
    2022             : };
    2023             : 
    2024             : 
    2025             : GF_FilterRegister VideoOutRegister = {
    2026             :         .name = "vout",
    2027             :         GF_FS_SET_DESCRIPTION("Video output")
    2028             :         GF_FS_SET_HELP("This filter displays a single visual pid in a window.\n"\
    2029             :         "The window is created unless a window handle (HWND, xWindow, etc) is indicated in the config file ( [Temp]OSWnd=ptr).\n"\
    2030             :         "The output uses GPAC video output module indicated in [-drv]() option or in the config file (see GPAC core help).\n"\
    2031             :         "The video output module can be further configured (see GPAC core help).\n"\
    2032             :         "The filter can use OpenGL or 2D blit of the graphics card, depending on the OS support.\n"\
    2033             :         "The filter can be used do dump frames as written on the graphics card.\n"\
    2034             :         "In this case, the window is not visible and only the listed frames are drawn to the GPU.\n"\
    2035             :         "The pixel format of the dumped frame is always RGB in OpenGL and matches the video backbuffer format in 2D mode.\n"\
    2036             :         )
    2037             :         .private_size = sizeof(GF_VideoOutCtx),
    2038             :         .flags = GF_FS_REG_MAIN_THREAD,
    2039             :         .args = VideoOutArgs,
    2040             :         SETCAPS(VideoOutCaps),
    2041             :         .initialize = vout_initialize,
    2042             :         .finalize = vout_finalize,
    2043             :         .configure_pid = vout_configure_pid,
    2044             :         .process = vout_process,
    2045             :         .process_event = vout_process_event,
    2046             :         .update_arg = vout_update_arg
    2047             : };
    2048             : 
    2049        2877 : const GF_FilterRegister *vout_register(GF_FilterSession *session)
    2050             : {
    2051        2877 :         return &VideoOutRegister;
    2052             : }
    2053             : #else
    2054             : const GF_FilterRegister *vout_register(GF_FilterSession *session)
    2055             : {
    2056             :         return NULL;
    2057             : }
    2058             : #endif // GPAC_DISABLE_PLAYER

Generated by: LCOV version 1.13