mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2024-11-24 09:49:41 +03:00
Fplay: code cleanup
git-svn-id: svn://kolibrios.org@6136 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
798707fd38
commit
f71187e224
@ -16,26 +16,17 @@
|
||||
#include "sound.h"
|
||||
#include "fplay.h"
|
||||
|
||||
#ifdef HAVE_VAAPI
|
||||
int va_check_codec_support(enum AVCodecID id);
|
||||
#endif
|
||||
|
||||
volatile enum player_state player_state = STOP;
|
||||
volatile enum player_state decoder_state = PREPARE;
|
||||
volatile enum player_state sound_state = STOP;
|
||||
|
||||
uint32_t win_width, win_height;
|
||||
|
||||
int have_sound = 0;
|
||||
int have_sound = 0;
|
||||
|
||||
uint8_t *decoder_buffer;
|
||||
extern int resampler_size;
|
||||
|
||||
extern int sample_rate;
|
||||
char *movie_file;
|
||||
|
||||
void flush_video(vst_t* vst);
|
||||
|
||||
|
||||
int64_t rewind_pos;
|
||||
|
||||
@ -46,12 +37,6 @@ int threads_running = DECODER_THREAD;
|
||||
extern double audio_base;
|
||||
|
||||
|
||||
double get_audio_base(vst_t* vst)
|
||||
{
|
||||
return (double)av_q2d(vst->fCtx->streams[vst->aStream]->time_base)*1000;
|
||||
};
|
||||
|
||||
|
||||
int main( int argc, char *argv[])
|
||||
{
|
||||
static vst_t vst;
|
||||
@ -60,14 +45,14 @@ int main( int argc, char *argv[])
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
movie_file = get_moviefile();
|
||||
if(movie_file == NULL)
|
||||
vst.input_file = get_moviefile();
|
||||
if(vst.input_file == NULL)
|
||||
{
|
||||
printf("Please provide a movie file\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else movie_file = argv[1];
|
||||
else vst.input_file = argv[1];
|
||||
|
||||
/* register all codecs, demux and protocols */
|
||||
|
||||
@ -77,9 +62,9 @@ int main( int argc, char *argv[])
|
||||
avdevice_register_all();
|
||||
av_register_all();
|
||||
|
||||
if( avformat_open_input(&vst.fCtx, movie_file, NULL, NULL) < 0)
|
||||
if( avformat_open_input(&vst.fCtx, vst.input_file, NULL, NULL) < 0)
|
||||
{
|
||||
printf("Cannot open file %s\n\r", movie_file);
|
||||
printf("Cannot open file %s\n\r", vst.input_file);
|
||||
return -1; // Couldn't open file
|
||||
};
|
||||
|
||||
@ -92,15 +77,15 @@ int main( int argc, char *argv[])
|
||||
return -1;
|
||||
};
|
||||
|
||||
file_name = strrchr(movie_file,'/')+1;
|
||||
file_name = strrchr(vst.input_file,'/')+1;
|
||||
dot = strrchr(file_name,'.');
|
||||
if(dot)
|
||||
{
|
||||
movie_file = malloc(dot-file_name+1);
|
||||
memcpy(movie_file, file_name, dot-file_name);
|
||||
movie_file[dot-file_name] = 0;
|
||||
vst.input_name = malloc(dot-file_name+1);
|
||||
memcpy(vst.input_name, file_name, dot-file_name);
|
||||
vst.input_name[dot-file_name] = 0;
|
||||
}
|
||||
else movie_file = file_name;
|
||||
else vst.input_name = file_name;
|
||||
|
||||
stream_duration = vst.fCtx->duration;
|
||||
|
||||
@ -114,7 +99,7 @@ int main( int argc, char *argv[])
|
||||
&& vst.vStream < 0)
|
||||
{
|
||||
vst.vStream = i;
|
||||
video_time_base = vst.fCtx->streams[i]->time_base;
|
||||
vst.video_time_base = vst.fCtx->streams[i]->time_base;
|
||||
if(stream_duration == 0)
|
||||
stream_duration = vst.fCtx->streams[i]->duration;
|
||||
}
|
||||
@ -123,6 +108,7 @@ int main( int argc, char *argv[])
|
||||
vst.aStream < 0)
|
||||
{
|
||||
vst.aStream = i;
|
||||
vst.audio_time_base = vst.fCtx->streams[i]->time_base;
|
||||
if(stream_duration == 0)
|
||||
stream_duration = vst.fCtx->streams[i]->duration;
|
||||
}
|
||||
@ -141,8 +127,6 @@ int main( int argc, char *argv[])
|
||||
vst.aCtx = vst.fCtx->streams[vst.aStream]->codec;
|
||||
|
||||
vst.vCodec = avcodec_find_decoder(vst.vCtx->codec_id);
|
||||
printf("codec id %x name %s\n",vst.vCtx->codec_id, vst.vCodec->name);
|
||||
printf("ctx->pix_fmt %d\n", vst.vCtx->pix_fmt);
|
||||
|
||||
INIT_LIST_HEAD(&vst.input_list);
|
||||
INIT_LIST_HEAD(&vst.output_list);
|
||||
@ -157,9 +141,16 @@ int main( int argc, char *argv[])
|
||||
{
|
||||
printf("Unsupported codec with id %d for input stream %d\n",
|
||||
vst.vCtx->codec_id, vst.vStream);
|
||||
return -1; // Codec not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
vst.Frame = av_frame_alloc();
|
||||
if(vst.Frame == NULL)
|
||||
{
|
||||
printf("Cannot alloc video frame\n");
|
||||
return -1;
|
||||
};
|
||||
|
||||
if(fplay_init_context(&vst))
|
||||
return -1;
|
||||
|
||||
@ -170,8 +161,6 @@ int main( int argc, char *argv[])
|
||||
return -1; // Could not open codec
|
||||
};
|
||||
|
||||
printf("ctx->pix_fmt %d\n", vst.vCtx->pix_fmt);
|
||||
|
||||
if (vst.aCtx->channels > 0)
|
||||
vst.aCtx->request_channels = FFMIN(2, vst.aCtx->channels);
|
||||
else
|
||||
@ -223,10 +212,10 @@ int main( int argc, char *argv[])
|
||||
}
|
||||
else printf("Unsupported audio codec!\n");
|
||||
|
||||
if(!init_video(&vst))
|
||||
return 0;
|
||||
|
||||
mutex_lock_timeout(&vst.decoder_lock, 3000);
|
||||
mutex_lock(&vst.decoder_lock);
|
||||
create_thread(video_thread, &vst, 1024*1024);
|
||||
if(mutex_lock_timeout(&vst.decoder_lock, 3000) == 0)
|
||||
return -1;
|
||||
|
||||
decoder(&vst);
|
||||
|
||||
@ -369,7 +358,6 @@ void decoder(vst_t* vst)
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
yield();
|
||||
continue;
|
||||
|
||||
|
@ -27,7 +27,6 @@ typedef struct
|
||||
int index;
|
||||
double pts;
|
||||
double pkt_pts;
|
||||
double pkt_dts;
|
||||
volatile int ready;
|
||||
}vframe_t;
|
||||
|
||||
@ -111,21 +110,24 @@ typedef struct {
|
||||
int put_packet(queue_t *q, AVPacket *pkt);
|
||||
int get_packet(queue_t *q, AVPacket *pkt);
|
||||
|
||||
#define HWDEC_NUM_SURFACES 16
|
||||
struct vstate
|
||||
{
|
||||
AVFormatContext *fCtx; /* format context */
|
||||
AVCodecContext *vCtx; /* video decoder context */
|
||||
AVCodecContext *aCtx; /* audio decoder context */
|
||||
AVCodec *vCodec; /* video codec */
|
||||
AVCodec *aCodec; /* audio codec */
|
||||
int vStream; /* video stream index */
|
||||
int aStream; /* audio stream index */
|
||||
AVFormatContext *fCtx; /* format context */
|
||||
AVCodecContext *vCtx; /* video decoder context */
|
||||
AVCodecContext *aCtx; /* audio decoder context */
|
||||
AVCodec *vCodec; /* video codec */
|
||||
AVCodec *aCodec; /* audio codec */
|
||||
char *input_file;
|
||||
char *input_name;
|
||||
int vStream; /* video stream index */
|
||||
int aStream; /* audio stream index */
|
||||
AVRational video_time_base;
|
||||
AVRational audio_time_base;
|
||||
|
||||
queue_t q_video; /* video packets queue */
|
||||
queue_t q_audio; /* audio packets queue */
|
||||
queue_t q_video; /* video packets queue */
|
||||
queue_t q_audio; /* audio packets queue */
|
||||
|
||||
mutex_t gpu_lock; /* gpu access lock. libdrm not yet thread safe :( */
|
||||
mutex_t gpu_lock; /* gpu access lock. libdrm not yet thread safe :( */
|
||||
mutex_t decoder_lock;
|
||||
|
||||
mutex_t input_lock;
|
||||
@ -133,13 +135,18 @@ struct vstate
|
||||
struct list_head input_list;
|
||||
struct list_head output_list;
|
||||
|
||||
AVFrame *Frame;
|
||||
|
||||
vframe_t *decoder_frame;
|
||||
void *hwCtx; /* hardware context */
|
||||
int hwdec:1; /* hardware decoder */
|
||||
int blit_bitmap:1; /* hardware RGBA blitter */
|
||||
int blit_texture:1; /* hardware RGBA blit and scale */
|
||||
int blit_planar:1; /* hardbare YUV blit and scale */
|
||||
volatile int frames_count;
|
||||
void *hwCtx; /* hardware context */
|
||||
int hwdec:1; /* hardware decoder */
|
||||
int blit_bitmap:1; /* hardware RGBA blitter */
|
||||
int blit_texture:1; /* hardware RGBA blit and scale */
|
||||
int blit_planar:1; /* hardbare YUV blit and scale */
|
||||
int frame_reorder:1;
|
||||
int nframes;
|
||||
vframe_t vframes[16];
|
||||
};
|
||||
|
||||
|
||||
@ -149,7 +156,6 @@ struct vstate
|
||||
|
||||
extern int threads_running;
|
||||
extern astream_t astream;
|
||||
extern AVRational video_time_base;
|
||||
|
||||
render_t *create_render(vst_t *vst, window_t *win, uint32_t flags);
|
||||
void destroy_render(render_t *render);
|
||||
@ -164,8 +170,8 @@ int init_audio(int format);
|
||||
int audio_thread(void *param);
|
||||
void set_audio_volume(int left, int right);
|
||||
|
||||
int init_video(vst_t* vst);
|
||||
int video_thread(void *param);
|
||||
void flush_video(vst_t* vst);
|
||||
|
||||
void decoder(vst_t *vst);
|
||||
int decode_video(vst_t* vst);
|
||||
@ -180,6 +186,11 @@ static inline void GetNotify(void *event)
|
||||
::"a"(68),"b"(14),"c"(event));
|
||||
}
|
||||
|
||||
static inline double get_audio_base(vst_t* vst)
|
||||
{
|
||||
return (double)av_q2d(vst->fCtx->streams[vst->aStream]->time_base)*1000;
|
||||
};
|
||||
|
||||
void va_create_planar(vst_t *vst, vframe_t *vframe);
|
||||
|
||||
int init_fontlib();
|
||||
|
@ -39,7 +39,7 @@ struct hw_profile
|
||||
static int drm_fd = 0;
|
||||
static struct vaapi_context *v_context;
|
||||
|
||||
static VASurfaceID v_surface_id[HWDEC_NUM_SURFACES];
|
||||
static VASurfaceID v_surface_id[16];
|
||||
|
||||
#define HAS_HEVC VA_CHECK_VERSION(0, 38, 0)
|
||||
#define HAS_VP9 (VA_CHECK_VERSION(0, 38, 1) && defined(FF_PROFILE_VP9_0))
|
||||
@ -281,10 +281,10 @@ static int has_entrypoint(struct vaapi_context *vaapi, VAProfile profile, VAEntr
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vaapi_init_decoder(VAProfile profile,
|
||||
VAEntrypoint entrypoint,
|
||||
unsigned int picture_width,
|
||||
unsigned int picture_height)
|
||||
static int vaapi_init_decoder(vst_t *vst,VAProfile profile,
|
||||
VAEntrypoint entrypoint,
|
||||
unsigned int picture_width,
|
||||
unsigned int picture_height)
|
||||
{
|
||||
struct vaapi_context* const vaapi = v_context;
|
||||
VAConfigAttrib attrib;
|
||||
@ -343,7 +343,7 @@ ENTER();
|
||||
|
||||
printf("vaCreateSurfaces %dx%d\n",picture_width,picture_height);
|
||||
status = vaCreateSurfaces(vaapi->display, VA_RT_FORMAT_YUV420, picture_width, picture_height,
|
||||
v_surface_id,HWDEC_NUM_SURFACES,NULL,0);
|
||||
v_surface_id,vst->nframes,NULL,0);
|
||||
if (!vaapi_check_status(status, "vaCreateSurfaces()"))
|
||||
{
|
||||
FAIL();
|
||||
@ -378,7 +378,7 @@ ENTER();
|
||||
status = vaCreateContext(vaapi->display, config_id,
|
||||
picture_width, picture_height,
|
||||
VA_PROGRESSIVE,
|
||||
v_surface_id, HWDEC_NUM_SURFACES,
|
||||
v_surface_id, vst->nframes,
|
||||
&context_id);
|
||||
if (!vaapi_check_status(status, "vaCreateContext()"))
|
||||
{
|
||||
@ -396,9 +396,9 @@ ENTER();
|
||||
static enum PixelFormat get_format(struct AVCodecContext *avctx,
|
||||
const enum AVPixelFormat *fmt)
|
||||
{
|
||||
vst_t *vst = (vst_t*)avctx->opaque;
|
||||
VAProfile profile = VAProfileNone;
|
||||
|
||||
ENTER();
|
||||
|
||||
for (int i = 0; fmt[i] != PIX_FMT_NONE; i++)
|
||||
{
|
||||
@ -419,17 +419,15 @@ static enum PixelFormat get_format(struct AVCodecContext *avctx,
|
||||
hw_profiles[n].ff_profile == avctx->profile)
|
||||
{
|
||||
profile = hw_profiles[n].va_profile;
|
||||
if (vaapi_init_decoder(profile, VAEntrypointVLD, avctx->width, avctx->height) == 0)
|
||||
if (vaapi_init_decoder(vst, profile, VAEntrypointVLD, avctx->width, avctx->height) == 0)
|
||||
{
|
||||
avctx->hwaccel_context = v_context;
|
||||
LEAVE();
|
||||
return fmt[i]; ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
FAIL();
|
||||
return PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
@ -472,6 +470,8 @@ int fplay_init_context(vst_t *vst)
|
||||
{
|
||||
AVCodecContext *vCtx = vst->vCtx;
|
||||
|
||||
vst->nframes = 4;
|
||||
|
||||
if(va_check_codec_support(vCtx->codec_id))
|
||||
{
|
||||
VADisplay dpy;
|
||||
@ -481,9 +481,12 @@ int fplay_init_context(vst_t *vst)
|
||||
|
||||
if(vst->hwCtx != NULL)
|
||||
{
|
||||
for(int i = 0; i < HWDEC_NUM_SURFACES; i++)
|
||||
if(vCtx->codec_id == AV_CODEC_ID_H264)
|
||||
vst->nframes = 16;
|
||||
|
||||
for(int i = 0; i < vst->nframes; i++)
|
||||
{
|
||||
vframe_t *vframe = calloc(1, sizeof(*vframe));
|
||||
vframe_t *vframe = &vst->vframes[i];
|
||||
|
||||
vframe->format = AV_PIX_FMT_NONE;
|
||||
vframe->is_hw_pic = 1;
|
||||
@ -494,6 +497,7 @@ int fplay_init_context(vst_t *vst)
|
||||
};
|
||||
|
||||
vst->hwdec = 1;
|
||||
vst->frame_reorder = 1;
|
||||
vCtx->opaque = vst;
|
||||
vCtx->thread_count = 1;
|
||||
vCtx->get_format = get_format;
|
||||
@ -504,12 +508,12 @@ int fplay_init_context(vst_t *vst)
|
||||
|
||||
vst->hwdec = 0;
|
||||
|
||||
for(int i = 0; i < HWDEC_NUM_SURFACES; i++)
|
||||
for(int i = 0; i < vst->nframes; i++)
|
||||
{
|
||||
vframe_t *vframe;
|
||||
int ret;
|
||||
|
||||
vframe = calloc(1, sizeof(*vframe));
|
||||
vframe = &vst->vframes[i];
|
||||
|
||||
ret = avpicture_alloc(&vframe->picture, vst->vCtx->pix_fmt,
|
||||
vst->vCtx->width, vst->vCtx->height);
|
||||
|
@ -21,16 +21,10 @@ extern int64_t stream_duration;
|
||||
extern volatile int sound_level_0;
|
||||
extern volatile int sound_level_1;
|
||||
|
||||
volatile int frames_count = 0;
|
||||
|
||||
struct SwsContext *cvt_ctx = NULL;
|
||||
|
||||
|
||||
render_t *main_render;
|
||||
|
||||
AVRational video_time_base;
|
||||
AVFrame *Frame;
|
||||
|
||||
void get_client_rect(rect_t *rc);
|
||||
void run_render(window_t *win, void *render);
|
||||
void window_update_layout(window_t *win);
|
||||
@ -51,30 +45,12 @@ void flush_video(vst_t *vst)
|
||||
vframe->pts = 0;
|
||||
vframe->ready = 0;
|
||||
}
|
||||
vst->frames_count = 0;
|
||||
|
||||
mutex_unlock(&vst->input_lock);
|
||||
mutex_unlock(&vst->output_lock);
|
||||
|
||||
frames_count = 0;
|
||||
};
|
||||
|
||||
int init_video(vst_t *vst)
|
||||
{
|
||||
Frame = av_frame_alloc();
|
||||
if ( Frame == NULL )
|
||||
{
|
||||
printf("Cannot alloc video frame\n\r");
|
||||
return 0;
|
||||
};
|
||||
|
||||
mutex_lock(&vst->decoder_lock);
|
||||
|
||||
create_thread(video_thread, vst, 1024*1024);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
static double dts = 0.0;
|
||||
|
||||
static vframe_t *get_input_frame(vst_t *vst)
|
||||
{
|
||||
@ -115,31 +91,31 @@ static void put_output_frame(vst_t *vst, vframe_t *vframe)
|
||||
};
|
||||
};
|
||||
};
|
||||
vst->frames_count++;
|
||||
mutex_unlock(&vst->output_lock);
|
||||
};
|
||||
|
||||
int decode_video(vst_t* vst)
|
||||
{
|
||||
AVPacket pkt;
|
||||
double pts;
|
||||
AVPacket pkt;
|
||||
|
||||
int frameFinished;
|
||||
|
||||
if(vst->decoder_frame == NULL)
|
||||
vst->decoder_frame = get_input_frame(vst);
|
||||
|
||||
if(vst->decoder_frame == NULL)
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
if( get_packet(&vst->q_video, &pkt) == 0 )
|
||||
return 0;
|
||||
|
||||
frameFinished = 0;
|
||||
if(dts == 0)
|
||||
dts = pkt.pts;
|
||||
|
||||
mutex_lock(&vst->gpu_lock);
|
||||
|
||||
if(avcodec_decode_video2(vst->vCtx, Frame, &frameFinished, &pkt) <= 0)
|
||||
if(avcodec_decode_video2(vst->vCtx, vst->Frame, &frameFinished, &pkt) <= 0)
|
||||
printf("video decoder error\n");
|
||||
|
||||
if(frameFinished)
|
||||
@ -150,22 +126,21 @@ int decode_video(vst_t* vst)
|
||||
if(vst->hwdec)
|
||||
pts = pkt.pts;
|
||||
else
|
||||
pts = av_frame_get_best_effort_timestamp(Frame);
|
||||
pts = av_frame_get_best_effort_timestamp(vst->Frame);
|
||||
|
||||
pts*= av_q2d(video_time_base);
|
||||
pts*= av_q2d(vst->video_time_base);
|
||||
|
||||
dst_pic = &vframe->picture;
|
||||
|
||||
if(vframe->is_hw_pic == 0)
|
||||
av_image_copy(dst_pic->data, dst_pic->linesize,
|
||||
(const uint8_t**)Frame->data,
|
||||
Frame->linesize, vst->vCtx->pix_fmt, vst->vCtx->width, vst->vCtx->height);
|
||||
(const uint8_t**)vst->Frame->data,
|
||||
vst->Frame->linesize, vst->vCtx->pix_fmt, vst->vCtx->width, vst->vCtx->height);
|
||||
else
|
||||
va_create_planar(vst, vframe);
|
||||
|
||||
vframe->pts = pts*1000.0;
|
||||
vframe->pkt_pts = pkt.pts*av_q2d(video_time_base)*1000.0;
|
||||
vframe->pkt_dts = dts*av_q2d(video_time_base)*1000.0;
|
||||
vframe->pkt_pts = pkt.pts*av_q2d(vst->video_time_base)*1000.0;
|
||||
vframe->ready = 1;
|
||||
|
||||
put_output_frame(vst, vframe);
|
||||
@ -175,10 +150,8 @@ int decode_video(vst_t* vst)
|
||||
// vst->vframe[vst->dfx].pkt_pts, vst->vframe[vst->dfx].pkt_dts);
|
||||
|
||||
vst->decoder_frame = NULL;
|
||||
frames_count++;
|
||||
dts = 0;
|
||||
};
|
||||
av_frame_unref(Frame);
|
||||
av_frame_unref(vst->Frame);
|
||||
mutex_unlock(&vst->gpu_lock);
|
||||
|
||||
av_free_packet(&pkt);
|
||||
@ -417,7 +390,7 @@ void render_time(render_t *render)
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
else if (decoder_state == STOP && frames_count == 0 &&
|
||||
else if (decoder_state == STOP && vst->frames_count == 0 &&
|
||||
player_state != STOP)
|
||||
{
|
||||
player_stop();
|
||||
@ -441,10 +414,11 @@ void render_time(render_t *render)
|
||||
|
||||
vframe = list_first_entry(&vst->output_list, vframe_t, list);
|
||||
list_del(&vframe->list);
|
||||
vst->frames_count--;
|
||||
mutex_unlock(&vst->output_lock);
|
||||
|
||||
ctime = get_master_clock();
|
||||
fdelay = (vframe->pkt_pts - ctime);
|
||||
fdelay = (vframe->pts - ctime);
|
||||
|
||||
if(fdelay > 15.0)
|
||||
{
|
||||
@ -458,7 +432,7 @@ void render_time(render_t *render)
|
||||
|
||||
if(main_render->win->win_state != FULLSCREEN)
|
||||
{
|
||||
prg->current = vframe->pkt_pts * 1000;
|
||||
prg->current = vframe->pts * 1000;
|
||||
lvl->current = vframe->index & 1 ? sound_level_1 : sound_level_0;
|
||||
|
||||
send_message(&prg->ctrl, PRG_PROGRESS, 0, 0);
|
||||
@ -467,7 +441,6 @@ void render_time(render_t *render)
|
||||
send_message(&lvl->ctrl, MSG_PAINT, 0, 0);
|
||||
}
|
||||
|
||||
frames_count--;
|
||||
vframe->ready = 0;
|
||||
|
||||
mutex_lock(&vst->input_lock);
|
||||
@ -476,9 +449,6 @@ void render_time(render_t *render)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern char *movie_file;
|
||||
|
||||
int video_thread(void *param)
|
||||
{
|
||||
vst_t *vst = param;
|
||||
@ -486,7 +456,7 @@ int video_thread(void *param)
|
||||
|
||||
init_winlib();
|
||||
|
||||
MainWindow = create_window(movie_file,0,
|
||||
MainWindow = create_window(vst->input_name,0,
|
||||
10,10,vst->vCtx->width,vst->vCtx->height+CAPTION_HEIGHT+PANEL_HEIGHT,MainWindowProc);
|
||||
|
||||
MainWindow->panel.prg->max = stream_duration;
|
||||
|
Loading…
Reference in New Issue
Block a user