mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-22 05:40:27 +00:00
774 lines
26 KiB
C++
774 lines
26 KiB
C++
/**
|
|
* @file video_display/sdl.cpp
|
|
* @author Lukas Hejtmanek <xhejtman@ics.muni.cz>
|
|
* @author Milos Liska <xliska@fi.muni.cz>
|
|
* @author Martin Pulec <pulec@cesnet.cz>
|
|
*/
|
|
/*
|
|
* Copyright (c) 2010-2015 CESNET, z. s. p. o.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, is permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of CESNET nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "config_unix.h"
|
|
#include "config_win32.h"
|
|
#include "host.h"
|
|
|
|
#include "debug.h"
|
|
#include "messaging.h"
|
|
#include "module.h"
|
|
#include "video_display.h"
|
|
#include "video_display/sdl.h"
|
|
#include "tv.h"
|
|
#include "audio/audio.h"
|
|
#include "audio/utils.h"
|
|
#include "utils/ring_buffer.h"
|
|
#include "video.h"
|
|
|
|
#ifdef HAVE_MACOSX
|
|
#include "utils/autorelease_pool.h"
|
|
extern "C" void NSApplicationLoad();
|
|
#elif defined HAVE_LINUX
|
|
#include "x11_common.h"
|
|
#endif /* HAVE_MACOSX */
|
|
|
|
#include <math.h>
|
|
|
|
#include <SDL/SDL.h>
|
|
#include <SDL/SDL_syswm.h>
|
|
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
#include <queue>
|
|
|
|
/* splashscreen (xsedmik) */
|
|
#include "video_display/splashscreen.h"
|
|
|
|
#define MAGIC_SDL DISPLAY_SDL_ID
|
|
#define FOURCC_UYVY 0x59565955
|
|
#define FOURCC_YUYV 0x32595559
|
|
|
|
#define MAX_BUFFER_SIZE 1
|
|
|
|
using namespace std;
|
|
|
|
struct state_sdl {
|
|
uint32_t magic;
|
|
|
|
struct timeval tv;
|
|
int frames;
|
|
|
|
SDL_Overlay * yuv_image;
|
|
SDL_Surface * sdl_screen;
|
|
SDL_Rect dst_rect;
|
|
|
|
bool deinterlace;
|
|
bool fs;
|
|
bool nodecorate;
|
|
|
|
int screen_w, screen_h;
|
|
|
|
struct ring_buffer *audio_buffer;
|
|
struct audio_frame audio_frame;
|
|
bool play_audio;
|
|
|
|
queue<struct video_frame *> frame_queue;
|
|
queue<struct video_frame *> free_frame_queue;
|
|
struct video_desc current_desc;
|
|
struct video_desc current_display_desc;
|
|
mutex lock;
|
|
condition_variable frame_ready_cv;
|
|
condition_variable frame_consumed_cv;
|
|
|
|
#ifdef HAVE_MACOSX
|
|
void *autorelease_pool;
|
|
#endif
|
|
volatile bool should_exit;
|
|
int bpp;
|
|
uint32_t sdl_flags_win, sdl_flags_fs;
|
|
|
|
struct module mod;
|
|
|
|
state_sdl(struct module *parent) : magic(MAGIC_SDL), frames(0), yuv_image(nullptr), sdl_screen(nullptr), dst_rect(),
|
|
deinterlace(false), fs(false), nodecorate(false),
|
|
screen_w(0), screen_h(0), audio_buffer(nullptr), audio_frame(), play_audio(false),
|
|
current_desc(), current_display_desc(),
|
|
#ifdef HAVE_MACOSX
|
|
autorelease_pool(nullptr),
|
|
#endif
|
|
should_exit(false)
|
|
{
|
|
gettimeofday(&tv, NULL);
|
|
module_init_default(&mod);
|
|
mod.cls = MODULE_CLASS_DATA;
|
|
module_register(&mod, parent);
|
|
}
|
|
|
|
~state_sdl() {
|
|
module_done(&mod);
|
|
}
|
|
};
|
|
|
|
static void loadSplashscreen(struct state_sdl *s);
|
|
static void show_help(void);
|
|
static int display_sdl_reconfigure_real(void *state, struct video_desc desc);
|
|
|
|
static void cleanup_screen(struct state_sdl *s);
|
|
static void configure_audio(struct state_sdl *s);
|
|
static int display_sdl_handle_events(struct state_sdl *s);
|
|
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len);
|
|
static void compute_dst_rect(struct state_sdl *s, int vid_w, int vid_h, int window_w, int window_h, codec_t codec);
|
|
static bool update_size(struct state_sdl *s, int win_w, int win_h);
|
|
|
|
/**
|
|
* Load splashscreen
|
|
* Function loads graphic data from header file "splashscreen.h", where are
|
|
* stored splashscreen data in RGB format.
|
|
*/
|
|
static void loadSplashscreen(struct state_sdl *s) {
|
|
struct video_desc desc;
|
|
|
|
desc.width = 512;
|
|
desc.height = 512;
|
|
desc.color_spec = RGBA;
|
|
desc.interlacing = PROGRESSIVE;
|
|
desc.fps = 1;
|
|
desc.tile_count = 1;
|
|
|
|
display_sdl_reconfigure(s, desc);
|
|
|
|
struct video_frame *frame = vf_alloc_desc_data(desc);
|
|
|
|
const char *data = splash_data;
|
|
memset(frame->tiles[0].data, 0, frame->tiles[0].data_len);
|
|
for (unsigned int y = 0; y < splash_height; ++y) {
|
|
char *line = frame->tiles[0].data;
|
|
line += vc_get_linesize(frame->tiles[0].width,
|
|
frame->color_spec) *
|
|
(((frame->tiles[0].height - splash_height) / 2) + y);
|
|
line += vc_get_linesize(
|
|
(frame->tiles[0].width - splash_width)/2,
|
|
frame->color_spec);
|
|
for (unsigned int x = 0; x < splash_width; ++x) {
|
|
HEADER_PIXEL(data,line);
|
|
line += 4;
|
|
}
|
|
}
|
|
|
|
display_sdl_putf(s, frame, PUTF_BLOCKING);
|
|
}
|
|
|
|
static void compute_dst_rect(struct state_sdl *s, int vid_w, int vid_h, int window_w, int window_h, codec_t codec)
|
|
{
|
|
s->dst_rect.x = 0;
|
|
s->dst_rect.y = 0;
|
|
s->dst_rect.w = vid_w;
|
|
s->dst_rect.h = vid_h;
|
|
|
|
if (codec_is_a_rgb(codec)) {
|
|
if (window_w > vid_w) {
|
|
s->dst_rect.x = ((int) window_w - vid_w) / 2;
|
|
} else if (window_w < vid_w) {
|
|
s->dst_rect.w = window_w;
|
|
}
|
|
if (window_h > vid_h) {
|
|
s->dst_rect.y = ((int) window_h - vid_h) / 2;
|
|
} else if (window_h < vid_h) {
|
|
s->dst_rect.h = window_h;
|
|
}
|
|
} else if (!codec_is_a_rgb(codec) && (vid_w != window_w || vid_h != window_h)) {
|
|
double frame_aspect = (double) vid_w / vid_h;
|
|
double screen_aspect = (double) window_w / window_h;
|
|
if(screen_aspect > frame_aspect) {
|
|
s->dst_rect.h = window_h;
|
|
s->dst_rect.w = window_h * frame_aspect;
|
|
s->dst_rect.x = ((int) window_w - s->dst_rect.w) / 2;
|
|
} else {
|
|
s->dst_rect.w = window_w;
|
|
s->dst_rect.h = window_w / frame_aspect;
|
|
s->dst_rect.y = ((int) window_h - s->dst_rect.h) / 2;
|
|
}
|
|
}
|
|
|
|
fprintf(stdout, "Setting SDL rect %dx%d - %d,%d.\n", s->dst_rect.w,
|
|
s->dst_rect.h, s->dst_rect.x, s->dst_rect.y);
|
|
}
|
|
|
|
static bool update_size(struct state_sdl *s, int win_w, int win_h)
|
|
{
|
|
unsigned int x_res_x, x_res_y;
|
|
|
|
if (s->fs) {
|
|
x_res_x = s->screen_w;
|
|
x_res_y = s->screen_h;
|
|
} else {
|
|
x_res_x = win_w;
|
|
x_res_y = win_h;
|
|
}
|
|
SDL_Surface *sdl_screen_old = s->sdl_screen;
|
|
s->sdl_screen = SDL_SetVideoMode(x_res_x, x_res_y, s->bpp, s->fs ? s->sdl_flags_fs : s->sdl_flags_win);
|
|
fprintf(stdout, "Setting video mode %dx%d.\n", x_res_x, x_res_y);
|
|
compute_dst_rect(s, s->current_display_desc.width, s->current_display_desc.height, x_res_x, x_res_y, s->current_display_desc.color_spec);
|
|
if (s->sdl_screen == NULL) {
|
|
fprintf(stderr, "Error setting video mode %dx%d!\n", x_res_x,
|
|
x_res_y);
|
|
s->sdl_screen = sdl_screen_old;
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles outer events like a keyboard press
|
|
* Responds to key:<br/>
|
|
* <table>
|
|
* <td><tr>q</tr><tr>terminates program</tr></td>
|
|
* <td><tr>f</tr><tr>toggles between fullscreen and windowed display mode</tr></td>
|
|
* </table>
|
|
*
|
|
* @since 08-04-2010, xsedmik
|
|
* @param arg Structure (state_sdl) contains the current settings
|
|
* @return zero value everytime
|
|
*/
|
|
static int display_sdl_handle_events(struct state_sdl *s)
|
|
{
|
|
struct message *msg;
|
|
while ((msg = check_message(&s->mod))) {
|
|
auto msg_univ = reinterpret_cast<struct msg_universal *>(msg);
|
|
if (strncasecmp(msg_univ->text, "win-title ", strlen("win_title ")) == 0) {
|
|
const char *title = msg_univ->text + strlen("win_title");
|
|
SDL_WM_SetCaption(title, title);
|
|
} else {
|
|
fprintf(stderr, "[SDL] Unknown command received: %s\n", msg_univ->text);
|
|
}
|
|
free_message(msg);
|
|
}
|
|
|
|
SDL_Event sdl_event;
|
|
while (SDL_PollEvent(&sdl_event)) {
|
|
switch (sdl_event.type) {
|
|
case SDL_VIDEORESIZE:
|
|
update_size(s, sdl_event.resize.w, sdl_event.resize.h);
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
if (!strcmp(SDL_GetKeyName(sdl_event.key.keysym.sym), "d")) {
|
|
s->deinterlace = s->deinterlace ? FALSE : TRUE;
|
|
printf("Deinterlacing: %s\n", s->deinterlace ? "ON"
|
|
: "OFF");
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp(SDL_GetKeyName(sdl_event.key.keysym.sym), "q")) {
|
|
exit_uv(0);
|
|
}
|
|
|
|
if (!strcmp(SDL_GetKeyName(sdl_event.key.keysym.sym), "f")) {
|
|
s->fs = !s->fs;
|
|
update_size(s, s->current_display_desc.width, s->current_display_desc.height);
|
|
return 1;
|
|
}
|
|
break;
|
|
case SDL_QUIT:
|
|
exit_uv(0);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void display_sdl_run(void *arg)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)arg;
|
|
struct timeval tv;
|
|
|
|
while (!s->should_exit) {
|
|
display_sdl_handle_events(s);
|
|
|
|
struct video_frame *frame = NULL;
|
|
|
|
{
|
|
unique_lock<mutex> lk(s->lock);
|
|
if (s->frame_ready_cv.wait_for(lk, std::chrono::milliseconds(100),
|
|
[s]{return s->frame_queue.size() > 0;})) {
|
|
frame = s->frame_queue.front();
|
|
s->frame_queue.pop();
|
|
lk.unlock();
|
|
s->frame_consumed_cv.notify_one();
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (s->deinterlace) {
|
|
vc_deinterlace((unsigned char *) frame->tiles[0].data,
|
|
vc_get_linesize(frame->tiles[0].width,
|
|
frame->color_spec), frame->tiles[0].height);
|
|
}
|
|
|
|
if (!video_desc_eq(video_desc_from_frame(frame), s->current_display_desc)) {
|
|
if (!display_sdl_reconfigure_real(s, video_desc_from_frame(frame))) {
|
|
goto free_frame;
|
|
}
|
|
}
|
|
|
|
if (codec_is_a_rgb(frame->color_spec)) {
|
|
decoder_t decoder = nullptr;
|
|
if (frame->color_spec == RGBA) {
|
|
decoder = vc_copylineRGBA;
|
|
} else {
|
|
decoder = vc_copylineRGB;
|
|
}
|
|
assert(decoder != nullptr);
|
|
SDL_LockSurface(s->sdl_screen);
|
|
size_t linesize = vc_get_linesize(frame->tiles[0].width, frame->color_spec);
|
|
for (size_t i = 0; i < frame->tiles[0].height; ++i) {
|
|
decoder((unsigned char *) s->sdl_screen->pixels +
|
|
s->sdl_screen->pitch * (s->dst_rect.y + i) +
|
|
s->dst_rect.x *
|
|
s->sdl_screen->format->BytesPerPixel,
|
|
(unsigned char *) frame->tiles[0].data + i * linesize,
|
|
linesize,
|
|
s->sdl_screen->format->Rshift,
|
|
s->sdl_screen->format->Gshift,
|
|
s->sdl_screen->format->Bshift);
|
|
}
|
|
|
|
SDL_UnlockSurface(s->sdl_screen);
|
|
SDL_Flip(s->sdl_screen);
|
|
} else {
|
|
SDL_LockYUVOverlay(s->yuv_image);
|
|
memcpy(*s->yuv_image->pixels, frame->tiles[0].data, frame->tiles[0].data_len);
|
|
SDL_UnlockYUVOverlay(s->yuv_image);
|
|
SDL_DisplayYUVOverlay(s->yuv_image, &s->dst_rect);
|
|
}
|
|
|
|
free_frame:
|
|
s->lock.lock();
|
|
s->free_frame_queue.push(frame);
|
|
s->lock.unlock();
|
|
|
|
s->frames++;
|
|
gettimeofday(&tv, NULL);
|
|
double seconds = tv_diff(tv, s->tv);
|
|
if (seconds > 5) {
|
|
double fps = s->frames / seconds;
|
|
fprintf(stdout, "[SDL] %d frames in %g seconds = %g FPS\n",
|
|
s->frames, seconds, fps);
|
|
s->tv = tv;
|
|
s->frames = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void show_help(void)
|
|
{
|
|
printf("SDL options:\n");
|
|
printf("\t-d sdl[:fs|:d|:nodecorate]* | help\n");
|
|
printf("\tfs - fullscreen\n");
|
|
printf("\td - deinterlace\n");
|
|
printf("\tnodecorate - disable WM decoration\n");
|
|
//printf("\t<f> - read frame content from the filename\n");
|
|
show_codec_help((char *) "sdl");
|
|
}
|
|
|
|
static void cleanup_screen(struct state_sdl *s)
|
|
{
|
|
if (!codec_is_a_rgb(s->current_display_desc.color_spec)) {
|
|
if (s->yuv_image != NULL) {
|
|
SDL_FreeYUVOverlay(s->yuv_image);
|
|
s->yuv_image = NULL;
|
|
}
|
|
}
|
|
if (s->sdl_screen != NULL) {
|
|
SDL_FreeSurface(s->sdl_screen);
|
|
s->sdl_screen = NULL;
|
|
}
|
|
}
|
|
|
|
int display_sdl_reconfigure(void *state, struct video_desc desc)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
|
|
s->current_desc = desc;
|
|
return 1;
|
|
}
|
|
|
|
static int display_sdl_reconfigure_real(void *state, struct video_desc desc)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
|
|
fprintf(stdout, "Reconfigure to size %dx%d\n", desc.width,
|
|
desc.height);
|
|
|
|
if (desc.color_spec == RGB) {
|
|
s->bpp = 24;
|
|
} else if (desc.color_spec == RGBA) {
|
|
s->bpp = 32;
|
|
} else {
|
|
s->bpp = 0; /* screen defautl */
|
|
}
|
|
s->sdl_flags_fs = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF;
|
|
s->sdl_flags_win = SDL_HWSURFACE | SDL_DOUBLEBUF | (s->nodecorate ? SDL_NOFRAME : 0);
|
|
if (!codec_is_a_rgb(desc.color_spec)) {
|
|
s->sdl_flags_win |= SDL_RESIZABLE;
|
|
}
|
|
s->current_display_desc = desc;
|
|
|
|
if (!update_size(s, desc.width, desc.height)) {
|
|
memset(&s->current_display_desc, 0, sizeof s->current_display_desc);
|
|
return FALSE;
|
|
}
|
|
if (window_title) {
|
|
SDL_WM_SetCaption(window_title, window_title);
|
|
} else {
|
|
SDL_WM_SetCaption("Ultragrid - SDL Display", "Ultragrid");
|
|
}
|
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
if (!codec_is_a_rgb(desc.color_spec)) {
|
|
s->yuv_image =
|
|
SDL_CreateYUVOverlay(desc.width, desc.height, desc.color_spec == UYVY ?
|
|
FOURCC_UYVY : FOURCC_YUYV, s->sdl_screen);
|
|
if (s->yuv_image == NULL) {
|
|
printf("SDL_overlay initialization failed.\n");
|
|
memset(&s->current_display_desc, 0, sizeof s->current_display_desc);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void *display_sdl_init(struct module *parent, const char *fmt, unsigned int flags)
|
|
{
|
|
struct state_sdl *s = new state_sdl(parent);
|
|
int ret;
|
|
const SDL_VideoInfo *video_info;
|
|
|
|
#ifdef HAVE_LINUX
|
|
x11_enter_thread();
|
|
#endif
|
|
|
|
if (fmt != NULL) {
|
|
if (strcmp(fmt, "help") == 0) {
|
|
show_help();
|
|
delete s;
|
|
return &display_init_noerr;
|
|
}
|
|
|
|
char *tmp = strdup(fmt);
|
|
char *ptr = tmp;
|
|
char *tok;
|
|
char *save_ptr = NULL;
|
|
|
|
while((tok = strtok_r(ptr, ":", &save_ptr)))
|
|
{
|
|
if (strcmp(tok, "fs") == 0) {
|
|
s->fs = 1;
|
|
} else if (strcmp(tok, "d") == 0) {
|
|
s->deinterlace = 1;
|
|
} else if (strcmp(tok, "nodecorate") == 0) {
|
|
s->nodecorate = true;
|
|
}
|
|
ptr = NULL;
|
|
}
|
|
|
|
free (tmp);
|
|
}
|
|
|
|
#ifdef HAVE_MACOSX
|
|
/* Startup function to call when running Cocoa code from a Carbon application.
|
|
* Whatever the fuck that means.
|
|
* Avoids uncaught exception (1002) when creating CGSWindow */
|
|
NSApplicationLoad();
|
|
s->autorelease_pool = autorelease_pool_allocate();
|
|
#endif
|
|
|
|
ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
|
|
|
if (ret < 0) {
|
|
printf("Unable to initialize SDL.\n");
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
|
|
video_info = SDL_GetVideoInfo();
|
|
s->screen_w = video_info->current_w;
|
|
s->screen_h = video_info->current_h;
|
|
|
|
SDL_SysWMinfo info;
|
|
memset(&info, 0, sizeof(SDL_SysWMinfo));
|
|
ret = SDL_GetWMInfo(&info);
|
|
#ifdef HAVE_LINUX
|
|
if (ret == 1) {
|
|
x11_set_display(info.info.x11.display);
|
|
} else if (ret == 0) {
|
|
fprintf(stderr, "[SDL] Warning: SDL_GetWMInfo unimplemented\n");
|
|
} else if (ret == -1) {
|
|
fprintf(stderr, "[SDL] Warning: SDL_GetWMInfo failure: %s\n", SDL_GetError());
|
|
} else abort();
|
|
#endif
|
|
|
|
loadSplashscreen(s);
|
|
|
|
if(flags & DISPLAY_FLAG_AUDIO_EMBEDDED) {
|
|
s->play_audio = TRUE;
|
|
configure_audio(s);
|
|
} else {
|
|
s->play_audio = FALSE;
|
|
}
|
|
|
|
return (void *)s;
|
|
}
|
|
|
|
void display_sdl_done(void *state)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
|
|
assert(s->magic == MAGIC_SDL);
|
|
|
|
cleanup_screen(s);
|
|
|
|
/*FIXME: free all the stuff */
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
SDL_Quit();
|
|
#ifdef HAVE_MACOSX
|
|
autorelease_pool_destroy(s->autorelease_pool);
|
|
#endif
|
|
|
|
while (s->free_frame_queue.size() > 0) {
|
|
struct video_frame *buffer = s->free_frame_queue.front();
|
|
s->free_frame_queue.pop();
|
|
vf_free(buffer);
|
|
}
|
|
|
|
delete s;
|
|
}
|
|
|
|
struct video_frame *display_sdl_getf(void *state)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
assert(s->magic == MAGIC_SDL);
|
|
|
|
lock_guard<mutex> lock(s->lock);
|
|
|
|
while (s->free_frame_queue.size() > 0) {
|
|
struct video_frame *buffer = s->free_frame_queue.front();
|
|
s->free_frame_queue.pop();
|
|
if (video_desc_eq(video_desc_from_frame(buffer), s->current_desc)) {
|
|
return buffer;
|
|
} else {
|
|
vf_free(buffer);
|
|
}
|
|
}
|
|
|
|
return vf_alloc_desc_data(s->current_desc);
|
|
}
|
|
|
|
int display_sdl_putf(void *state, struct video_frame *frame, int nonblock)
|
|
{
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
|
|
assert(s->magic == MAGIC_SDL);
|
|
|
|
if (!frame) {
|
|
s->should_exit = true;
|
|
return 0;
|
|
}
|
|
|
|
if (nonblock == PUTF_DISCARD) {
|
|
vf_free(frame);
|
|
return 0;
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lk(s->lock);
|
|
if (s->frame_queue.size() >= MAX_BUFFER_SIZE && nonblock == PUTF_NONBLOCK) {
|
|
vf_free(frame);
|
|
printf("1 frame(s) dropped!\n");
|
|
return 1;
|
|
}
|
|
s->frame_consumed_cv.wait(lk, [s]{return s->frame_queue.size() < MAX_BUFFER_SIZE;});
|
|
s->frame_queue.push(frame);
|
|
lk.unlock();
|
|
s->frame_ready_cv.notify_one();
|
|
|
|
return 0;
|
|
}
|
|
|
|
display_type_t *display_sdl_probe(void)
|
|
{
|
|
display_type_t *dt;
|
|
|
|
dt = (display_type_t *) malloc(sizeof(display_type_t));
|
|
if (dt != NULL) {
|
|
dt->id = DISPLAY_SDL_ID;
|
|
dt->name = "sdl";
|
|
dt->description = "SDL";
|
|
}
|
|
return dt;
|
|
}
|
|
|
|
int display_sdl_get_property(void *state, int property, void *val, size_t *len)
|
|
{
|
|
UNUSED(state);
|
|
codec_t codecs[] = {UYVY, YUYV, RGBA, RGB};
|
|
|
|
switch (property) {
|
|
case DISPLAY_PROPERTY_CODECS:
|
|
if(sizeof(codecs) <= *len) {
|
|
memcpy(val, codecs, sizeof(codecs));
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
*len = sizeof(codecs);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len) {
|
|
struct state_sdl *s = (struct state_sdl *)userdata;
|
|
if (ring_buffer_read(s->audio_buffer, (char *) stream, len) != len)
|
|
{
|
|
fprintf(stderr, "[SDL] audio buffer underflow!!!\n");
|
|
usleep(500);
|
|
}
|
|
}
|
|
|
|
static void configure_audio(struct state_sdl *s)
|
|
{
|
|
s->audio_frame.data = NULL;
|
|
|
|
SDL_Init(SDL_INIT_AUDIO);
|
|
|
|
if(SDL_GetAudioStatus() != SDL_AUDIO_STOPPED) {
|
|
s->play_audio = FALSE;
|
|
fprintf(stderr, "[SDL] Audio init failed - driver is already used (testcard?)\n");
|
|
return;
|
|
}
|
|
|
|
s->audio_buffer = ring_buffer_init(1<<20);
|
|
}
|
|
|
|
int display_sdl_reconfigure_audio(void *state, int quant_samples, int channels,
|
|
int sample_rate) {
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
SDL_AudioSpec desired, obtained;
|
|
int sample_type;
|
|
|
|
s->audio_frame.bps = quant_samples / 8;
|
|
s->audio_frame.sample_rate = sample_rate;
|
|
s->audio_frame.ch_count = channels;
|
|
|
|
if(s->audio_frame.data != NULL) {
|
|
free(s->audio_frame.data);
|
|
SDL_CloseAudio();
|
|
}
|
|
|
|
if(quant_samples % 8 != 0) {
|
|
fprintf(stderr, "[SDL] audio format isn't supported: "
|
|
"channels: %d, samples: %d, sample rate: %d\n",
|
|
channels, quant_samples, sample_rate);
|
|
goto error;
|
|
}
|
|
switch(quant_samples) {
|
|
case 8:
|
|
sample_type = AUDIO_S8;
|
|
break;
|
|
case 16:
|
|
sample_type = AUDIO_S16LSB;
|
|
break;
|
|
/* TO enable in sdl 1.3
|
|
* case 32:
|
|
sample_type = AUDIO_S32;
|
|
break; */
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
desired.freq=sample_rate;
|
|
desired.format=sample_type;
|
|
desired.channels=channels;
|
|
|
|
/* Large audio buffer reduces risk of dropouts but increases response time */
|
|
desired.samples=1024;
|
|
|
|
/* Our callback function */
|
|
desired.callback=sdl_audio_callback;
|
|
desired.userdata=s;
|
|
|
|
|
|
/* Open the audio device */
|
|
if ( SDL_OpenAudio(&desired, &obtained) < 0 ){
|
|
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
|
|
goto error;
|
|
}
|
|
|
|
s->audio_frame.max_size = 5 * (quant_samples / 8) * channels *
|
|
sample_rate;
|
|
s->audio_frame.data = (char *) malloc (s->audio_frame.max_size);
|
|
|
|
/* Start playing */
|
|
SDL_PauseAudio(0);
|
|
|
|
return TRUE;
|
|
error:
|
|
s->play_audio = FALSE;
|
|
s->audio_frame.max_size = 0;
|
|
s->audio_frame.data = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
void display_sdl_put_audio_frame(void *state, struct audio_frame *frame) {
|
|
struct state_sdl *s = (struct state_sdl *)state;
|
|
char *tmp;
|
|
|
|
if(!s->play_audio)
|
|
return;
|
|
|
|
if(frame->bps == 4 || frame->bps == 3) {
|
|
tmp = (char *) malloc(frame->data_len / frame->bps * 2);
|
|
change_bps(tmp, 2, frame->data, frame->bps, frame->data_len);
|
|
ring_buffer_write(s->audio_buffer, tmp, frame->bps * 2);
|
|
free(tmp);
|
|
} else {
|
|
ring_buffer_write(s->audio_buffer, frame->data, frame->data_len);
|
|
}
|
|
}
|
|
|