/* Copyright (C) 2009 Red Hat, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define GL_GLEXT_PROTOTYPES #include "common.h" #include "canvas.h" #include "red_pixmap.h" #ifdef USE_OGL #include "red_pixmap_gl.h" #endif #include "debug.h" #include "utils.h" #include "common.h" #include "display_channel.h" #include "application.h" #include "screen.h" #ifdef USE_OGL #include "red_gl_canvas.h" #endif #include "red_cairo_canvas.h" #include "red_client.h" #include "utils.h" #include "debug.h" #ifdef WIN32 #include "red_gdi_canvas.h" #endif #include "platform_utils.h" #include "ffmpeg_inc.h" static Mutex avcodec_mutex; class SetModeEvent: public SyncEvent { public: SetModeEvent(DisplayChannel& channel, int width, int height, int depth) : _channel (channel) , _width (width) , _height (height) , _depth (depth) { } virtual void do_responce(Application& application) { _channel.destroy_canvas(); _channel.screen()->set_mode(_width, _height, _depth); _channel.create_canvas(application.get_canvas_types(), _width, _height, _depth); } private: DisplayChannel& _channel; int _width; int _height; int _depth; }; class DisplayMarkEvent: public Event { public: DisplayMarkEvent(int screen_id) : _screen_id (screen_id) { } virtual void responce(Application& application) { application.hide_splash(_screen_id); } private: int _screen_id; }; #define CLIP_ARRAY_SIZE 1500 #define CLIP_ARRAY_SHIFT 500 static uint8_t clip_array[CLIP_ARRAY_SIZE]; static uint8_t *clip_zero = &clip_array[CLIP_ARRAY_SHIFT]; static void init_clip_array() { int i; for (i = 0; i < CLIP_ARRAY_SHIFT; i++) { clip_array[i] = 0; } for (i = CLIP_ARRAY_SHIFT + 256; i < CLIP_ARRAY_SIZE; i++) { clip_array[i] = 0xff; } for (i = 0; i < 256; i++) { clip_zero[i] = i; } } #define CLIP(val) clip_zero[(int)(val)] #define R(pixel) (((uint8_t*)(pixel))[0]) #define G(pixel) (((uint8_t*)(pixel))[1]) #define B(pixel) (((uint8_t*)(pixel))[2]) #define YUV_TO_RGB(pixel, y, u , v) { \ int Y = (y) - 16; \ int U = (u) - 128; \ int V = (v) - 128; \ R(pixel) = CLIP((298 * Y + 409 * V + 128) >> 8); \ G(pixel) = CLIP((298 * Y - 100 * U - 208 * V + 128) >> 8); \ B(pixel) = CLIP((298 * Y + 516 * U + 128) >> 8); \ } static inline void yuv420_to_rgb(AVFrame* frame, uint8_t* data, uint32_t width, uint32_t height, int stride, int top_down) { ASSERT(width % 2 == 0); ASSERT(height % 2 == 0); if (top_down) { data += stride * height - 1; stride = -stride; } // B = 1.164(Y - 16) + 2.018(U - 128) // G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) // R = 1.164(Y - 16) + 1.596(V - 128) uint8_t* y_line = frame->data[0]; uint8_t* u = frame->data[1]; uint8_t* v = frame->data[2]; uint32_t y_stride = frame->linesize[0]; uint32_t u_stride = frame->linesize[1]; uint32_t v_stride = frame->linesize[2]; for (unsigned int i = 0; i < height / 2; i++) { uint8_t* now = data; uint8_t* y = y_line; for (unsigned int j = 0; j < width / 2; j++) { YUV_TO_RGB(now, *y, u[j], v[j]); YUV_TO_RGB(now + sizeof(uint32_t), *(y + 1), u[j], v[j]); YUV_TO_RGB(now + stride, *(y + y_stride), u[j], v[j]); YUV_TO_RGB(now + stride + sizeof(uint32_t), *(y + y_stride + 1), u[j], v[j]); y += 2; now += 2 * sizeof(uint32_t); } data += stride * 2; y_line += y_stride * 2; u += u_stride; v += v_stride; } } #define MAX_VIDEO_FRAMES 30 #define MAX_OVER 15 #define MAX_UNDER -15 class VideoStream { public: VideoStream(RedClient& client, Canvas& canvas, DisplayChannel& channel, uint32_t codec_type, bool top_down, uint32_t stream_width, uint32_t stream_height, uint32_t src_width, uint32_t src_height, Rect* dest, int clip_type, uint32_t num_clip_rects, Rect* clip_rects); ~VideoStream(); void push_data(uint32_t mm_time, uint32_t length, uint8_t* data, uint32_t ped_size); void set_clip(int type, uint32_t num_clip_rects, Rect* clip_rects); const Rect& get_dest() {return _dest;} void handle_update_mark(uint64_t update_mark); uint32_t handle_timer_update(uint32_t now); static void init(); private: void free_frame(uint32_t frame_index); void release_all_bufs(); void remove_dead_frames(uint32_t mm_time); uint32_t alloc_frame_slot(); void maintenance(); void drop_one_frame(); uint32_t frame_slot(uint32_t frame_index) { return frame_index % MAX_VIDEO_FRAMES;} static bool is_time_to_display(uint32_t now, uint32_t frame_time); private: RedClient& _client; Canvas& _canvas; DisplayChannel& _channel; AVCodecContext* _ctx; AVFrame* _frame; int _stream_width; int _stream_height; int _stride; bool _top_down; Rect _dest; QRegion _clip_region; QRegion* _clip; struct VideoFrame { uint32_t mm_time; uint32_t compressed_data_size; uint8_t* compressed_data; }; uint32_t _frames_head; uint32_t _frames_tail; uint32_t _kill_mark; VideoFrame _frames[MAX_VIDEO_FRAMES]; #ifdef WIN32 HBITMAP _prev_bitmap; HDC _dc; #endif uint8_t *_uncompressed_data; PixmapHeader _pixmap; uint64_t _update_mark; uint32_t _update_time; public: VideoStream* next; }; #ifdef WIN32 static int create_bitmap(HDC *dc, HBITMAP *prev_bitmap, uint8_t **data, int *nstride, int width, int height) { HBITMAP bitmap; struct { BITMAPINFO inf; RGBQUAD palette[255]; } bitmap_info; memset(&bitmap_info, 0, sizeof(bitmap_info)); bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader); bitmap_info.inf.bmiHeader.biWidth = width; bitmap_info.inf.bmiHeader.biHeight = height; bitmap_info.inf.bmiHeader.biPlanes = 1; bitmap_info.inf.bmiHeader.biBitCount = 32; bitmap_info.inf.bmiHeader.biCompression = BI_RGB; *nstride = width * 4; *dc = create_compatible_dc(); if (!*dc) { return 0; } bitmap = CreateDIBSection(*dc, &bitmap_info.inf, 0, (void **)data, NULL, 0); if (!bitmap) { DeleteObject(*dc); return 0; } *prev_bitmap = (HBITMAP)SelectObject(*dc, bitmap); return 1; } #endif VideoStream::VideoStream(RedClient& client, Canvas& canvas, DisplayChannel& channel, uint32_t codec_type, bool top_down, uint32_t stream_width, uint32_t stream_height, uint32_t src_width, uint32_t src_height, Rect* dest, int clip_type, uint32_t num_clip_rects, Rect* clip_rects) : _client (client) , _canvas (canvas) , _channel (channel) , _ctx (NULL) , _frame (NULL) , _stream_width (stream_width) , _stream_height (stream_height) , _stride (stream_width * sizeof(uint32_t)) , _top_down (top_down) , _dest (*dest) , _clip (NULL) , _frames_head (0) , _frames_tail (0) , _uncompressed_data (NULL) , _update_mark (0) , _update_time (0) , next (NULL) { AVCodecContext* ctx = NULL; AVCodec* codec; AVFrame* frame = NULL; enum CodecID type; memset(_frames, 0, sizeof(_frames)); region_init(&_clip_region); switch (codec_type) { case RED_VIDEO_CODEC_TYPE_MJPEG: type = CODEC_ID_MJPEG; break; default: THROW("invalid vide codec type %u", codec_type); } if (!(codec = avcodec_find_decoder(type))) { THROW("can't find codec %u", type); } if (!(ctx = avcodec_alloc_context())) { THROW("alloc codec ctx failed"); } try { if (!(frame = avcodec_alloc_frame())) { THROW("alloc frame failed"); } #ifdef WIN32 if (!create_bitmap(&_dc, &_prev_bitmap, &_uncompressed_data, &_stride, stream_width, stream_height)) { THROW("create_bitmap failed"); } #else _uncompressed_data = new uint8_t[_stride * stream_height]; #endif _pixmap.width = src_width; _pixmap.height = src_height; if (top_down) { _pixmap.data = _uncompressed_data; _pixmap.stride = _stride; } else { #ifdef WIN32 SetViewportOrgEx(_dc, 0, stream_height - src_height, NULL); #endif _pixmap.data = _uncompressed_data + _stride * (src_height - 1); _pixmap.stride = -_stride; } set_clip(clip_type, num_clip_rects, clip_rects); Lock lock(avcodec_mutex); if (avcodec_open(ctx, codec) < 0) { THROW("open avcodec failed"); } } catch (...) { av_free(frame); av_free(ctx); release_all_bufs(); throw; } _frame = frame; _ctx = ctx; } VideoStream::~VideoStream() { if (_ctx) { Lock lock(avcodec_mutex); avcodec_close(_ctx); av_free(_ctx); av_free(_frame); } release_all_bufs(); region_destroy(&_clip_region); } void VideoStream::release_all_bufs() { for (int i = 0; i < MAX_VIDEO_FRAMES; i++) { delete[] _frames[i].compressed_data; } #ifdef WIN32 if (_dc) { HBITMAP bitmap = (HBITMAP)SelectObject(_dc, _prev_bitmap); DeleteObject(bitmap); DeleteObject(_dc); } #else delete[] _uncompressed_data; #endif } void VideoStream::free_frame(uint32_t frame_index) { int slot = frame_slot(frame_index); delete[] _frames[slot].compressed_data; _frames[slot].compressed_data = NULL; } void VideoStream::remove_dead_frames(uint32_t mm_time) { while (_frames_head != _frames_tail) { if (int(_frames[frame_slot(_frames_tail)].mm_time - mm_time) >= MAX_UNDER) { return; } free_frame(_frames_tail); _frames_tail++; } } void VideoStream::drop_one_frame() { ASSERT(MAX_VIDEO_FRAMES > 2 && (_frames_head - _frames_tail) == MAX_VIDEO_FRAMES); int frame_index = _frames_head - _kill_mark++ % (MAX_VIDEO_FRAMES - 2) - 2; free_frame(frame_index); while (frame_index != _frames_tail) { --frame_index; _frames[frame_slot(frame_index + 1)] = _frames[frame_slot(frame_index)]; } _frames_tail++; } bool VideoStream::is_time_to_display(uint32_t now, uint32_t frame_time) { int delta = frame_time - now; return delta <= MAX_OVER && delta >= MAX_UNDER; } void VideoStream::maintenance() { uint32_t mm_time = _client.get_mm_time(); remove_dead_frames(mm_time); if (!_update_mark && !_update_time && _frames_head != _frames_tail) { VideoFrame* tail = &_frames[frame_slot(_frames_tail)]; ASSERT(tail->compressed_data); uint8_t* data = tail->compressed_data; uint32_t length = tail->compressed_data_size; int got_picture = 0; while (length > 0) { int n = avcodec_decode_video(_ctx, _frame, &got_picture, data, length); if (n < 0) { THROW("decoding eror"); } if (got_picture) { yuv420_to_rgb(_frame, _uncompressed_data, _stream_width, _stream_height, _stride, _top_down); ASSERT(length - n == 0); break; } length -= n; data += n; } if (got_picture) { #ifdef WIN32 _canvas.put_image(_dc, _pixmap, _dest, _clip); #else _canvas.put_image(_pixmap, _dest, _clip); #endif if (is_time_to_display(mm_time, tail->mm_time)) { _update_mark = _channel.invalidate(_dest, true); Platform::yield(); } else { _update_time = tail->mm_time; _channel.stream_update_request(_update_time); } } free_frame(_frames_tail++); } } uint32_t VideoStream::handle_timer_update(uint32_t now) { if (!_update_time) { return 0; } if (is_time_to_display(now, _update_time)) { _update_time = 0; _update_mark = _channel.invalidate(_dest, true); } else if ((int)(_update_time - now) < 0) { DBG(0, "to late"); _update_time = 0; } return _update_time; } void VideoStream::handle_update_mark(uint64_t update_mark) { if (!_update_mark || update_mark < _update_mark) { return; } _update_mark = 0; maintenance(); } uint32_t VideoStream::alloc_frame_slot() { if ((_frames_head - _frames_tail) == MAX_VIDEO_FRAMES) { drop_one_frame(); } return frame_slot(_frames_head++); } void VideoStream::push_data(uint32_t mm_time, uint32_t length, uint8_t* data, uint32_t ped_size) { if (ped_size < FF_INPUT_BUFFER_PADDING_SIZE) { THROW("insufficieant pedding"); } maintenance(); uint32_t frame_slot = alloc_frame_slot(); _frames[frame_slot].compressed_data = new uint8_t[length]; memcpy(_frames[frame_slot].compressed_data, data, length); _frames[frame_slot].compressed_data_size = length; _frames[frame_slot].mm_time = mm_time ? mm_time : 1; maintenance(); } void VideoStream::set_clip(int type, uint32_t num_clip_rects, Rect* clip_rects) { if (type == CLIP_TYPE_NONE) { _clip = NULL; return; } ASSERT(type == CLIP_TYPE_RECTS) region_clear(&_clip_region); for (unsigned int i = 0; i < num_clip_rects; i++) { region_add(&_clip_region, &clip_rects[i]); } _clip = &_clip_region; } void VideoStream::init() { avcodec_init(); avcodec_register_all(); init_clip_array(); } class AutoVStreamInit { public: AutoVStreamInit() { VideoStream::init(); } }; static AutoVStreamInit auto_init; StreamsTrigger::StreamsTrigger(DisplayChannel& channel) : _channel (channel) { } void StreamsTrigger::on_event() { _channel.on_streams_trigger(); } #ifdef USE_OGL GLInterruptRecreate::GLInterruptRecreate(DisplayChannel& channel) : _channel (channel) { } void GLInterruptRecreate::trigger() { Lock lock(_lock); EventsLoop::Trigger::trigger(); _cond.wait(lock); } void GLInterruptRecreate::on_event() { Lock lock(_lock); _channel.recreate_ogl_context_interrupt(); _cond.notify_one(); } #endif void InterruptUpdate::on_event() { _channel.update_interrupt(); } InterruptUpdate::InterruptUpdate(DisplayChannel& channel) : _channel (channel) { } class DisplayHandler: public MessageHandlerImp { public: DisplayHandler(DisplayChannel& channel) : MessageHandlerImp(channel) {} }; DisplayChannel::DisplayChannel(RedClient& client, uint32_t id, PixmapCache& pixmap_cache, GlzDecoderWindow& glz_window) : RedChannel(client, RED_CHANNEL_DISPLAY, id, new DisplayHandler(*this), Platform::PRIORITY_LOW) , ScreenLayer (SCREEN_LAYER_DISPLAY, true) , _canvas (NULL) , _pixmap_cache (pixmap_cache) , _glz_window (glz_window) , _mark (false) , _update_mark (0) , _streams_timer (INVALID_TIMER) , _next_timer_time (0) , _active_streams (NULL) , _streams_trigger (*this) #ifdef USE_OGL , _gl_interrupt_recreate (*this) #endif , _interrupt_update (*this) { DisplayHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_MIGRATE, &DisplayChannel::handle_migrate, 0); handler->set_handler(RED_SET_ACK, &DisplayChannel::handle_set_ack, sizeof(RedSetAck)); handler->set_handler(RED_PING, &DisplayChannel::handle_ping, sizeof(RedPing)); handler->set_handler(RED_WAIT_FOR_CHANNELS, &DisplayChannel::handle_wait_for_channels, sizeof(RedWaitForChannels)); handler->set_handler(RED_DISCONNECTING, &DisplayChannel::handle_disconnect, sizeof(RedDisconnect)); handler->set_handler(RED_NOTIFY, &DisplayChannel::handle_notify, sizeof(RedNotify)); handler->set_handler(RED_DISPLAY_MODE, &DisplayChannel::handle_mode, sizeof(RedMode)); handler->set_handler(RED_DISPLAY_MARK, &DisplayChannel::handle_mark, 0); handler->set_handler(RED_DISPLAY_RESET, &DisplayChannel::handle_reset, 0); handler->set_handler(RED_DISPLAY_INVAL_LIST, &DisplayChannel::handle_inval_list, sizeof(RedResorceList)); handler->set_handler(RED_DISPLAY_INVAL_ALL_PIXMAPS, &DisplayChannel::handle_inval_all_pixmaps, sizeof(RedWaitForChannels)); handler->set_handler(RED_DISPLAY_INVAL_PALETTE, &DisplayChannel::handle_inval_palette, sizeof(RedInvalOne)); handler->set_handler(RED_DISPLAY_INVAL_ALL_PALETTES, &DisplayChannel::handle_inval_all_palettes, 0); handler->set_handler(RED_DISPLAY_STREAM_CREATE, &DisplayChannel::handle_stream_create, sizeof(RedStreamCreate)); handler->set_handler(RED_DISPLAY_STREAM_CLIP, &DisplayChannel::handle_stream_clip, sizeof(RedStreamClip)); handler->set_handler(RED_DISPLAY_STREAM_DESTROY, &DisplayChannel::handle_stream_destroy, sizeof(RedStreamDestroy)); handler->set_handler(RED_DISPLAY_STREAM_DESTROY_ALL, &DisplayChannel::handle_stream_destroy_all, 0); get_events_loop().add_trigger(_streams_trigger); #ifdef USE_OGL get_events_loop().add_trigger(_gl_interrupt_recreate); #endif get_events_loop().add_trigger(_interrupt_update); } DisplayChannel::~DisplayChannel() { if (screen()) { screen()->set_update_interrupt_trigger(NULL); } destroy_strams(); } void DisplayChannel::destroy_strams() { Lock lock(_streams_lock); for (unsigned int i = 0; i < _streams.size(); i++) { delete _streams[i]; _streams[i] = NULL; } _active_streams = NULL; } void DisplayChannel::set_draw_handlers() { DisplayHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_DISPLAY_COPY_BITS, &DisplayChannel::handle_copy_bits, sizeof(RedCopyBits)); handler->set_handler(RED_DISPLAY_DRAW_FILL, &DisplayChannel::handle_draw_fill, sizeof(RedFill)); handler->set_handler(RED_DISPLAY_DRAW_OPAQUE, &DisplayChannel::handle_draw_opaque, sizeof(RedOpaque)); handler->set_handler(RED_DISPLAY_DRAW_COPY, &DisplayChannel::handle_draw_copy, sizeof(RedCopy)); handler->set_handler(RED_DISPLAY_DRAW_BLEND, &DisplayChannel::handle_draw_blend, sizeof(RedBlend)); handler->set_handler(RED_DISPLAY_DRAW_BLACKNESS, &DisplayChannel::handle_draw_blackness, sizeof(RedBlackness)); handler->set_handler(RED_DISPLAY_DRAW_WHITENESS, &DisplayChannel::handle_draw_whiteness, sizeof(RedWhiteness)); handler->set_handler(RED_DISPLAY_DRAW_INVERS, &DisplayChannel::handle_draw_invers, sizeof(RedInvers)); handler->set_handler(RED_DISPLAY_DRAW_ROP3, &DisplayChannel::handle_draw_rop3, sizeof(RedRop3)); handler->set_handler(RED_DISPLAY_DRAW_STROKE, &DisplayChannel::handle_draw_stroke, sizeof(RedStroke)); handler->set_handler(RED_DISPLAY_DRAW_TEXT, &DisplayChannel::handle_draw_text, sizeof(RedText)); handler->set_handler(RED_DISPLAY_DRAW_TRANSPARENT, &DisplayChannel::handle_draw_transparent, sizeof(RedTransparent)); handler->set_handler(RED_DISPLAY_DRAW_ALPHA_BLEND, &DisplayChannel::handle_draw_alpha_blend, sizeof(RedAlphaBlend)); handler->set_handler(RED_DISPLAY_STREAM_DATA, &DisplayChannel::handle_stream_data, sizeof(RedStreamData)); } void DisplayChannel::clear_draw_handlers() { DisplayHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_DISPLAY_COPY_BITS, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_FILL, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_OPAQUE, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_COPY, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_BLEND, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_BLACKNESS, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_WHITENESS, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_INVERS, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_ROP3, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_STROKE, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_TEXT, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_TRANSPARENT, NULL, 0); handler->set_handler(RED_DISPLAY_DRAW_ALPHA_BLEND, NULL, 0); handler->set_handler(RED_DISPLAY_STREAM_DATA, NULL, 0); } void DisplayChannel::copy_pixels(const QRegion& dest_region, const PixmapHeader &dest_pixmap) { if (!_canvas.get()) { return; } _canvas->copy_pixels(dest_region, NULL, &dest_pixmap); } #ifdef USE_OGL void DisplayChannel::recreate_ogl_context_interrupt() { Canvas* canvas = _canvas.release(); if (canvas) { ((GCanvas *)canvas)->textures_lost(); delete canvas; } if (!create_ogl_canvas(_x_res, _y_res, _depth, 0, _rendertype)) { THROW("create_ogl_canvas failed"); } } void DisplayChannel::recreate_ogl_context() { if (_canvas.get() && _canvas->get_pixmap_type() == CANVAS_TYPE_GL) { if (!screen()->need_recreate_context_gl()) { _gl_interrupt_recreate.trigger(); } } } #endif void DisplayChannel::update_interrupt() { if (!_canvas.get() || !screen()) { return; } #ifdef USE_OGL if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) { ((GCanvas *)(_canvas.get()))->pre_gl_copy(); } #endif screen()->update(); #ifdef USE_OGL if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) { ((GCanvas *)(_canvas.get()))->post_gl_copy(); } #endif } #ifdef USE_OGL void DisplayChannel::pre_migrate() { } void DisplayChannel::post_migrate() { #ifdef USE_OGL if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) { _gl_interrupt_recreate.trigger(); } #endif } #endif void DisplayChannel::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc) { if (!_canvas.get()) { return; } _canvas->copy_pixels(dest_region, dest_dc); } class CreateTimerEvent: public SyncEvent { public: CreateTimerEvent(timer_proc_t proc, void* user_data) : SyncEvent() , _proc (proc) , _user_data (user_data) , _timer (INVALID_TIMER) { } virtual ~CreateTimerEvent() { ASSERT(_timer == INVALID_TIMER); } virtual void do_responce(Application& application) { if ((_timer = Platform::create_interval_timer(_proc, _user_data)) == INVALID_TIMER) { THROW("create timer failed"); } } TimerID release() { TimerID ret = _timer; _timer = INVALID_TIMER; return ret;} private: timer_proc_t _proc; void* _user_data; TimerID _timer; }; class DestroyTimerEvent: public Event { public: DestroyTimerEvent(TimerID timer) : _timer (timer) {} virtual void responce(Application& application) { Platform::destroy_interval_timer(_timer); } private: TimerID _timer; }; class ActivateTimerEvent: public Event { public: ActivateTimerEvent(DisplayChannel& channel) : _channel (channel) { } virtual void responce(Application& application) { _channel.activate_streams_timer(); } private: DisplayChannel& _channel; }; void DisplayChannel::streams_timer_callback(void* opaque, TimerID timer) { ((DisplayChannel *)opaque)->streams_time(); } #define RESET_TIMEOUT (1000 * 5) void DisplayChannel::reset_timer_callback(void* opaque, TimerID timer) { ((RedScreen *)opaque)->unref(); Platform::destroy_interval_timer(timer); } void DisplayChannel::on_connect() { Message* message = new Message(REDC_DISPLAY_INIT, sizeof(RedcDisplayInit)); RedcDisplayInit* init = (RedcDisplayInit*)message->data(); init->pixmap_cache_id = 1; init->pixmap_cache_size = get_client().get_pixmap_cache_size(); init->glz_dictionary_id = 1; init->glz_dictionary_window_size = get_client().get_glz_window_size(); post_message(message); AutoRef event(new CreateTimerEvent(streams_timer_callback, this)); get_client().push_event(*event); (*event)->wait(); _streams_timer = (*event)->release(); if (!(*event)->success()) { THROW("create timer failed"); } } void DisplayChannel::on_disconnect() { if (_canvas.get()) { _canvas->clear(); } if (screen()) { screen()->set_update_interrupt_trigger(NULL); } detach_from_screen(get_client().get_application()); AutoRef event(new DestroyTimerEvent(_streams_timer)); get_client().push_event(*event); AutoRef sync_event(new SyncEvent()); get_client().push_event(*sync_event); (*sync_event)->wait(); } bool DisplayChannel::create_cairo_canvas(int width, int height, int depth) { try { std::auto_ptr canvas(new CCanvas(_pixmap_cache, _palette_cache, _glz_window)); canvas->set_mode(width, height, depth, screen()->get_window()); _canvas.reset(canvas.release()); LOG_INFO("display %d: using cairo", get_id()); } catch (...) { return false; } return true; } #ifdef USE_OGL bool DisplayChannel::create_ogl_canvas(int width, int height, int depth, bool recreate, RenderType rendertype) { try { RedWindow *win; int ret = 1; std::auto_ptr canvas(new GCanvas(_pixmap_cache, _palette_cache, _glz_window)); win = screen()->get_window(); if (!ret) { return false; } screen()->untouch_context(); canvas->set_mode(width, height, depth, win, rendertype); _canvas.reset(canvas.release()); _rendertype = rendertype; LOG_INFO("display %d: using ogl", get_id()); } catch (...) { return false; } return true; } #endif #ifdef WIN32 bool DisplayChannel::create_gdi_canvas(int width, int height, int depth) { try { std::auto_ptr canvas( new GDICanvas(_pixmap_cache, _palette_cache, _glz_window)); canvas->set_mode(width, height, depth); _canvas.reset(canvas.release()); LOG_INFO("display %d: using gdi", get_id()); } catch (...) { return false; } return true; } #endif void DisplayChannel::destroy_canvas() { Canvas* canvas = _canvas.release(); if (canvas) { delete canvas; } } void DisplayChannel::create_canvas(const std::vector& canvas_types, int width, int height, int depth) { #ifdef USE_OGL bool recreate = true; #endif unsigned int i; clear_draw_handlers(); #ifdef USE_OGL if (screen()->need_recreate_context_gl()) { recreate = false; } #endif screen()->set_update_interrupt_trigger(NULL); for (i = 0; i < canvas_types.size(); i++) { if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(width, height, depth)) { break; } #ifdef USE_OGL if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(width, height, depth, recreate, RENDER_TYPE_FBO)) { break; } if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(width, height, depth, recreate, RENDER_TYPE_PBUFF)) { break; } #endif #ifdef WIN32 if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(width, height, depth)) { break; } #endif } if (i == canvas_types.size()) { THROW("create canvas failed"); } set_draw_handlers(); } void DisplayChannel::handle_mode(RedPeer::InMessage* message) { RedMode *mode = (RedMode *)message->data(); _mark = false; attach_to_screen(get_client().get_application(), get_id()); clear_area(); #ifdef USE_OGL if (screen()) { if (_canvas.get()) { if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) { screen()->unset_type_gl(); //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } } } #endif AutoRef event(new SetModeEvent(*this, mode->x_res, mode->y_res, mode->bits)); get_client().push_event(*event); (*event)->wait(); if (!(*event)->success()) { THROW("set mode failed"); } _x_res = mode->x_res; _y_res = mode->y_res; _depth = mode->bits; #ifdef USE_OGL if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) { screen()->set_update_interrupt_trigger(&_interrupt_update); screen()->set_type_gl(); } #endif } void DisplayChannel::handle_mark(RedPeer::InMessage *message) { _mark = true; Rect area; area.top = area.left = 0; area.right = _x_res; area.bottom = _y_res; AutoRef event(new DisplayMarkEvent(get_id())); get_client().push_event(*event); set_rect_area(area); } void DisplayChannel::handle_reset(RedPeer::InMessage *message) { TimerID reset_timer; screen()->set_update_interrupt_trigger(NULL); if (_canvas.get()) { _canvas->clear(); } reset_timer = Platform::create_interval_timer(reset_timer_callback, screen()->ref()); if (reset_timer == INVALID_TIMER) { THROW("invalid reset timer"); } detach_from_screen(get_client().get_application()); _palette_cache.clear(); Platform::activate_interval_timer(reset_timer, RESET_TIMEOUT); } void DisplayChannel::handle_inval_list(RedPeer::InMessage* message) { RedResorceList *inval_list = (RedResorceList *)message->data(); if (message->size() < sizeof(*inval_list) + inval_list->count * sizeof(inval_list->resorces[0])) { THROW("access violation"); } for (int i = 0; i < inval_list->count; i++) { if (inval_list->resorces[i].type != RED_RES_TYPE_PIXMAP) { THROW("invalid res type"); } _pixmap_cache.remove(inval_list->resorces[i].id); } } void DisplayChannel::handle_inval_all_pixmaps(RedPeer::InMessage* message) { RedWaitForChannels *wait = (RedWaitForChannels *)message->data(); if (message->size() < sizeof(*wait) + wait->wait_count * sizeof(wait->wait_list[0])) { THROW("access violation"); } get_client().wait_for_channels(wait->wait_count, wait->wait_list); _pixmap_cache.clear(); } void DisplayChannel::handle_inval_palette(RedPeer::InMessage* message) { RedInvalOne* inval = (RedInvalOne*)message->data(); _palette_cache.remove(inval->id); } void DisplayChannel::handle_inval_all_palettes(RedPeer::InMessage* message) { _palette_cache.clear(); } void DisplayChannel::set_clip_rects(const Clip& clip, uint32_t& num_clip_rects, Rect*& clip_rects, unsigned long addr_offset, uint8_t *min, uint8_t *max) { switch (clip.type) { case CLIP_TYPE_RECTS: { uint32_t* n = (uint32_t*)GET_ADDRESS(clip.data + addr_offset); if (n < (uint32_t*)min || n + 1 > (uint32_t*)max) { THROW("access violation"); } num_clip_rects = *n; clip_rects = (Rect *)(n + 1); if (clip_rects + num_clip_rects > (Rect*)max) { THROW("access violation"); } break; } case CLIP_TYPE_NONE: num_clip_rects = 0; clip_rects = NULL; break; case CLIP_TYPE_PATH: THROW("unexpected clip type"); } } void DisplayChannel::handle_stream_create(RedPeer::InMessage* message) { RedStreamCreate* stream_create = (RedStreamCreate*)message->data(); Lock lock(_streams_lock); if (_streams.size() <= stream_create->id) { _streams.resize(stream_create->id + 1); } if (_streams[stream_create->id]) { THROW("stream exist"); } uint32_t num_clip_rects; Rect* clip_rects; set_clip_rects(stream_create->clip, num_clip_rects, clip_rects, (unsigned long)message->data(), (uint8_t*)(stream_create + 1), message->data() + message->size()); _streams[stream_create->id] = new VideoStream(get_client(), *_canvas.get(), *this, stream_create->codec_type, !!(stream_create->flags & STREAM_TOP_DOWN), stream_create->stream_width, stream_create->stream_height, stream_create->src_width, stream_create->src_height, &stream_create->dest, stream_create->clip.type, num_clip_rects, clip_rects); _streams[stream_create->id]->next = _active_streams; _active_streams = _streams[stream_create->id]; } void DisplayChannel::handle_stream_data(RedPeer::InMessage* message) { RedStreamData* stream_data = (RedStreamData*)message->data(); VideoStream* stream; if (stream_data->id >= _streams.size() || !(stream = _streams[stream_data->id])) { THROW("invalid stream"); } if (message->size() < sizeof(RedStreamData) + stream_data->data_size + stream_data->ped_size) { THROW("access violation"); } stream->push_data(stream_data->multi_media_time, stream_data->data_size, stream_data->data, stream_data->ped_size); } void DisplayChannel::handle_stream_clip(RedPeer::InMessage* message) { RedStreamClip* clip_data = (RedStreamClip*)message->data(); VideoStream* stream; uint32_t num_clip_rects; Rect* clip_rects; if (clip_data->id >= _streams.size() || !(stream = _streams[clip_data->id])) { THROW("invalid stream"); } if (message->size() < sizeof(RedStreamClip)) { THROW("access violation"); } set_clip_rects(clip_data->clip, num_clip_rects, clip_rects, (unsigned long)message->data(), (uint8_t*)(clip_data + 1), message->data() + message->size()); Lock lock(_streams_lock); stream->set_clip(clip_data->clip.type, num_clip_rects, clip_rects); } void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message) { RedStreamDestroy* stream_destroy = (RedStreamDestroy*)message->data(); if (stream_destroy->id >= _streams.size() || !_streams[stream_destroy->id]) { THROW("invalid stream"); } Lock lock(_streams_lock); VideoStream **active_stream = &_active_streams; for (;;) { if (!*active_stream) { THROW("not in actibe streams"); } if (*active_stream == _streams[stream_destroy->id]) { *active_stream = _streams[stream_destroy->id]->next; break; } active_stream = &(*active_stream)->next; } delete _streams[stream_destroy->id]; _streams[stream_destroy->id] = NULL; } void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message) { destroy_strams(); } #define PRE_DRAW #define POST_DRAW #define DRAW(type) { \ PRE_DRAW; \ _canvas->draw_##type(*type, message->size()); \ POST_DRAW; \ invalidate(type->base.box, false); \ } void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message) { RedCopyBits* copy_bits = (RedCopyBits*)message->data(); PRE_DRAW; _canvas->copy_bits(*copy_bits, message->size()); POST_DRAW; invalidate(copy_bits->base.box, false); } void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message) { RedFill* fill = (RedFill*)message->data(); DRAW(fill); } void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message) { RedOpaque* opaque = (RedOpaque*)message->data(); DRAW(opaque); } void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message) { RedCopy* copy = (RedCopy*)message->data(); DRAW(copy); } void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message) { RedBlend* blend = (RedBlend*)message->data(); DRAW(blend); } void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message) { RedBlackness* blackness = (RedBlackness*)message->data(); DRAW(blackness); } void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message) { RedWhiteness* whiteness = (RedWhiteness*)message->data(); DRAW(whiteness); } void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message) { RedInvers* invers = (RedInvers*)message->data(); DRAW(invers); } void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message) { RedRop3* rop3 = (RedRop3*)message->data(); DRAW(rop3); } void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message) { RedStroke* stroke = (RedStroke*)message->data(); DRAW(stroke); } void DisplayChannel::handle_draw_text(RedPeer::InMessage* message) { RedText* text = (RedText*)message->data(); DRAW(text); } void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message) { RedTransparent* transparent = (RedTransparent*)message->data(); DRAW(transparent); } void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message) { RedAlphaBlend* alpha_blend = (RedAlphaBlend*)message->data(); DRAW(alpha_blend); } void DisplayChannel::streams_time() { _next_timer_time = 0; Lock lock(_streams_lock); uint32_t mm_time = get_client().get_mm_time(); uint32_t next_time = 0; VideoStream* stream = _active_streams; while (stream) { uint32_t next_frame_time; if ((next_frame_time = stream->handle_timer_update(mm_time))) { if (!next_time || int(next_frame_time - next_time) < 0) { next_time = next_frame_time; } } stream = stream->next; } Lock timer_lock(_timer_lock); mm_time = get_client().get_mm_time(); next_time = mm_time + 15; if (next_time && (!_next_timer_time || int(next_time - _next_timer_time) < 0)) { Platform::activate_interval_timer(_streams_timer, MAX(int(next_time - mm_time), 0)); _next_timer_time = next_time; } else if (!_next_timer_time) { Platform::deactivate_interval_timer(_streams_timer); } timer_lock.unlock(); lock.unlock(); Platform::yield(); } void DisplayChannel::activate_streams_timer() { uint32_t next_time = _next_timer_time; if (!next_time) { return; } int delta = next_time - get_client().get_mm_time(); if (delta <= 0) { streams_time(); } else { Lock timer_lock(_timer_lock); if (!_next_timer_time) { return; } delta = _next_timer_time - get_client().get_mm_time(); Platform::activate_interval_timer(_streams_timer, delta); } } void DisplayChannel::stream_update_request(uint32_t mm_time) { Lock lock(_timer_lock); if (_next_timer_time && int(mm_time - _next_timer_time) > 0) { return; } _next_timer_time = mm_time; lock.unlock(); AutoRef event(new ActivateTimerEvent(*this)); get_client().push_event(*event); } void DisplayChannel::on_update_completion(uint64_t mark) { #ifndef RED64 Lock lock(_mark_lock); #endif _update_mark = mark; #ifndef RED64 lock.unlock(); #endif _streams_trigger.trigger(); } void DisplayChannel::on_streams_trigger() { #ifndef RED64 Lock lock(_mark_lock); #endif uint64_t update_mark = _update_mark; #ifndef RED64 lock.unlock(); #endif VideoStream* stream = _active_streams; while (stream) { stream->handle_update_mark(update_mark); stream = stream->next; } } class DisplayFactory: public ChannelFactory { public: DisplayFactory() : ChannelFactory(RED_CHANNEL_DISPLAY) {} virtual RedChannel* construct(RedClient& client, uint32_t id) { return new DisplayChannel(client, id, client.get_pixmap_cache(), client.get_glz_window()); } }; static DisplayFactory factory; ChannelFactory& DisplayChannel::Factory() { return factory; }