diff options
author | Slávek Banko <slavek.banko@axis.cz> | 2019-02-06 16:56:55 +0100 |
---|---|---|
committer | Slávek Banko <slavek.banko@axis.cz> | 2019-02-06 16:56:55 +0100 |
commit | f3f392caec43b4095bc1d84b315ed7972c13c144 (patch) | |
tree | 5c4ba8b5d38f1ae33de71507c5634a15a0b35bfe /client_examples | |
parent | 8c081c8888bccbf5adfe0fc4ec518e2cbfba9871 (diff) | |
parent | 0a70095271d845d16a3ed17354841b01f33963ad (diff) | |
download | libtdevnc-f3f392caec43b4095bc1d84b315ed7972c13c144.tar.gz libtdevnc-f3f392caec43b4095bc1d84b315ed7972c13c144.zip |
Merge tag 'LibVNCServer-0.9.12'
Signed-off-by: Slávek Banko <slavek.banko@axis.cz>
Diffstat (limited to 'client_examples')
-rw-r--r-- | client_examples/Makefile.am | 38 | ||||
-rw-r--r-- | client_examples/SDLvncviewer.c | 431 | ||||
-rw-r--r-- | client_examples/backchannel.c | 4 | ||||
-rw-r--r-- | client_examples/ppmtest.c | 15 | ||||
-rw-r--r-- | client_examples/scrap.c | 558 | ||||
-rw-r--r-- | client_examples/scrap.h | 18 | ||||
-rw-r--r-- | client_examples/vnc2mpg.c | 688 |
7 files changed, 582 insertions, 1170 deletions
diff --git a/client_examples/Makefile.am b/client_examples/Makefile.am deleted file mode 100644 index 9cb2c32..0000000 --- a/client_examples/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir) -LDADD = ../libvncclient/libvncclient.la @WSOCKLIB@ - -if WITH_FFMPEG -FFMPEG_HOME=@with_ffmpeg@ - -if HAVE_MP3LAME -MP3LAME_LIB=-lmp3lame -endif - -vnc2mpg_CFLAGS=-I$(FFMPEG_HOME)/libavformat -I$(FFMPEG_HOME)/libavcodec -I$(FFMPEG_HOME)/libavutil -vnc2mpg_LDADD=$(LDADD) $(FFMPEG_HOME)/libavformat/libavformat.a $(FFMPEG_HOME)/libavcodec/libavcodec.a $(MP3LAME_LIB) -lm - -FFMPEG_CLIENT=vnc2mpg -endif - -if HAVE_LIBSDL -SDLVIEWER=SDLvncviewer - -SDLvncviewer_CFLAGS=$(SDL_CFLAGS) -SDLvncviewer_SOURCES=SDLvncviewer.c scrap.c scrap.h - -# thanks to autoconf, this looks ugly -SDLvncviewer_LDADD=$(LDADD) $(SDL_LIBS) -endif - -if HAVE_LIBGTK -GTKVIEWER=gtkvncviewer -gtkvncviewer_SOURCES=gtkvncviewer.c -gtkvncviewer_CFLAGS=$(GTK_CFLAGS) -gtkvncviewer_LDADD=$(LDADD) $(GTK_LIBS) -endif - - -noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(GTKVIEWER) $(FFMPEG_CLIENT) backchannel - - - diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c index 8fe6f57..d17b74e 100644 --- a/client_examples/SDLvncviewer.c +++ b/client_examples/SDLvncviewer.c @@ -5,7 +5,6 @@ #include <SDL.h> #include <signal.h> #include <rfb/rfbclient.h> -#include "scrap.h" struct { int sdl; int rfb; } buttonMapping[]={ {1, rfbButton1Mask}, @@ -16,14 +15,22 @@ struct { int sdl; int rfb; } buttonMapping[]={ {0,0} }; +struct { char mask; int bits_stored; } utf8Mapping[]= { + {0b00111111, 6}, + {0b01111111, 7}, + {0b00011111, 5}, + {0b00001111, 4}, + {0b00000111, 3}, + {0,0} +}; + static int enableResizable = 1, viewOnly, listenLoop, buttonMask; -#ifdef SDL_ASYNCBLIT - int sdlFlags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; -#else - int sdlFlags = SDL_HWSURFACE | SDL_HWACCEL; -#endif -static int realWidth, realHeight, bytesPerPixel, rowStride; -static char *sdlPixels; +int sdlFlags; +SDL_Texture *sdlTexture; +SDL_Renderer *sdlRenderer; +SDL_Window *sdlWindow; +/* client's pointer position */ +int x,y; static int rightAltKeyDown, leftAltKeyDown; @@ -32,53 +39,72 @@ static rfbBool resize(rfbClient* client) { depth=client->format.bitsPerPixel; if (enableResizable) - sdlFlags |= SDL_RESIZABLE; + sdlFlags |= SDL_WINDOW_RESIZABLE; client->updateRect.x = client->updateRect.y = 0; client->updateRect.w = width; client->updateRect.h = height; - rfbBool okay=SDL_VideoModeOK(width,height,depth,sdlFlags); - if(!okay) - for(depth=24;!okay && depth>4;depth/=2) - okay=SDL_VideoModeOK(width,height,depth,sdlFlags); - if(okay) { - SDL_Surface* sdl=SDL_SetVideoMode(width,height,depth,sdlFlags); - rfbClientSetClientData(client, SDL_Init, sdl); - client->width = sdl->pitch / (depth / 8); - if (sdlPixels) { - free(client->frameBuffer); - sdlPixels = NULL; - } - client->frameBuffer=sdl->pixels; - - client->format.bitsPerPixel=depth; - client->format.redShift=sdl->format->Rshift; - client->format.greenShift=sdl->format->Gshift; - client->format.blueShift=sdl->format->Bshift; - client->format.redMax=sdl->format->Rmask>>client->format.redShift; - client->format.greenMax=sdl->format->Gmask>>client->format.greenShift; - client->format.blueMax=sdl->format->Bmask>>client->format.blueShift; - SetFormatAndEncodings(client); + /* (re)create the surface used as the client's framebuffer */ + SDL_FreeSurface(rfbClientGetClientData(client, SDL_Init)); + SDL_Surface* sdl=SDL_CreateRGBSurface(0, + width, + height, + depth, + 0,0,0,0); + if(!sdl) + rfbClientErr("resize: error creating surface: %s\n", SDL_GetError()); + + rfbClientSetClientData(client, SDL_Init, sdl); + client->width = sdl->pitch / (depth / 8); + client->frameBuffer=sdl->pixels; + + client->format.bitsPerPixel=depth; + client->format.redShift=sdl->format->Rshift; + client->format.greenShift=sdl->format->Gshift; + client->format.blueShift=sdl->format->Bshift; + client->format.redMax=sdl->format->Rmask>>client->format.redShift; + client->format.greenMax=sdl->format->Gmask>>client->format.greenShift; + client->format.blueMax=sdl->format->Bmask>>client->format.blueShift; + SetFormatAndEncodings(client); + + /* create or resize the window */ + if(!sdlWindow) { + sdlWindow = SDL_CreateWindow(client->desktopName, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, + height, + sdlFlags); + if(!sdlWindow) + rfbClientErr("resize: error creating window: %s\n", SDL_GetError()); } else { - SDL_Surface* sdl=rfbClientGetClientData(client, SDL_Init); - rfbClientLog("Could not set resolution %dx%d!\n", - client->width,client->height); - if(sdl) { - client->width=sdl->pitch / (depth / 8); - client->height=sdl->h; - } else { - client->width=0; - client->height=0; - } - return FALSE; + SDL_SetWindowSize(sdlWindow, width, height); + } + + /* create the renderer if it does not already exist */ + if(!sdlRenderer) { + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0); + if(!sdlRenderer) + rfbClientErr("resize: error creating renderer: %s\n", SDL_GetError()); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); /* make the scaled rendering look smoother. */ } - SDL_WM_SetCaption(client->desktopName, "SDL"); + SDL_RenderSetLogicalSize(sdlRenderer, width, height); /* this is a departure from the SDL1.2-based version, but more in the sense of a VNC viewer in keeeping aspect ratio */ + + /* (re)create the texture that sits in between the surface->pixels and the renderer */ + if(sdlTexture) + SDL_DestroyTexture(sdlTexture); + sdlTexture = SDL_CreateTexture(sdlRenderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + width, height); + if(!sdlTexture) + rfbClientErr("resize: error creating texture: %s\n", SDL_GetError()); return TRUE; } static rfbKeySym SDL_key2rfbKeySym(SDL_KeyboardEvent* e) { rfbKeySym k = 0; - SDLKey sym = e->keysym.sym; + SDL_Keycode sym = e->keysym.sym; switch (sym) { case SDLK_BACKSPACE: k = XK_BackSpace; break; @@ -87,18 +113,17 @@ static rfbKeySym SDL_key2rfbKeySym(SDL_KeyboardEvent* e) { case SDLK_RETURN: k = XK_Return; break; case SDLK_PAUSE: k = XK_Pause; break; case SDLK_ESCAPE: k = XK_Escape; break; - case SDLK_SPACE: k = XK_space; break; case SDLK_DELETE: k = XK_Delete; break; - case SDLK_KP0: k = XK_KP_0; break; - case SDLK_KP1: k = XK_KP_1; break; - case SDLK_KP2: k = XK_KP_2; break; - case SDLK_KP3: k = XK_KP_3; break; - case SDLK_KP4: k = XK_KP_4; break; - case SDLK_KP5: k = XK_KP_5; break; - case SDLK_KP6: k = XK_KP_6; break; - case SDLK_KP7: k = XK_KP_7; break; - case SDLK_KP8: k = XK_KP_8; break; - case SDLK_KP9: k = XK_KP_9; break; + case SDLK_KP_0: k = XK_KP_0; break; + case SDLK_KP_1: k = XK_KP_1; break; + case SDLK_KP_2: k = XK_KP_2; break; + case SDLK_KP_3: k = XK_KP_3; break; + case SDLK_KP_4: k = XK_KP_4; break; + case SDLK_KP_5: k = XK_KP_5; break; + case SDLK_KP_6: k = XK_KP_6; break; + case SDLK_KP_7: k = XK_KP_7; break; + case SDLK_KP_8: k = XK_KP_8; break; + case SDLK_KP_9: k = XK_KP_9; break; case SDLK_KP_PERIOD: k = XK_KP_Decimal; break; case SDLK_KP_DIVIDE: k = XK_KP_Divide; break; case SDLK_KP_MULTIPLY: k = XK_KP_Multiply; break; @@ -130,165 +155,58 @@ static rfbKeySym SDL_key2rfbKeySym(SDL_KeyboardEvent* e) { case SDLK_F13: k = XK_F13; break; case SDLK_F14: k = XK_F14; break; case SDLK_F15: k = XK_F15; break; - case SDLK_NUMLOCK: k = XK_Num_Lock; break; + case SDLK_NUMLOCKCLEAR: k = XK_Num_Lock; break; case SDLK_CAPSLOCK: k = XK_Caps_Lock; break; - case SDLK_SCROLLOCK: k = XK_Scroll_Lock; break; + case SDLK_SCROLLLOCK: k = XK_Scroll_Lock; break; case SDLK_RSHIFT: k = XK_Shift_R; break; case SDLK_LSHIFT: k = XK_Shift_L; break; case SDLK_RCTRL: k = XK_Control_R; break; case SDLK_LCTRL: k = XK_Control_L; break; case SDLK_RALT: k = XK_Alt_R; break; case SDLK_LALT: k = XK_Alt_L; break; - case SDLK_RMETA: k = XK_Meta_R; break; - case SDLK_LMETA: k = XK_Meta_L; break; - case SDLK_LSUPER: k = XK_Super_L; break; - case SDLK_RSUPER: k = XK_Super_R; break; + case SDLK_LGUI: k = XK_Super_L; break; + case SDLK_RGUI: k = XK_Super_R; break; #if 0 case SDLK_COMPOSE: k = XK_Compose; break; #endif case SDLK_MODE: k = XK_Mode_switch; break; case SDLK_HELP: k = XK_Help; break; - case SDLK_PRINT: k = XK_Print; break; + case SDLK_PRINTSCREEN: k = XK_Print; break; case SDLK_SYSREQ: k = XK_Sys_Req; break; - case SDLK_BREAK: k = XK_Break; break; default: break; } - /* both SDL and X11 keysyms match ASCII in the range 0x01-0x7f */ - if (k == 0 && sym > 0x0 && sym < 0x100) { - k = sym; - if (e->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { - if (k >= '1' && k <= '9') - k &= ~0x10; - else if (k >= 'a' && k <= 'f') - k &= ~0x20; - } - } - if (k == 0) { - if (e->keysym.unicode < 0x100) - k = e->keysym.unicode; - else - rfbClientLog("Unknown keysym: %d\n", sym); - } + /* SDL_TEXTINPUT does not generate characters if ctrl is down, so handle those here */ + if (k == 0 && sym > 0x0 && sym < 0x100 && e->keysym.mod & KMOD_CTRL) + k = sym; return k; } -static uint32_t get(rfbClient *cl, int x, int y) -{ - switch (bytesPerPixel) { - case 1: return ((uint8_t *)cl->frameBuffer)[x + y * cl->width]; - case 2: return ((uint16_t *)cl->frameBuffer)[x + y * cl->width]; - case 4: return ((uint32_t *)cl->frameBuffer)[x + y * cl->width]; - default: - rfbClientErr("Unknown bytes/pixel: %d", bytesPerPixel); - exit(1); +/* UTF-8 decoding is from https://rosettacode.org/wiki/UTF-8_encode_and_decode which is under GFDL 1.2 */ +static rfbKeySym utf8char2rfbKeySym(const char chr[4]) { + int bytes = strlen(chr); + int shift = utf8Mapping[0].bits_stored * (bytes - 1); + rfbKeySym codep = (*chr++ & utf8Mapping[bytes].mask) << shift; + int i; + for(i = 1; i < bytes; ++i, ++chr) { + shift -= utf8Mapping[0].bits_stored; + codep |= ((char)*chr & utf8Mapping[0].mask) << shift; } -} - -static void put(int x, int y, uint32_t v) -{ - switch (bytesPerPixel) { - case 1: ((uint8_t *)sdlPixels)[x + y * rowStride] = v; break; - case 2: ((uint16_t *)sdlPixels)[x + y * rowStride] = v; break; - case 4: ((uint32_t *)sdlPixels)[x + y * rowStride] = v; break; - default: - rfbClientErr("Unknown bytes/pixel: %d", bytesPerPixel); - exit(1); - } -} - -static void resizeRectangleToReal(rfbClient *cl, int x, int y, int w, int h) -{ - int i0 = x * realWidth / cl->width; - int i1 = ((x + w) * realWidth - 1) / cl->width + 1; - int j0 = y * realHeight / cl->height; - int j1 = ((y + h) * realHeight - 1) / cl->height + 1; - int i, j; - - for (j = j0; j < j1; j++) - for (i = i0; i < i1; i++) { - int x0 = i * cl->width / realWidth; - int x1 = ((i + 1) * cl->width - 1) / realWidth + 1; - int y0 = j * cl->height / realHeight; - int y1 = ((j + 1) * cl->height - 1) / realHeight + 1; - uint32_t r = 0, g = 0, b = 0; - - for (y = y0; y < y1; y++) - for (x = x0; x < x1; x++) { - uint32_t v = get(cl, x, y); -#define REDSHIFT cl->format.redShift -#define REDMAX cl->format.redMax -#define GREENSHIFT cl->format.greenShift -#define GREENMAX cl->format.greenMax -#define BLUESHIFT cl->format.blueShift -#define BLUEMAX cl->format.blueMax - r += (v >> REDSHIFT) & REDMAX; - g += (v >> GREENSHIFT) & GREENMAX; - b += (v >> BLUESHIFT) & BLUEMAX; - } - r /= (x1 - x0) * (y1 - y0); - g /= (x1 - x0) * (y1 - y0); - b /= (x1 - x0) * (y1 - y0); - - put(i, j, (r << REDSHIFT) | (g << GREENSHIFT) | - (b << BLUESHIFT)); - } + return codep; } static void update(rfbClient* cl,int x,int y,int w,int h) { - if (sdlPixels) { - resizeRectangleToReal(cl, x, y, w, h); - w = ((x + w) * realWidth - 1) / cl->width + 1; - h = ((y + h) * realHeight - 1) / cl->height + 1; - x = x * realWidth / cl->width; - y = y * realHeight / cl->height; - w -= x; - h -= y; - } - SDL_UpdateRect(rfbClientGetClientData(cl, SDL_Init), x, y, w, h); -} - -static void setRealDimension(rfbClient *client, int w, int h) -{ - SDL_Surface* sdl; - - if (w < 0) { - const SDL_VideoInfo *info = SDL_GetVideoInfo(); - w = info->current_h; - h = info->current_w; - } - - if (w == realWidth && h == realHeight) - return; - - if (!sdlPixels) { - int size; - - sdlPixels = (char *)client->frameBuffer; - rowStride = client->width; - - bytesPerPixel = client->format.bitsPerPixel / 8; - size = client->width * bytesPerPixel * client->height; - client->frameBuffer = malloc(size); - if (!client->frameBuffer) { - rfbClientErr("Could not allocate %d bytes", size); - exit(1); - } - memcpy(client->frameBuffer, sdlPixels, size); - } - - sdl = rfbClientGetClientData(client, SDL_Init); - if (sdl->w != w || sdl->h != h) { - int depth = sdl->format->BitsPerPixel; - sdl = SDL_SetVideoMode(w, h, depth, sdlFlags); - rfbClientSetClientData(client, SDL_Init, sdl); - sdlPixels = sdl->pixels; - rowStride = sdl->pitch / (depth / 8); - } - - realWidth = w; - realHeight = h; - update(client, 0, 0, client->width, client->height); + SDL_Surface *sdl = rfbClientGetClientData(cl, SDL_Init); + /* update texture from surface->pixels */ + SDL_Rect r = {x,y,w,h}; + if(SDL_UpdateTexture(sdlTexture, &r, sdl->pixels + y*sdl->pitch + x*4, sdl->pitch) < 0) + rfbClientErr("update: failed to update texture: %s\n", SDL_GetError()); + /* copy texture to renderer and show */ + if(SDL_RenderClear(sdlRenderer) < 0) + rfbClientErr("update: failed to clear renderer: %s\n", SDL_GetError()); + if(SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL) < 0) + rfbClientErr("update: failed to copy texture to renderer: %s\n", SDL_GetError()); + SDL_RenderPresent(sdlRenderer); } static void kbd_leds(rfbClient* cl, int value, int pad) { @@ -374,17 +292,69 @@ static void cleanup(rfbClient* cl) static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) { switch(e->type) { -#if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION >= 2 - case SDL_VIDEOEXPOSE: + case SDL_WINDOWEVENT: + switch (e->window.event) { + case SDL_WINDOWEVENT_EXPOSED: SendFramebufferUpdateRequest(cl, 0, 0, cl->width, cl->height, FALSE); break; -#endif + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (SDL_HasClipboardText()) { + char *text = SDL_GetClipboardText(); + if(text) { + rfbClientLog("sending clipboard text '%s'\n", text); + SendClientCutText(cl, text, strlen(text)); + } + } + + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (rightAltKeyDown) { + SendKeyEvent(cl, XK_Alt_R, FALSE); + rightAltKeyDown = FALSE; + rfbClientLog("released right Alt key\n"); + } + if (leftAltKeyDown) { + SendKeyEvent(cl, XK_Alt_L, FALSE); + leftAltKeyDown = FALSE; + rfbClientLog("released left Alt key\n"); + } + break; + } + break; + case SDL_MOUSEWHEEL: + { + int steps; + if (viewOnly) + break; + + if(e->wheel.y > 0) + for(steps = 0; steps < e->wheel.y; ++steps) { + SendPointerEvent(cl, x, y, rfbButton4Mask); + SendPointerEvent(cl, x, y, 0); + } + if(e->wheel.y < 0) + for(steps = 0; steps > e->wheel.y; --steps) { + SendPointerEvent(cl, x, y, rfbButton5Mask); + SendPointerEvent(cl, x, y, 0); + } + if(e->wheel.x > 0) + for(steps = 0; steps < e->wheel.x; ++steps) { + SendPointerEvent(cl, x, y, 0b01000000); + SendPointerEvent(cl, x, y, 0); + } + if(e->wheel.x < 0) + for(steps = 0; steps > e->wheel.x; --steps) { + SendPointerEvent(cl, x, y, 0b00100000); + SendPointerEvent(cl, x, y, 0); + } + break; + } case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEMOTION: { - int x, y, state, i; + int state, i; if (viewOnly) break; @@ -407,10 +377,6 @@ static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) break; } } - if (sdlPixels) { - x = x * cl->width / realWidth; - y = y * cl->height / realHeight; - } SendPointerEvent(cl, x, y, buttonMask); buttonMask &= ~(rfbButton4Mask | rfbButton5Mask); break; @@ -426,6 +392,13 @@ static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) if (e->key.keysym.sym == SDLK_LALT) leftAltKeyDown = e->type == SDL_KEYDOWN; break; + case SDL_TEXTINPUT: + if (viewOnly) + break; + rfbKeySym sym = utf8char2rfbKeySym(e->text.text); + SendKeyEvent(cl, sym, TRUE); + SendKeyEvent(cl, sym, FALSE); + break; case SDL_QUIT: if(listenLoop) { @@ -437,32 +410,6 @@ static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) rfbClientCleanup(cl); exit(0); } - case SDL_ACTIVEEVENT: - if (!e->active.gain && rightAltKeyDown) { - SendKeyEvent(cl, XK_Alt_R, FALSE); - rightAltKeyDown = FALSE; - rfbClientLog("released right Alt key\n"); - } - if (!e->active.gain && leftAltKeyDown) { - SendKeyEvent(cl, XK_Alt_L, FALSE); - leftAltKeyDown = FALSE; - rfbClientLog("released left Alt key\n"); - } - - if (e->active.gain && lost_scrap()) { - static char *data = NULL; - static int len = 0; - get_scrap(T('T', 'E', 'X', 'T'), &len, &data); - if (len) - SendClientCutText(cl, data, len); - } - break; - case SDL_SYSWMEVENT: - clipboard_filter(e); - break; - case SDL_VIDEORESIZE: - setRealDimension(cl, e->resize.w, e->resize.h); - break; default: rfbClientLog("ignore SDL event: 0x%x\n", e->type); } @@ -471,7 +418,33 @@ static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) static void got_selection(rfbClient *cl, const char *text, int len) { - put_scrap(T('T', 'E', 'X', 'T'), len, text); + rfbClientLog("received clipboard text '%s'\n", text); + if(SDL_SetClipboardText(text) != 0) + rfbClientErr("could not set received clipboard text: %s\n", SDL_GetError()); +} + + +static rfbCredential* get_credential(rfbClient* cl, int credentialType){ + rfbCredential *c = malloc(sizeof(rfbCredential)); + c->userCredential.username = malloc(RFB_BUF_SIZE); + c->userCredential.password = malloc(RFB_BUF_SIZE); + + if(credentialType != rfbCredentialTypeUser) { + rfbClientErr("something else than username and password required for authentication\n"); + return NULL; + } + + rfbClientLog("username and password required for authentication!\n"); + printf("user: "); + fgets(c->userCredential.username, RFB_BUF_SIZE, stdin); + printf("pass: "); + fgets(c->userCredential.password, RFB_BUF_SIZE, stdin); + + /* remove trailing newlines */ + c->userCredential.username[strcspn(c->userCredential.username, "\n")] = 0; + c->userCredential.password[strcspn(c->userCredential.password, "\n")] = 0; + + return c; } @@ -508,9 +481,6 @@ int main(int argc,char** argv) { argc = j; SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); - SDL_EnableUNICODE(1); - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, - SDL_DEFAULT_REPEAT_INTERVAL); atexit(SDL_Quit); signal(SIGINT, exit); @@ -523,6 +493,7 @@ int main(int argc,char** argv) { cl->HandleKeyboardLedState=kbd_leds; cl->HandleTextChat=text_chat; cl->GotXCutText = got_selection; + cl->GetCredential = get_credential; cl->listenPort = LISTEN_PORT_OFFSET; cl->listen6Port = LISTEN_PORT_OFFSET; if(!rfbInitClient(cl,&argc,argv)) @@ -532,8 +503,6 @@ int main(int argc,char** argv) { break; } - init_scrap(); - while(1) { if(SDL_PollEvent(&e)) { /* diff --git a/client_examples/backchannel.c b/client_examples/backchannel.c index 04d154e..a7db9a0 100644 --- a/client_examples/backchannel.c +++ b/client_examples/backchannel.c @@ -71,7 +71,9 @@ static rfbClientProtocolExtension backChannel = { backChannelEncodings, /* encodings */ NULL, /* handleEncoding */ handleBackChannelMessage, /* handleMessage */ - NULL /* next extension */ + NULL, /* next extension */ + NULL, /* securityTypes */ + NULL /* handleAuthentication */ }; int diff --git a/client_examples/ppmtest.c b/client_examples/ppmtest.c index b8602f0..99ee595 100644 --- a/client_examples/ppmtest.c +++ b/client_examples/ppmtest.c @@ -58,12 +58,27 @@ static void SaveFramebufferAsPPM(rfbClient* client, int x, int y, int w, int h) fclose(f); } +char * getuser(rfbClient *client) +{ +return strdup("testuser@test"); +} + +char * getpassword(rfbClient *client) +{ +return strdup("Password"); +} + int main(int argc, char **argv) { rfbClient* client = rfbGetClient(8,3,4); time_t t=time(NULL); +#ifdef LIBVNCSERVER_HAVE_SASL + client->GetUser = getuser; + client->GetPassword = getpassword; +#endif + if(argc>1 && !strcmp("-print",argv[1])) { client->GotFrameBufferUpdate = PrintRect; argv[1]=argv[0]; argv++; argc--; diff --git a/client_examples/scrap.c b/client_examples/scrap.c deleted file mode 100644 index c28800c..0000000 --- a/client_examples/scrap.c +++ /dev/null @@ -1,558 +0,0 @@ -/* Handle clipboard text and data in arbitrary formats */ - -#include <stdio.h> -#include <limits.h> - -#ifdef WIN32 -#include <SDL.h> -#include <SDL_syswm.h> -#else -#include <SDL/SDL.h> -#include <SDL/SDL_syswm.h> -#endif -#include "scrap.h" -#include "rfb/rfbconfig.h" - -/* Determine what type of clipboard we are using */ -#if defined(__unix__) && !defined(__QNXNTO__) && defined(LIBVNCSERVER_HAVE_X11) -#define X11_SCRAP -#elif defined(__WIN32__) -#define WIN_SCRAP -#elif defined(__QNXNTO__) -#define QNX_SCRAP -#else -#warning Unknown window manager for clipboard handling -#endif /* scrap type */ - -/* System dependent data types */ -#if defined(X11_SCRAP) -typedef Atom scrap_type; -static Atom XA_TARGETS, XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING; -#elif defined(WIN_SCRAP) -typedef UINT scrap_type; -#elif defined(QNX_SCRAP) -typedef uint32_t scrap_type; -#define Ph_CL_TEXT T('T', 'E', 'X', 'T') -#else -typedef int scrap_type; -#endif /* scrap type */ - -/* System dependent variables */ -#if defined(X11_SCRAP) -static Display *SDL_Display; -static Window SDL_Window; -static void (*Lock_Display)(void); -static void (*Unlock_Display)(void); -static Atom XA_UTF8_STRING; -#elif defined(WIN_SCRAP) -static HWND SDL_Window; -#elif defined(QNX_SCRAP) -static unsigned short InputGroup; -#endif /* scrap type */ - -#define FORMAT_PREFIX "SDL_scrap_0x" - -static scrap_type convert_format(int type) -{ - switch (type) { - case T('T', 'E', 'X', 'T'): -#if defined(X11_SCRAP) - return XA_UTF8_STRING ? XA_UTF8_STRING : XA_STRING; -#elif defined(WIN_SCRAP) - return CF_TEXT; -#elif defined(QNX_SCRAP) - return Ph_CL_TEXT; -#endif /* scrap type */ - default: - { - char format[sizeof(FORMAT_PREFIX)+8+1]; - - sprintf(format, "%s%08lx", FORMAT_PREFIX, - (unsigned long)type); -#if defined(X11_SCRAP) - return XInternAtom(SDL_Display, format, False); -#elif defined(WIN_SCRAP) - return RegisterClipboardFormat(format); -#endif /* scrap type */ - } - } -} - -/* Convert internal data to scrap format */ -static int convert_data(int type, char *dst, const char *src, int srclen) -{ - int dstlen; - - dstlen = 0; - switch (type) { - case T('T', 'E', 'X', 'T'): - if (dst) { - while (--srclen >= 0) { -#if defined(__unix__) - if (*src == '\r') { - *dst++ = '\n'; - ++dstlen; - } - else -#elif defined(__WIN32__) - if (*src == '\r') { - *dst++ = '\r'; - ++dstlen; - *dst++ = '\n'; - ++dstlen; - } - else -#endif - { - *dst++ = *src; - ++dstlen; - } - ++src; - } - *dst = '\0'; - ++dstlen; - } - else { - while (--srclen >= 0) { -#if defined(__unix__) - if (*src == '\r') - ++dstlen; - else -#elif defined(__WIN32__) - if (*src == '\r') { - ++dstlen; - ++dstlen; - } - else -#endif - { - ++dstlen; - } - ++src; - } - ++dstlen; - } - break; - default: - if (dst) { - *(int *)dst = srclen; - dst += sizeof(int); - memcpy(dst, src, srclen); - } - dstlen = sizeof(int)+srclen; - break; - } - return(dstlen); -} - -/* Convert scrap data to internal format */ -static int convert_scrap(int type, char *dst, char *src, int srclen) -{ - int dstlen; - - dstlen = 0; - switch (type) { - case T('T', 'E', 'X', 'T'): - { - if (srclen == 0) - srclen = strlen(src); - if (dst) { - while (--srclen >= 0) { -#if defined(__WIN32__) - if (*src == '\r') - /* drop extraneous '\r' */; - else -#endif - if (*src == '\n') { - *dst++ = '\r'; - ++dstlen; - } - else { - *dst++ = *src; - ++dstlen; - } - ++src; - } - *dst = '\0'; - ++dstlen; - } - else { - while (--srclen >= 0) { -#if defined(__WIN32__) - /* drop extraneous '\r' */; - if (*src != '\r') -#endif - ++dstlen; - ++src; - } - ++dstlen; - } - break; - } - default: - dstlen = *(int *)src; - if (dst) - memcpy(dst, src + sizeof(int), - srclen ? srclen - sizeof(int) : dstlen); - break; - } - return dstlen; -} - -int init_scrap(void) -{ - SDL_SysWMinfo info; - int retval; - - /* Grab the window manager specific information */ - retval = -1; - SDL_SetError("SDL is not running on known window manager"); - - SDL_VERSION(&info.version); - if (SDL_GetWMInfo(&info)) { - /* Save the information for later use */ -#if defined(X11_SCRAP) - if (info.subsystem == SDL_SYSWM_X11) { - SDL_Display = info.info.x11.display; - SDL_Window = info.info.x11.window; - Lock_Display = info.info.x11.lock_func; - Unlock_Display = info.info.x11.unlock_func; - - /* Enable the special window hook events */ - SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); - SDL_SetEventFilter(clipboard_filter); - - XA_TARGETS = XInternAtom(SDL_Display, "TARGETS", False); - XA_TEXT = XInternAtom(SDL_Display, "TEXT", False); - XA_COMPOUND_TEXT = XInternAtom(SDL_Display, - "COMPOUND_TEXT", False); - XA_UTF8_STRING = XInternAtom(SDL_Display, - "UTF8_STRING", False); - - retval = 0; - } - else - SDL_SetError("SDL is not running on X11"); -#elif defined(WIN_SCRAP) - SDL_Window = info.window; - retval = 0; -#elif defined(QNX_SCRAP) - InputGroup = PhInputGroup(NULL); - retval = 0; -#endif /* scrap type */ - } - return(retval); -} - -int lost_scrap(void) -{ - int retval; - -#if defined(X11_SCRAP) - if (Lock_Display) - Lock_Display(); - retval = (XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window); - if (Unlock_Display) - Unlock_Display(); -#elif defined(WIN_SCRAP) - retval = (GetClipboardOwner() != SDL_Window); -#elif defined(QNX_SCRAP) - retval = (PhInputGroup(NULL) != InputGroup); -#endif /* scrap type */ - - return(retval); -} - -void put_scrap(int type, int srclen, const char *src) -{ - scrap_type format; - int dstlen; - char *dst; - - format = convert_format(type); - dstlen = convert_data(type, NULL, src, srclen); - -#if defined(X11_SCRAP) - dst = (char *)malloc(dstlen); - if (dst != NULL) { - if (Lock_Display) - Lock_Display(); - convert_data(type, dst, src, srclen); - XChangeProperty(SDL_Display, DefaultRootWindow(SDL_Display), - XA_CUT_BUFFER0, format, 8, PropModeReplace, - (unsigned char *)dst, dstlen); - free(dst); - if (lost_scrap()) - XSetSelectionOwner(SDL_Display, XA_PRIMARY, - SDL_Window, CurrentTime); - if (Unlock_Display) - Unlock_Display(); - } -#elif defined(WIN_SCRAP) - if (OpenClipboard(SDL_Window)) { - HANDLE hMem; - - hMem = GlobalAlloc((GMEM_MOVEABLE|GMEM_DDESHARE), dstlen); - if (hMem != NULL) { - dst = (char *)GlobalLock(hMem); - convert_data(type, dst, src, srclen); - GlobalUnlock(hMem); - EmptyClipboard(); - SetClipboardData(format, hMem); - } - CloseClipboard(); - } -#elif defined(QNX_SCRAP) -#if (_NTO_VERSION < 620) /* before 6.2.0 releases */ -#define PhClipboardHdr PhClipHeader -#endif - { - PhClipboardHdr clheader = { Ph_CLIPBOARD_TYPE_TEXT, 0, NULL }; - int* cldata; - int status; - - dst = (char *)malloc(dstlen+4); - if (dst != NULL) { - cldata = (int*)dst; - *cldata = type; - convert_data(type, dst+4, src, srclen); - clheader.data = dst; -#if (_NTO_VERSION < 620) /* before 6.2.0 releases */ - if (dstlen > 65535) - /* maximum photon clipboard size :(*/ - clheader.length = 65535; - else -#endif - clheader.length = dstlen+4; - status = PhClipboardCopy(InputGroup, 1, &clheader); - if (status == -1) - fprintf(stderr, - "Photon: copy to clipboard failed!\n"); - free(dst); - } - } -#endif /* scrap type */ -} - -void get_scrap(int type, int *dstlen, char **dst) -{ - scrap_type format; - - *dstlen = 0; - format = convert_format(type); - -#if defined(X11_SCRAP) - { - Window owner; - Atom selection; - Atom seln_type; - int seln_format; - unsigned long nbytes; - unsigned long overflow; - char *src; - - if (Lock_Display) - Lock_Display(); - owner = XGetSelectionOwner(SDL_Display, XA_PRIMARY); - if (Unlock_Display) - Unlock_Display(); - if ((owner == None) || (owner == SDL_Window)) { - owner = DefaultRootWindow(SDL_Display); - selection = XA_CUT_BUFFER0; - } - else { - int selection_response = 0; - SDL_Event event; - - owner = SDL_Window; - if (Lock_Display) - Lock_Display(); - selection = XInternAtom(SDL_Display, "SDL_SELECTION", - False); - XConvertSelection(SDL_Display, XA_PRIMARY, format, - selection, owner, CurrentTime); - if (Unlock_Display) - Unlock_Display(); - while (!selection_response) { - SDL_WaitEvent(&event); - if (event.type == SDL_SYSWMEVENT) { - XEvent xevent = - event.syswm.msg->event.xevent; - - if ((xevent.type == SelectionNotify) && - (xevent.xselection.requestor - == owner)) - selection_response = 1; - } - } - } - if (Lock_Display) - Lock_Display(); - if (XGetWindowProperty(SDL_Display, owner, selection, - 0, INT_MAX/4, False, format, &seln_type, - &seln_format, &nbytes, &overflow, - (unsigned char **)&src) == Success) { - if (seln_type == format) { - *dstlen = convert_scrap(type, NULL, - src, nbytes); - *dst = (char *)realloc(*dst, *dstlen); - if (*dst == NULL) - *dstlen = 0; - else - convert_scrap(type, *dst, src, nbytes); - } - XFree(src); - } - } - if (Unlock_Display) - Unlock_Display(); -#elif defined(WIN_SCRAP) - if (IsClipboardFormatAvailable(format) && OpenClipboard(SDL_Window)) { - HANDLE hMem; - char *src; - - hMem = GetClipboardData(format); - if (hMem != NULL) { - src = (char *)GlobalLock(hMem); - *dstlen = convert_scrap(type, NULL, src, 0); - *dst = (char *)realloc(*dst, *dstlen); - if (*dst == NULL) - *dstlen = 0; - else - convert_scrap(type, *dst, src, 0); - GlobalUnlock(hMem); - } - CloseClipboard(); - } -#elif defined(QNX_SCRAP) -#if (_NTO_VERSION < 620) /* before 6.2.0 releases */ - { - void* clhandle; - PhClipHeader* clheader; - int* cldata; - - clhandle = PhClipboardPasteStart(InputGroup); - if (clhandle != NULL) { - clheader = PhClipboardPasteType(clhandle, - Ph_CLIPBOARD_TYPE_TEXT); - if (clheader != NULL) { - cldata = clheader->data; - if ((clheader->length>4) && (*cldata == type)) { - *dstlen = convert_scrap(type, NULL, - (char*)clheader->data+4, - clheader->length-4); - *dst = (char *)realloc(*dst, *dstlen); - if (*dst == NULL) - *dstlen = 0; - else - convert_scrap(type, *dst, - (char*)clheader->data+4, - clheader->length-4); - } - } - PhClipboardPasteFinish(clhandle); - } - } -#else /* 6.2.0 and 6.2.1 and future releases */ - { - void* clhandle; - PhClipboardHdr* clheader; - int* cldata; - - clheader=PhClipboardRead(InputGroup, Ph_CLIPBOARD_TYPE_TEXT); - if (clheader!=NULL) { - cldata=clheader->data; - if ((clheader->length>4) && (*cldata==type)) { - *dstlen = convert_scrap(type, NULL, - (char*)clheader->data+4, - clheader->length-4); - *dst = (char *)realloc(*dst, *dstlen); - if (*dst == NULL) - *dstlen = 0; - else - convert_scrap(type, *dst, - (char*)clheader->data+4, - clheader->length-4); - } - } - } -#endif -#endif /* scrap type */ -} - -int clipboard_filter(const SDL_Event *event) -{ -#if defined(X11_SCRAP) - /* Post all non-window manager specific events */ - if (event->type != SDL_SYSWMEVENT) - return(1); - - /* Handle window-manager specific clipboard events */ - switch (event->syswm.msg->event.xevent.type) { - /* Copy the selection from XA_CUT_BUFFER0 to the requested property */ - case SelectionRequest: { - XSelectionRequestEvent *req; - XEvent sevent; - int seln_format; - unsigned long nbytes; - unsigned long overflow; - unsigned char *seln_data; - - req = &event->syswm.msg->event.xevent.xselectionrequest; - if (req->target == XA_TARGETS) { - Atom supported[] = { - XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING, - XA_TARGETS, XA_STRING - }; - XEvent response; - - XChangeProperty(SDL_Display, req->requestor, - req->property, req->target, 32, PropModeReplace, - (unsigned char*)supported, - sizeof(supported) / sizeof(supported[0])); - response.xselection.property=None; - response.xselection.type= SelectionNotify; - response.xselection.display= req->display; - response.xselection.requestor= req->requestor; - response.xselection.selection=req->selection; - response.xselection.target= req->target; - response.xselection.time = req->time; - XSendEvent (SDL_Display, req->requestor,0,0,&response); - XFlush (SDL_Display); - return 1; - } - - sevent.xselection.type = SelectionNotify; - sevent.xselection.display = req->display; - sevent.xselection.selection = req->selection; - sevent.xselection.target = None; - sevent.xselection.property = req->property; - sevent.xselection.requestor = req->requestor; - sevent.xselection.time = req->time; - if (XGetWindowProperty(SDL_Display, - DefaultRootWindow(SDL_Display), XA_CUT_BUFFER0, - 0, INT_MAX/4, False, req->target, - &sevent.xselection.target, &seln_format, - &nbytes, &overflow, &seln_data) == Success) { - if (sevent.xselection.target == req->target) { - if (sevent.xselection.target == XA_STRING && - nbytes > 0 && - seln_data[nbytes-1] == '\0') - --nbytes; - XChangeProperty(SDL_Display, req->requestor, - req->property, sevent.xselection.target, - seln_format, PropModeReplace, - seln_data, nbytes); - sevent.xselection.property = req->property; - } - XFree(seln_data); - } - XSendEvent(SDL_Display,req->requestor,False,0,&sevent); - XSync(SDL_Display, False); - break; - } - } - /* Post the event for X11 clipboard reading above */ -#endif /* X11_SCRAP */ - return(1); -} diff --git a/client_examples/scrap.h b/client_examples/scrap.h deleted file mode 100644 index 647bd74..0000000 --- a/client_examples/scrap.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Handle clipboard text and data in arbitrary formats */ - -/* Miscellaneous defines */ -#define T(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0)) - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -extern int init_scrap(void); -extern int lost_scrap(void); -extern void put_scrap(int type, int srclen, const char *src); -extern void get_scrap(int type, int *dstlen, char **dst); -extern int clipboard_filter(const SDL_Event *event); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ diff --git a/client_examples/vnc2mpg.c b/client_examples/vnc2mpg.c index af4a73a..a7438af 100644 --- a/client_examples/vnc2mpg.c +++ b/client_examples/vnc2mpg.c @@ -3,6 +3,7 @@ * Simple movie writer for vnc; based on Libavformat API example from FFMPEG * * Copyright (c) 2003 Fabrice Bellard, 2004 Johannes E. Schindelin + * Updates copyright (c) 2017 Tyrel M. McQueen * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,412 +26,451 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <signal.h> #include <math.h> - -#ifndef M_PI -#define M_PI 3.1415926535897931 -#endif - -#include "avformat.h" +#include <signal.h> +#include <sys/time.h> +#include <libavformat/avformat.h> +#include <libswscale/swscale.h> #include <rfb/rfbclient.h> -#define STREAM_FRAME_RATE 25 /* 25 images/s */ +#define VNC_PIX_FMT AV_PIX_FMT_RGB565 /* pixel format generated by VNC client */ +#define OUTPUT_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ -/**************************************************************/ -/* video output */ +static int write_packet(AVFormatContext *oc, const AVRational *time_base, AVStream *st, AVPacket *pkt) +{ + /* rescale output packet timestamp values from codec to stream timebase */ + av_packet_rescale_ts(pkt, *time_base, st->time_base); + pkt->stream_index = st->index; + /* Write the compressed frame to the media file. */ + return av_interleaved_write_frame(oc, pkt); +} -AVFrame *picture, *tmp_picture; -uint8_t *video_outbuf; -int frame_count, video_outbuf_size; +/*************************************************/ +/* video functions */ -/* add a video output stream */ -AVStream *add_video_stream(AVFormatContext *oc, int codec_id, int w, int h) -{ - AVCodecContext *c; +/* a wrapper around a single output video stream */ +typedef struct { AVStream *st; + AVCodec *codec; + AVCodecContext *enc; + int64_t pts; + AVFrame *frame; + AVFrame *tmp_frame; + struct SwsContext *sws; +} VideoOutputStream; + +/* Add an output video stream. */ +int add_video_stream(VideoOutputStream *ost, AVFormatContext *oc, + enum AVCodecID codec_id, int64_t br, int sr, int w, int h) +{ + int i; - st = av_new_stream(oc, 0); - if (!st) { - fprintf(stderr, "Could not alloc stream\n"); - exit(1); - } - -#if LIBAVFORMAT_BUILD<4629 - c = &st->codec; -#else - c = st->codec; -#endif - c->codec_id = codec_id; - c->codec_type = CODEC_TYPE_VIDEO; - - /* put sample parameters */ - c->bit_rate = 800000; - /* resolution must be a multiple of two */ - c->width = w; - c->height = h; - /* frames per second */ -#if LIBAVCODEC_BUILD<4754 - c->frame_rate = STREAM_FRAME_RATE; - c->frame_rate_base = 1; -#else - c->time_base.den = STREAM_FRAME_RATE; - c->time_base.num = 1; - c->pix_fmt = PIX_FMT_YUV420P; -#endif - c->gop_size = 12; /* emit one intra frame every twelve frames at most */ - if (c->codec_id == CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - c->max_b_frames = 2; + /* find the encoder */ + ost->codec = avcodec_find_encoder(codec_id); + if (!(ost->codec)) { + fprintf(stderr, "Could not find encoder for '%s'\n", + avcodec_get_name(codec_id)); + return -1; + } // no extra memory allocation from this call + if (ost->codec->type != AVMEDIA_TYPE_VIDEO) { + fprintf(stderr, "Encoder for '%s' does not seem to be for video.\n", + avcodec_get_name(codec_id)); + return -2; } - if (c->codec_id == CODEC_ID_MPEG1VIDEO){ - /* needed to avoid using macroblocks in which some coeffs overflow - this doesn't happen with normal video, it just happens here as the - motion of the chroma plane doesn't match the luma plane */ - c->mb_decision=2; + ost->enc = avcodec_alloc_context3(ost->codec); + if (!(ost->enc)) { + fprintf(stderr, "Could not alloc an encoding context\n"); + return -3; + } // from now on need to call avcodec_free_context(&(ost->enc)) on error + + /* Set codec parameters */ + ost->enc->codec_id = codec_id; + ost->enc->bit_rate = br; + /* Resolution must be a multiple of two (round up to avoid buffer overflow). */ + ost->enc->width = w + (w % 2); + ost->enc->height = h + (h % 2); + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + ost->enc->time_base = (AVRational){ 1, sr }; + ost->enc->gop_size = 12; /* emit one intra frame every twelve frames at most */ + ost->enc->pix_fmt = OUTPUT_PIX_FMT; + if (ost->enc->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + ost->enc->mb_decision = 2; } - /* some formats want stream headers to be separate */ - if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp")) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - - return st; + + ost->st = avformat_new_stream(oc, ost->codec); + if (!ost->st) { + fprintf(stderr, "Could not allocate stream\n"); + avcodec_free_context(&(ost->enc)); + return -4; + } // stream memory cleared up when oc is freed, so no need to do so later in this function on error + ost->st->id = oc->nb_streams-1; + ost->st->time_base = ost->enc->time_base; + ost->pts = 0; + + /* Some formats want stream headers to be separate. */ + if (oc->oformat->flags & AVFMT_GLOBALHEADER) + ost->enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + + // must wait to allocate frame buffers until codec is opened (in case codec changes the PIX_FMT) + return 0; } -AVFrame *alloc_picture(int pix_fmt, int width, int height) +AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) { AVFrame *picture; - uint8_t *picture_buf; - int size; - - picture = avcodec_alloc_frame(); + int ret; + picture = av_frame_alloc(); if (!picture) return NULL; - size = avpicture_get_size(pix_fmt, width, height); - picture_buf = malloc(size); - if (!picture_buf) { - av_free(picture); + // from now on need to call av_frame_free(&picture) on error + picture->format = pix_fmt; + picture->width = width; + picture->height = height; + /* allocate the buffers for the frame data */ + ret = av_frame_get_buffer(picture, 64); + if (ret < 0) { + fprintf(stderr, "Could not allocate frame data.\n"); + av_frame_free(&picture); return NULL; } - avpicture_fill((AVPicture *)picture, picture_buf, - pix_fmt, width, height); return picture; -} - -void open_video(AVFormatContext *oc, AVStream *st) -{ - AVCodec *codec; - AVCodecContext *c; - -#if LIBAVFORMAT_BUILD<4629 - c = &st->codec; -#else - c = st->codec; -#endif - - /* find the video encoder */ - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - fprintf(stderr, "codec not found\n"); - exit(1); - } +} // use av_frame_free(&picture) to free memory from this call +int open_video(AVFormatContext *oc, VideoOutputStream *ost) +{ + int ret; /* open the codec */ - if (avcodec_open(c, codec) < 0) { - fprintf(stderr, "could not open codec\n"); - exit(1); + ret = avcodec_open2(ost->enc, ost->codec, NULL); + if (ret < 0) { + fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret)); + return ret; + } // memory from this call freed when oc is freed, no need to do it on error in this call + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc); + if (ret < 0) { + fprintf(stderr, "Could not copy the stream parameters.\n"); + return ret; + } // memory from this call is freed when oc (parent of ost->st) is freed, no need to do it on error in this call + /* allocate and init a re-usable frame */ + ost->frame = alloc_picture(ost->enc->pix_fmt, ost->enc->width, ost->enc->height); + if (!(ost->frame)) { + fprintf(stderr, "Could not allocate video frame\n"); + return -1; + } // from now on need to call av_frame_free(&(ost->frame)) on error + /* If the output format is not the same as the VNC format, then a temporary VNC format + * picture is needed too. It is then converted to the required + * output format. */ + ost->tmp_frame = NULL; + ost->sws = NULL; + if (ost->enc->pix_fmt != VNC_PIX_FMT) { + ost->tmp_frame = alloc_picture(VNC_PIX_FMT, ost->enc->width, ost->enc->height); + if (!(ost->tmp_frame)) { + fprintf(stderr, "Could not allocate temporary picture\n"); + av_frame_free(&(ost->frame)); + return -2; + } // from now on need to call av_frame_free(&(ost->tmp_frame)) on error + ost->sws = sws_getCachedContext(ost->sws, ost->enc->width, ost->enc->height, VNC_PIX_FMT, ost->enc->width, ost->enc->height, ost->enc->pix_fmt, 0, NULL, NULL, NULL); + if (!(ost->sws)) { + fprintf(stderr, "Could not get sws context\n"); + av_frame_free(&(ost->frame)); + av_frame_free(&(ost->tmp_frame)); + return -3; + } // from now on need to call sws_freeContext(ost->sws); ost->sws = NULL; on error } - video_outbuf = NULL; - if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { - /* allocate output buffer */ - /* XXX: API change will be done */ - video_outbuf_size = 200000; - video_outbuf = malloc(video_outbuf_size); - } + return 0; +} - /* allocate the encoded raw picture */ - picture = alloc_picture(c->pix_fmt, c->width, c->height); - if (!picture) { - fprintf(stderr, "Could not allocate picture\n"); - exit(1); +/* + * encode current video frame and send it to the muxer + * return 0 on success, negative on error + */ +int write_video_frame(AVFormatContext *oc, VideoOutputStream *ost, int64_t pts) +{ + int ret, ret2; + AVPacket pkt = { 0 }; + if (pts <= ost->pts) return 0; // nothing to do + /* convert format if needed */ + if (ost->tmp_frame) { + sws_scale(ost->sws, (const uint8_t * const *)ost->tmp_frame->data, + ost->tmp_frame->linesize, 0, ost->enc->height, ost->frame->data, ost->frame->linesize); } - /* if the output format is not RGB565, then a temporary RGB565 - picture is needed too. It is then converted to the required - output format */ - tmp_picture = NULL; - if (c->pix_fmt != PIX_FMT_RGB565) { - tmp_picture = alloc_picture(PIX_FMT_RGB565, c->width, c->height); - if (!tmp_picture) { - fprintf(stderr, "Could not allocate temporary picture\n"); - exit(1); + /* send the imager to encoder */ + ost->pts = pts; + ost->frame->pts = ost->pts; + ret = avcodec_send_frame(ost->enc, ost->frame); + if (ret < 0) { + fprintf(stderr, "Error sending video frame to encoder: %s\n", av_err2str(ret)); + return ret; + } + /* read all available packets */ + ret2 = 0; + for (ret = avcodec_receive_packet(ost->enc, &pkt); ret == 0; ret = avcodec_receive_packet(ost->enc, &pkt)) { + ret2 = write_packet(oc, &(ost->enc->time_base), ost->st, &pkt); + if (ret2 < 0) { + fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret2)); + /* continue on this error to not gum up encoder */ } } + if (ret2 < 0) return ret2; + if (!(ret == AVERROR(EAGAIN))) return ret; // if AVERROR(EAGAIN), means all available packets output, need more frames (i.e. success) + return 0; } -void write_video_frame(AVFormatContext *oc, AVStream *st) +/* + * Write final video frame (i.e. drain codec). + */ +int write_final_video_frame(AVFormatContext *oc, VideoOutputStream *ost) { - int out_size, ret; - AVCodecContext *c; - AVFrame *picture_ptr; - -#if LIBAVFORMAT_BUILD<4629 - c = &st->codec; -#else - c = st->codec; -#endif - - if (c->pix_fmt != PIX_FMT_RGB565) { - /* as we only generate a RGB565 picture, we must convert it - to the codec pixel format if needed */ - img_convert((AVPicture *)picture, c->pix_fmt, - (AVPicture *)tmp_picture, PIX_FMT_RGB565, - c->width, c->height); - } - picture_ptr = picture; - - - if (oc->oformat->flags & AVFMT_RAWPICTURE) { - /* raw video case. The API will change slightly in the near - futur for that */ - AVPacket pkt; - av_init_packet(&pkt); - - pkt.flags |= PKT_FLAG_KEY; - pkt.stream_index= st->index; - pkt.data= (uint8_t *)picture_ptr; - pkt.size= sizeof(AVPicture); - - ret = av_write_frame(oc, &pkt); - } else { - /* encode the image */ - out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture_ptr); - /* if zero size, it means the image was buffered */ - if (out_size != 0) { - AVPacket pkt; - av_init_packet(&pkt); - - pkt.pts= c->coded_frame->pts; - if(c->coded_frame->key_frame) - pkt.flags |= PKT_FLAG_KEY; - pkt.stream_index= st->index; - pkt.data= video_outbuf; - pkt.size= out_size; - - /* write the compressed frame in the media file */ - ret = av_write_frame(oc, &pkt); - } else { - ret = 0; - } + int ret, ret2; + AVPacket pkt = { 0 }; + + /* send NULL image to encoder */ + ret = avcodec_send_frame(ost->enc, NULL); + if (ret < 0) { + fprintf(stderr, "Error sending final video frame to encoder: %s\n", av_err2str(ret)); + return ret; } - if (ret != 0) { - fprintf(stderr, "Error while writing video frame\n"); - exit(1); + /* read all available packets */ + ret2 = 0; + for (ret = avcodec_receive_packet(ost->enc, &pkt); ret == 0; ret = avcodec_receive_packet(ost->enc, &pkt)) { + ret2 = write_packet(oc, &(ost->enc->time_base), ost->st, &pkt); + if (ret2 < 0) { + fprintf(stderr, "Error while writing final video frame: %s\n", av_err2str(ret2)); + /* continue on this error to not gum up encoder */ + } } - frame_count++; + if (ret2 < 0) return ret2; + if (!(ret == AVERROR(EOF))) return ret; + return 0; } -void close_video(AVFormatContext *oc, AVStream *st) +void close_video_stream(VideoOutputStream *ost) { - avcodec_close(st->codec); - av_free(picture->data[0]); - av_free(picture); - if (tmp_picture) { - av_free(tmp_picture->data[0]); - av_free(tmp_picture); - } - av_free(video_outbuf); + avcodec_free_context(&(ost->enc)); + av_frame_free(&(ost->frame)); + av_frame_free(&(ost->tmp_frame)); + sws_freeContext(ost->sws); ost->sws = NULL; + ost->codec = NULL; /* codec not an allocated item */ + ost->st = NULL; /* freeing parent oc will free this memory */ } -static const char *filename; -static AVOutputFormat *fmt; -static AVFormatContext *oc; -static AVStream *video_st; -static double video_pts; - -static int movie_open(int w, int h) { - if (fmt->video_codec != CODEC_ID_NONE) { - video_st = add_video_stream(oc, fmt->video_codec, w, h); - } else - return 1; - - /* set the output parameters (must be done even if no - parameters). */ - if (av_set_parameters(oc, NULL) < 0) { - fprintf(stderr, "Invalid output format parameters\n"); - return 2; +/**************************************************************/ +/* Output movie handling */ +AVFormatContext *movie_open(char *filename, VideoOutputStream *video_st, int br, int fr, int w, int h) { + int ret; + AVFormatContext *oc; + + /* allocate the output media context. */ + ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename); + if (ret < 0) { + fprintf(stderr, "Warning: Could not deduce output format from file extension: using MP4.\n"); + ret = avformat_alloc_output_context2(&oc, NULL, "mp4", filename); } + if (ret < 0) { + fprintf(stderr, "Error: Could not allocate media context: %s.\n", av_err2str(ret)); + return NULL; + } // from now on, need to call avformat_free_context(oc); oc=NULL; to free memory on error - dump_format(oc, 0, filename, 1); - - /* now that all the parameters are set, we can open the audio and - video codecs and allocate the necessary encode buffers */ - if (video_st) - open_video(oc, video_st); + /* Add the video stream using the default format codec and initialize the codec. */ + if (oc->oformat->video_codec != AV_CODEC_ID_NONE) { + ret = add_video_stream(video_st, oc, oc->oformat->video_codec, br, fr, w, h); + } else { + ret = -1; + } + if (ret < 0) { + fprintf(stderr, "Error: chosen output format does not have a video codec, or error %i\n", ret); + avformat_free_context(oc); oc = NULL; + return NULL; + } // from now on, need to call close_video_stream(video_st) to free memory on error + + /* Now that all the parameters are set, we can open the codecs and allocate the necessary encode buffers. */ + ret = open_video(oc, video_st); + if (ret < 0) { + fprintf(stderr, "Error: error opening video codec, error %i\n", ret); + close_video_stream(video_st); + avformat_free_context(oc); oc = NULL; + return NULL; + } // no additional calls required to free memory, as close_video_stream(video_st) will do it /* open the output file, if needed */ - if (!(fmt->flags & AVFMT_NOFILE)) { - if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { - fprintf(stderr, "Could not open '%s'\n", filename); - return 3; + if (!(oc->oformat->flags & AVFMT_NOFILE)) { + ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); + if (ret < 0) { + fprintf(stderr, "Could not open '%s': %s\n", filename, + av_err2str(ret)); + close_video_stream(video_st); + avformat_free_context(oc); oc = NULL; + return NULL; } - } - - /* write the stream header, if any */ - av_write_header(oc); - - return 0; + } // will need to call avio_closep(&oc->pb) to free file handle on error + + /* Write the stream header, if any. */ + ret = avformat_write_header(oc, NULL); + if (ret < 0) { + fprintf(stderr, "Error occurred when writing to output file: %s\n", + av_err2str(ret)); + if (!(oc->oformat->flags & AVFMT_NOFILE)) + avio_closep(&oc->pb); + close_video_stream(video_st); + avformat_free_context(oc); oc = NULL; + } // no additional items to free + + return oc; } -static int movie_close() { - int i; +void movie_close(AVFormatContext **ocp, VideoOutputStream *video_st) { + AVFormatContext *oc = *ocp; + /* Write the trailer, if any. The trailer must be written before you + * close the CodecContexts open when you wrote the header; otherwise + * av_write_trailer() may try to use memory that was freed on + * av_codec_close(). */ + if (oc) { + if (video_st) + write_final_video_frame(oc, video_st); - /* close each codec */ - close_video(oc, video_st); + av_write_trailer(oc); - /* write the trailer, if any */ - av_write_trailer(oc); - - /* free the streams */ - for(i = 0; i < oc->nb_streams; i++) { - av_freep(&oc->streams[i]); - } + /* Close the video codec. */ + close_video_stream(video_st); - if (!(fmt->flags & AVFMT_NOFILE)) { - /* close the output file */ - url_fclose(&oc->pb); - } + if (!(oc->oformat->flags & AVFMT_NOFILE)) + /* Close the output file. */ + avio_closep(&oc->pb); - /* free the stream */ - av_free(oc); + /* free the stream */ + avformat_free_context(oc); + ocp = NULL; + } +} +/**************************************************************/ +/* VNC globals */ +VideoOutputStream video_st = { 0 }; +rfbClient *client = NULL; +rfbBool quit = FALSE; +char *filename = NULL; +AVFormatContext *oc = NULL; +int bitrate = 1000000; +int framerate = 5; +long max_time = 0; +struct timespec start_time, cur_time; + +/* Signal handling */ +void signal_handler(int signal) { + quit=TRUE; } -static rfbBool quit=FALSE; -static void signal_handler(int signal) { - fprintf(stderr,"Cleaning up.\n"); - quit=TRUE; +/* returns time since start in pts units */ +int64_t time_to_pts(int framerate, struct timespec *start_time, struct timespec *cur_time) { + time_t ds = cur_time->tv_sec - start_time->tv_sec; + long dns = cur_time->tv_nsec - start_time->tv_nsec; + /* use usecs */ + int64_t dt = (int64_t)ds*(int64_t)1000000+(int64_t)dns/(int64_t)1000; + /* compute rv in units of frame number (rounding to nearest, not truncating) */ + int64_t rv = (((int64_t)framerate)*dt + (int64_t)500000) / (int64_t)(1000000); + + return rv; } -/**************************************************************/ /* VNC callback functions */ -static rfbBool resize(rfbClient* client) { - static rfbBool first=TRUE; - if(!first) { - movie_close(); - perror("I don't know yet how to change resolutions!\n"); - } - movie_open(client->width, client->height); - signal(SIGINT,signal_handler); - if(tmp_picture) - client->frameBuffer=tmp_picture->data[0]; - else - client->frameBuffer=picture->data[0]; - return TRUE; +rfbBool vnc_malloc_fb(rfbClient* client) { + movie_close(&oc, &video_st); + oc = movie_open(filename, &video_st, bitrate, framerate, client->width, client->height); + if (!oc) + return FALSE; + signal(SIGINT,signal_handler); + signal(SIGTERM,signal_handler); + signal(SIGQUIT,signal_handler); + signal(SIGABRT,signal_handler); + /* These assignments assumes the AVFrame buffer is contigous. This is true in current ffmpeg versions for + * most non-HW accelerated bits, but may not be true globally. */ + if(video_st.tmp_frame) + client->frameBuffer=video_st.tmp_frame->data[0]; + else + client->frameBuffer=video_st.frame->data[0]; + return TRUE; } -static void update(rfbClient* client,int x,int y,int w,int h) { +void vnc_update(rfbClient* client,int x,int y,int w,int h) { } /**************************************************************/ /* media file output */ - int main(int argc, char **argv) { - time_t stop=0; - rfbClient* client; int i,j; - /* get a vnc client structure (don't connect yet). */ + /* Initialize vnc client structure (don't connect yet). */ client = rfbGetClient(5,3,2); client->format.redShift=11; client->format.redMax=31; client->format.greenShift=5; client->format.greenMax=63; client->format.blueShift=0; client->format.blueMax=31; - /* initialize libavcodec, and register all codecs and formats */ + /* Initialize libavcodec, and register all codecs and formats. */ av_register_all(); - - if(!strncmp(argv[argc-1],":",1) || - !strncmp(argv[argc-1],"127.0.0.1",9) || - !strncmp(argv[argc-1],"localhost",9)) - client->appData.encodingsString="raw"; - filename=0; + /* Parse command line. */ for(i=1;i<argc;i++) { - j=i; - if(argc>i+1 && !strcmp("-o",argv[i])) { - filename=argv[2]; - j+=2; - } else if(argc>i+1 && !strcmp("-t",argv[i])) { - stop=time(0)+atoi(argv[i+1]); - j+=2; - } - if(j>i) { - argc-=j-i; - memmove(argv+i,argv+j,(argc-i)*sizeof(char*)); - i--; - } + j=i; + if(argc>i+1 && !strcmp("-o",argv[i])) { + filename=argv[i+1]; + j+=2; + } else if(argc>i+1 && !strcmp("-t",argv[i])) { + max_time=atol(argv[i+1]); + if (max_time < 10 || max_time > 100000000) { + fprintf(stderr, "Warning: Nonsensical time-per-file %li, resetting to default.\n", max_time); + max_time = 0; + } + j+=2; + } + /* This is so that argc/argv are ready for passing to rfbInitClient */ + if(j>i) { + argc-=j-i; + memmove(argv+i,argv+j,(argc-i)*sizeof(char*)); + i--; + } } - - /* auto detect the output format from the name. default is - mpeg. */ - fmt = filename?guess_format(NULL, filename, NULL):0; - if (!fmt) { - printf("Could not deduce output format from file extension: using MPEG.\n"); - fmt = guess_format("mpeg", NULL, NULL); - } - if (!fmt) { - fprintf(stderr, "Could not find suitable output format\n"); - exit(1); + /* default filename. */ + if (!filename) { + fprintf(stderr, "Warning: No filename specified. Using output.mp4\n"); + filename = "output.mp4"; } - - /* allocate the output media context */ - oc = av_alloc_format_context(); - if (!oc) { - fprintf(stderr, "Memory error\n"); - exit(1); - } - oc->oformat = fmt; - snprintf(oc->filename, sizeof(oc->filename), "%s", filename); - - /* add the audio and video streams using the default format codecs - and initialize the codecs */ - video_st = NULL; - /* open VNC connection */ - client->MallocFrameBuffer=resize; - client->GotFrameBufferUpdate=update; + /* open VNC connection. */ + client->MallocFrameBuffer=vnc_malloc_fb; + client->GotFrameBufferUpdate=vnc_update; if(!rfbInitClient(client,&argc,argv)) { - printf("usage: %s [-o output_file] [-t seconds] server:port\n" - "Shoot a movie from a VNC server.\n", argv[0]); - exit(1); + printf("usage: %s [-o output_file] [-t seconds-per-file] server:port\n", argv[0]); + return 1; } - if(client->serverPort==-1) - client->vncRec->doNotSleep = TRUE; /* vncrec playback */ - - /* main loop */ + /* main loop */ + clock_gettime(CLOCK_MONOTONIC, &start_time); while(!quit) { - int i=WaitForMessage(client,1000000/STREAM_FRAME_RATE); - if(i<0) { - movie_close(); - return 0; + int i=WaitForMessage(client,10000/framerate); /* useful for timeout to be no more than 10 msec per second (=10000/framerate usec) */ + if (i>0) { + if(!HandleRFBServerMessage(client)) + quit=TRUE; + } else if (i<0) { + quit=TRUE; } - if(i) - if(!HandleRFBServerMessage(client)) - quit=TRUE; - else { - /* compute current audio and video time */ - video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; - - /* write interleaved audio and video frames */ - write_video_frame(oc, video_st); - } - if(stop!=0 && stop<time(0)) - quit=TRUE; + if (!quit) { + clock_gettime(CLOCK_MONOTONIC, &cur_time); + write_video_frame(oc, &video_st, time_to_pts(framerate, &start_time, &cur_time)); + if ((cur_time.tv_sec - start_time.tv_sec) > max_time && max_time > 0) { + quit = TRUE; + } + } } - - movie_close(); + movie_close(&oc,&video_st); return 0; } |