[asterisk-commits] rizzo: trunk r94774 - /trunk/channels/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Dec 26 04:14:12 CST 2007
Author: rizzo
Date: Wed Dec 26 04:14:11 2007
New Revision: 94774
URL: http://svn.digium.com/view/asterisk?view=rev&rev=94774
Log:
Split console_video.c so that video codecs and gui functions
are in separate files (still #include'd because of tangling in the data
structures, but this is going to be cleaned up).
The video grabbing functions still need to be moved to a separate file.
Added:
trunk/channels/console_gui.c (with props)
trunk/channels/vcodecs.c (with props)
Modified:
trunk/channels/console_video.c
Added: trunk/channels/console_gui.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/console_gui.c?view=auto&rev=94774
==============================================================================
--- trunk/channels/console_gui.c (added)
+++ trunk/channels/console_gui.c Wed Dec 26 04:14:11 2007
@@ -1,0 +1,880 @@
+/*
+ * GUI for console video.
+ * The routines here are in charge of loading the keypad and handling events.
+ * $Revision$
+ */
+
+static void cleanup_sdl(struct video_desc *env)
+{
+ int i;
+
+#ifdef HAVE_SDL_TTF
+ /* unload font file */
+ if (env->gui.font) {
+ TTF_CloseFont(env->gui.font);
+ env->gui.font = NULL;
+ }
+
+ /* uninitialize SDL_ttf library */
+ if ( TTF_WasInit() )
+ TTF_Quit();
+#endif
+
+ /* uninitialize the SDL environment */
+ for (i = 0; i < WIN_MAX; i++) {
+ if (env->win[i].bmp)
+ SDL_FreeYUVOverlay(env->win[i].bmp);
+ }
+ if (env->gui.keypad)
+ SDL_FreeSurface(env->gui.keypad);
+ env->gui.keypad = NULL;
+ SDL_Quit();
+ env->screen = NULL; /* XXX check reference */
+ bzero(env->win, sizeof(env->win));
+ if (env->sdl_ok)
+ ast_mutex_destroy(&(env->in.dec_in_lock));
+}
+
+/*
+ * Display video frames (from local or remote stream) using the SDL library.
+ * - Set the video mode to use the resolution specified by the codec context
+ * - Create a YUV Overlay to copy the frame into it;
+ * - After the frame is copied into the overlay, display it
+ *
+ * The size is taken from the configuration.
+ *
+ * 'out' is 0 for remote video, 1 for the local video
+ */
+static void show_frame(struct video_desc *env, int out)
+{
+ AVPicture *p_in, p_out;
+ struct fbuf_t *b_in, *b_out;
+ SDL_Overlay *bmp;
+
+ if (!env->sdl_ok)
+ return;
+
+ if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
+ b_in = &env->out.enc_in;
+ b_out = &env->out.loc_dpy;
+ p_in = NULL;
+ } else {
+ /* copy input format from the decoding context */
+ AVCodecContext *c = env->in.dec_ctx;
+ b_in = &env->in.dec_out;
+ b_in->pix_fmt = c->pix_fmt;
+ b_in->w = c->width;
+ b_in->h = c->height;
+
+ b_out = &env->in.rem_dpy;
+ p_in = (AVPicture *)env->in.d_frame;
+ }
+ bmp = env->win[out].bmp;
+ SDL_LockYUVOverlay(bmp);
+ /* output picture info - this is sdl, YUV420P */
+ bzero(&p_out, sizeof(p_out));
+ p_out.data[0] = bmp->pixels[0];
+ p_out.data[1] = bmp->pixels[1];
+ p_out.data[2] = bmp->pixels[2];
+ p_out.linesize[0] = bmp->pitches[0];
+ p_out.linesize[1] = bmp->pitches[1];
+ p_out.linesize[2] = bmp->pitches[2];
+
+ my_scale(b_in, p_in, b_out, &p_out);
+
+ /* lock to protect access to Xlib by different threads. */
+ SDL_DisplayYUVOverlay(bmp, &env->win[out].rect);
+ SDL_UnlockYUVOverlay(bmp);
+}
+
+/*
+ * GUI layout, structure and management
+ *
+
+For the GUI we use SDL to create a large surface (env->screen)
+containing tree sections: remote video on the left, local video
+on the right, and the keypad with all controls and text windows
+in the center.
+The central section is built using two images: one is the skin,
+the other one is a mask where the sensitive areas of the skin
+are colored in different grayscale levels according to their
+functions. The mapping between colors and function is defined
+in the 'enum pixel_value' below.
+
+Mouse and keyboard events are detected on the whole surface, and
+handled differently according to their location, as follows:
+
+- drag on the local video window are used to move the captured
+ area (in the case of X11 grabber) or the picture-in-picture
+ location (in case of camera included on the X11 grab).
+- click on the keypad are mapped to the corresponding key;
+- drag on some keypad areas (sliders etc.) are mapped to the
+ corresponding functions;
+- keystrokes are used as keypad functions, or as text input
+ if we are in text-input mode.
+
+To manage these behavior we use two status variables,
+that defines if keyboard events should be redirect to dialing functions
+or to write message functions, and if mouse events should be used
+to implement keypad functionalities or to drag the capture device.
+
+Configuration options control the appeareance of the gui:
+
+ keypad = /tmp/phone.jpg ; the keypad on the screen
+ keypad_font = /tmp/font.ttf ; the font to use for output
+
+ *
+ */
+
+/* enumerate for the pixel value. 0..127 correspond to ascii chars */
+enum pixel_value {
+ /* answer/close functions */
+ KEY_PICK_UP = 128,
+ KEY_HANG_UP = 129,
+
+ /* other functions */
+ KEY_MUTE = 130,
+ KEY_AUTOANSWER = 131,
+ KEY_SENDVIDEO = 132,
+ KEY_LOCALVIDEO = 133,
+ KEY_REMOTEVIDEO = 134,
+ KEY_WRITEMESSAGE = 135,
+ KEY_GUI_CLOSE = 136, /* close gui */
+
+ /* other areas within the keypad */
+ KEY_DIGIT_BACKGROUND = 255,
+
+ /* areas outside the keypad - simulated */
+ KEY_OUT_OF_KEYPAD = 251,
+ KEY_REM_DPY = 252,
+ KEY_LOC_DPY = 253,
+};
+
+/*
+ * Handlers for the various keypad functions
+ */
+
+/*! \brief append a character, or reset if '\0' */
+static void append_char(char *str, int *str_pos, const char c)
+{
+ int i = *str_pos;
+ if (c == '\0')
+ i = 0;
+ else if (i < GUI_BUFFER_LEN - 1)
+ str[i++] = c;
+ else
+ i = GUI_BUFFER_LEN - 1; /* unnecessary, i think */
+ str = '\0';
+ *str_pos = i;
+}
+
+/* accumulate digits, possibly call dial if in connected mode */
+static void keypad_digit(struct video_desc *env, int digit)
+{
+ if (env->owner) { /* we have a call, send the digit */
+ struct ast_frame f = { AST_FRAME_DTMF, 0 };
+
+ f.subclass = digit;
+ ast_queue_frame(env->owner, &f);
+ } else { /* no call, accumulate digits */
+ append_char(env->gui.inbuf, &env->gui.inbuf_pos, digit);
+ }
+}
+
+/* this is a wrapper for actions that are available through the cli */
+/* TODO append arg to command and send the resulting string as cli command */
+static void keypad_send_command(struct video_desc *env, char *command)
+{
+ ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
+ ast_cli_command(env->gui.outfd, command);
+ return;
+}
+
+/* function used to toggle on/off the status of some variables */
+static char *keypad_toggle(struct video_desc *env, int index)
+{
+ ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
+
+ switch (index) {
+ case KEY_SENDVIDEO:
+ env->out.sendvideo = !env->out.sendvideo;
+ break;
+#ifdef notyet
+ case KEY_MUTE: {
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ o->mute = !o->mute;
+ }
+ break;
+ case KEY_AUTOANSWER: {
+ struct chan_oss_pvt *o = find_desc(oss_active);
+ o->autoanswer = !o->autoanswer;
+ }
+ break;
+#endif
+ }
+ return NULL;
+}
+
+char *console_do_answer(int fd);
+/*
+ * Function called when the pick up button is pressed
+ * perform actions according the channel status:
+ *
+ * - if no one is calling us and no digits was pressed,
+ * the operation have no effects,
+ * - if someone is calling us we answer to the call.
+ * - if we have no call in progress and we pressed some
+ * digit, send the digit to the console.
+ */
+static void keypad_pick_up(struct video_desc *env)
+{
+ ast_log(LOG_WARNING, "keypad_pick_up called\n");
+
+ if (env->owner) { /* someone is calling us, just answer */
+ console_do_answer(-1);
+ } else if (env->gui.inbuf_pos) { /* we have someone to call */
+ ast_cli_command(env->gui.outfd, env->gui.inbuf);
+ }
+
+ append_char(env->gui.inbuf, &env->gui.inbuf_pos, '\0'); /* clear buffer */
+}
+
+#if 0 /* still unused */
+/*
+ * As an alternative to SDL_TTF, we can simply load the font from
+ * an image and blit characters on the background of the GUI.
+ *
+ * To generate a font we can use the 'fly' command with the
+ * following script (3 lines with 32 chars each)
+
+size 320,64
+name font.png
+transparent 0,0,0
+string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
+string 255,255,255, 0,20,giant, at ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
+end
+
+ */
+
+/* Print given text on the gui */
+static int gui_output(struct video_desc *env, const char *text)
+{
+#ifndef HAVE_SDL_TTF
+ return 1; /* error, not supported */
+#else
+ int x = 30, y = 20; /* XXX change */
+ SDL_Surface *output = NULL;
+ SDL_Color color = {0, 0, 0}; /* text color */
+ SDL_Rect dest = {env->win[WIN_KEYPAD].rect.x + x, y};
+
+ /* clean surface each rewrite */
+ SDL_BlitSurface(env->gui.keypad, NULL, env->screen, &env->win[WIN_KEYPAD].rect);
+
+ output = TTF_RenderText_Solid(env->gui.font, text, color);
+ if (output == NULL) {
+ ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
+ return 1;
+ }
+
+ SDL_BlitSurface(output, NULL, env->screen, &dest);
+
+ SDL_UpdateRects(env->gui.keypad, 1, &env->win[WIN_KEYPAD].rect);
+ SDL_FreeSurface(output);
+ return 0; /* success */
+#endif
+}
+#endif
+
+static int video_geom(struct fbuf_t *b, const char *s);
+static void sdl_setup(struct video_desc *env);
+static int kp_match_area(const struct keypad_entry *e, int x, int y);
+
+/*
+ * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
+ * index value and calling the right callback.
+ *
+ * x, y are referred to the upper left corner of the main SDL window.
+ */
+static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
+{
+ uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
+
+ /* for each click we come back in normal mode */
+ env->gui.text_mode = 0;
+
+ /* define keypad boundary */
+ if (button.x < env->in.rem_dpy.w)
+ index = KEY_REM_DPY; /* click on remote video */
+ else if (button.x > env->in.rem_dpy.w + env->out.keypad_dpy.w)
+ index = KEY_LOC_DPY; /* click on local video */
+ else if (button.y > env->out.keypad_dpy.h)
+ index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
+ else if (env->gui.kp) {
+ int i;
+ for (i = 0; i < env->gui.kp_used; i++) {
+ if (kp_match_area(&env->gui.kp[i], button.x - env->in.rem_dpy.w, button.y)) {
+ index = env->gui.kp[i].c;
+ break;
+ }
+ }
+ }
+
+ /* exec the function */
+ if (index < 128) { /* surely clicked on the keypad, don't care which key */
+ keypad_digit(env, index);
+ return;
+ }
+ switch (index) {
+ /* answer/close function */
+ case KEY_PICK_UP:
+ keypad_pick_up(env);
+ break;
+ case KEY_HANG_UP:
+ keypad_send_command(env, "console hangup");
+ break;
+
+ /* other functions */
+ case KEY_MUTE:
+ case KEY_AUTOANSWER:
+ case KEY_SENDVIDEO:
+ keypad_toggle(env, index);
+ break;
+
+ case KEY_LOCALVIDEO:
+ break;
+ case KEY_REMOTEVIDEO:
+ break;
+ case KEY_WRITEMESSAGE:
+ /* goes in text-mode */
+ env->gui.text_mode = 1;
+ break;
+
+
+ /* press outside the keypad. right increases size, center decreases, left drags */
+ case KEY_LOC_DPY:
+ case KEY_REM_DPY:
+ if (button.button == SDL_BUTTON_LEFT) {
+ if (index == KEY_LOC_DPY) {
+ /* store points where the drag start
+ * and switch in drag mode */
+ env->gui.x_drag = button.x;
+ env->gui.y_drag = button.y;
+ env->gui.drag_mode = 1;
+ }
+ break;
+ } else {
+ char buf[128];
+ struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->out.loc_dpy : &env->in.rem_dpy;
+ sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
+ fb->w, fb->h);
+ video_geom(fb, buf);
+ sdl_setup(env);
+ }
+ break;
+ case KEY_OUT_OF_KEYPAD:
+ break;
+
+ case KEY_GUI_CLOSE:
+ cleanup_sdl(env);
+ break;
+ case KEY_DIGIT_BACKGROUND:
+ break;
+ default:
+ ast_log(LOG_WARNING, "function not yet defined %i\n", index);
+ }
+}
+
+/*
+ * Handle SDL_KEYDOWN type event, put the key pressed
+ * in the dial buffer or in the text-message buffer,
+ * depending on the text_mode variable value.
+ *
+ * key is the SDLKey structure corresponding to the key pressed.
+ */
+static void handle_keyboard_input(struct video_desc *env, SDLKey key)
+{
+ if (env->gui.text_mode) {
+ /* append in the text-message buffer */
+ if (key == SDLK_RETURN) {
+ /* send the text message and return in normal mode */
+ env->gui.text_mode = 0;
+ keypad_send_command(env, "send text");
+ } else {
+ /* accumulate the key in the message buffer */
+ append_char(env->gui.msgbuf, &env->gui.msgbuf_pos, key);
+ }
+ }
+ else {
+ /* append in the dial buffer */
+ append_char(env->gui.inbuf, &env->gui.inbuf_pos, key);
+ }
+
+ return;
+}
+
+/*
+ * Check if the grab point is inside the X screen.
+ *
+ * x represent the new grab value
+ * limit represent the upper value to use
+ */
+static int boundary_checks(int x, int limit)
+{
+ return (x <= 0) ? 0 : (x > limit ? limit : x);
+}
+
+/* implement superlinear acceleration on the movement */
+static int move_accel(int delta)
+{
+ int d1 = delta*delta / 100;
+ return (delta > 0) ? delta + d1 : delta - d1;
+}
+
+/*
+ * Move the source of the captured video.
+ *
+ * x_final_drag and y_final_drag are the coordinates where the drag ends,
+ * start coordinares are in the gui_info structure.
+ */
+static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
+{
+ int new_x, new_y; /* new coordinates for grabbing local video */
+ int x = env->out.loc_src.x; /* old value */
+ int y = env->out.loc_src.y; /* old value */
+
+ /* move the origin */
+#define POLARITY -1 /* +1 or -1 depending on the desired direction */
+ new_x = x + POLARITY*move_accel(x_final_drag - env->gui.x_drag) * 3;
+ new_y = y + POLARITY*move_accel(y_final_drag - env->gui.y_drag) * 3;
+#undef POLARITY
+ env->gui.x_drag = x_final_drag; /* update origin */
+ env->gui.y_drag = y_final_drag;
+
+ /* check boundary and let the source to grab from the new points */
+ env->out.loc_src.x = boundary_checks(new_x, env->out.screen_width - env->out.loc_src.w);
+ env->out.loc_src.y = boundary_checks(new_y, env->out.screen_height - env->out.loc_src.h);
+ return;
+}
+
+/*
+ * I am seeing some kind of deadlock or stall around
+ * SDL_PumpEvents() while moving the window on a remote X server
+ * (both xfree-4.4.0 and xorg 7.2)
+ * and windowmaker. It is unclear what causes it.
+ */
+
+/* grab a bunch of events */
+static void eventhandler(struct video_desc *env)
+{
+#define N_EVENTS 32
+ int i, n;
+ SDL_Event ev[N_EVENTS];
+
+#define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
+ while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
+ for (i = 0; i < n; i++) {
+#if 0
+ ast_log(LOG_WARNING, "------ event %d at %d %d\n",
+ ev[i].type, ev[i].button.x, ev[i].button.y);
+#endif
+ switch (ev[i].type) {
+ case SDL_KEYDOWN:
+ handle_keyboard_input(env, ev[i].key.keysym.sym);
+ break;
+ case SDL_MOUSEMOTION:
+ if (env->gui.drag_mode != 0)
+ move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ handle_button_event(env, ev[i].button);
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if (env->gui.drag_mode != 0) {
+ move_capture_source(env, ev[i].button.x, ev[i].button.y);
+ env->gui.drag_mode = 0;
+ }
+ break;
+ }
+
+ }
+ }
+ if (1) {
+ struct timeval b, a = ast_tvnow();
+ int i;
+ //SDL_Lock_EventThread();
+ SDL_PumpEvents();
+ b = ast_tvnow();
+ i = ast_tvdiff_ms(b, a);
+ if (i > 3)
+ fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
+ //SDL_Unlock_EventThread();
+ }
+}
+
+static SDL_Surface *get_keypad(const char *file)
+{
+ SDL_Surface *temp;
+
+#ifdef HAVE_SDL_IMAGE
+ temp = IMG_Load(file);
+#else
+ temp = SDL_LoadBMP(file);
+#endif
+ if (temp == NULL)
+ fprintf(stderr, "Unable to load image %s: %s\n",
+ file, SDL_GetError());
+ return temp;
+}
+
+/* TODO: consistency checks, check for bpp, widht and height */
+/* Init the mask image used to grab the action. */
+static int gui_init(struct video_desc *env)
+{
+ /* initialize keypad status */
+ env->gui.text_mode = 0;
+ env->gui.drag_mode = 0;
+
+ /* initialize grab coordinates */
+ env->out.loc_src.x = 0;
+ env->out.loc_src.y = 0;
+
+ /* initialize keyboard buffer */
+ append_char(env->gui.inbuf, &env->gui.inbuf_pos, '\0');
+ append_char(env->gui.msgbuf, &env->gui.msgbuf_pos, '\0');
+
+#ifdef HAVE_SDL_TTF
+ /* Initialize SDL_ttf library and load font */
+ if (TTF_Init() == -1) {
+ ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
+ return -1;
+ }
+
+#define GUI_FONTSIZE 28
+ env->gui.font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
+ if (!env->gui.font) {
+ ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
+ return -1;
+ }
+ ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
+#endif
+
+ env->gui.outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
+ if ( env->gui.outfd < 0 ) {
+ ast_log(LOG_WARNING, "Unable output fd\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
+static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
+ int w, int h, int x, int y)
+{
+ win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
+ if (win->bmp == NULL)
+ return -1; /* error */
+ win->rect.x = x;
+ win->rect.y = y;
+ win->rect.w = w;
+ win->rect.h = h;
+ return 0;
+}
+
+static int keypad_cfg_read(struct gui_info *gui, const char *val);
+
+static void keypad_setup(struct video_desc *env)
+{
+ int fd = -1;
+ void *p = NULL;
+ off_t l = 0;
+
+ if (env->gui.keypad)
+ return;
+ env->gui.keypad = get_keypad(env->keypad_file);
+ if (!env->gui.keypad)
+ return;
+
+ env->out.keypad_dpy.w = env->gui.keypad->w;
+ env->out.keypad_dpy.h = env->gui.keypad->h;
+ /*
+ * If the keypad image has a comment field, try to read
+ * the button location from there. The block must be
+ * keypad_entry = token shape x0 y0 x1 y1 h
+ * ...
+ * (basically, lines have the same format as config file entries.
+ * same as the keypad_entry.
+ * You can add it to a jpeg file using wrjpgcom
+ */
+ do { /* only once, in fact */
+ const char region[] = "region";
+ int reg_len = strlen(region);
+ const unsigned char *s, *e;
+
+ fd = open(env->keypad_file, O_RDONLY);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "fail to open %s\n", env->keypad_file);
+ break;
+ }
+ l = lseek(fd, 0, SEEK_END);
+ if (l <= 0) {
+ ast_log(LOG_WARNING, "fail to lseek %s\n", env->keypad_file);
+ break;
+ }
+ p = mmap(NULL, l, PROT_READ, 0, fd, 0);
+ if (p == NULL) {
+ ast_log(LOG_WARNING, "fail to mmap %s size %ld\n", env->keypad_file, (long)l);
+ break;
+ }
+ e = (const unsigned char *)p + l;
+ for (s = p; s < e - 20 ; s++) {
+ if (!memcmp(s, region, reg_len)) { /* keyword found */
+ /* reset previous entries */
+ keypad_cfg_read(&env->gui, "reset");
+ break;
+ }
+ }
+ for ( ;s < e - 20; s++) {
+ char buf[256];
+ const unsigned char *s1;
+ if (index(" \t\r\n", *s)) /* ignore blanks */
+ continue;
+ if (*s > 127) /* likely end of comment */
+ break;
+ if (memcmp(s, region, reg_len)) /* keyword not found */
+ break;
+ s += reg_len;
+ l = MIN(sizeof(buf), e - s);
+ ast_copy_string(buf, s, l);
+ s1 = ast_skip_blanks(buf); /* between token and '=' */
+ if (*s1++ != '=') /* missing separator */
+ break;
+ if (*s1 == '>') /* skip => */
+ s1++;
+ keypad_cfg_read(&env->gui, ast_skip_blanks(s1));
+ /* now wait for a newline */
+ s1 = s;
+ while (s1 < e - 20 && !index("\r\n", *s1) && *s1 < 128)
+ s1++;
+ s = s1;
+ }
+ } while (0);
+ if (p)
+ munmap(p, l);
+ if (fd >= 0)
+ close(fd);
+}
+
+/* [re]set the main sdl window, useful in case of resize */
+static void sdl_setup(struct video_desc *env)
+{
+ int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
+ int depth, maxw, maxh;
+ const SDL_VideoInfo *info = SDL_GetVideoInfo();
+
+ /* We want at least 16bpp to support YUV overlays.
+ * E.g with SDL_VIDEODRIVER = aalib the default is 8
+ */
+ depth = info->vfmt->BitsPerPixel;
+ if (depth < 16)
+ depth = 16;
+ /*
+ * initialize the SDL environment. We have one large window
+ * with local and remote video, and a keypad.
+ * At the moment we arrange them statically, as follows:
+ * - on the left, the remote video;
+ * - on the center, the keypad
+ * - on the right, the local video
+ */
+
+ keypad_setup(env);
+#define BORDER 5 /* border around our windows */
+ maxw = env->in.rem_dpy.w + env->out.loc_dpy.w + env->out.keypad_dpy.w;
+ maxh = MAX( MAX(env->in.rem_dpy.h, env->out.loc_dpy.h), env->out.keypad_dpy.h);
+ maxw += 4 * BORDER;
+ maxh += 2 * BORDER;
+ env->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
+ if (!env->screen) {
+ ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
+ goto no_sdl;
+ }
+
+ SDL_WM_SetCaption("Asterisk console Video Output", NULL);
+ if (set_win(env->screen, &env->win[WIN_REMOTE], dpy_fmt,
+ env->in.rem_dpy.w, env->in.rem_dpy.h, BORDER, BORDER))
+ goto no_sdl;
+ if (set_win(env->screen, &env->win[WIN_LOCAL], dpy_fmt,
+ env->out.loc_dpy.w, env->out.loc_dpy.h,
+ 3*BORDER+env->in.rem_dpy.w + env->out.keypad_dpy.w, BORDER))
+ goto no_sdl;
+
+ /* display the skin, but do not free it as we need it later to
+ * restore text areas and maybe sliders too.
+ */
+ if (env->gui.keypad) {
+ struct SDL_Rect *dest = &env->win[WIN_KEYPAD].rect;
+ dest->x = 2*BORDER + env->in.rem_dpy.w;
+ dest->y = BORDER;
+ dest->w = env->gui.keypad->w;
+ dest->h = env->gui.keypad->h;
+ SDL_BlitSurface(env->gui.keypad, NULL, env->screen, dest);
+ SDL_UpdateRects(env->screen, 1, dest);
+ }
+ env->in.dec_in_cur = &env->in.dec_in[0];
+ env->in.dec_in_dpy = NULL; /* nothing to display */
+ env->sdl_ok = 1;
+
+no_sdl:
+ if (env->sdl_ok == 0) /* free resources in case of errors */
+ cleanup_sdl(env);
+}
+
+/*
+ * Functions to determine if a point is within a region. Return 1 if success.
+ * First rotate the point, with
+ * x' = (x - x0) * cos A + (y - y0) * sin A
+ * y' = -(x - x0) * sin A + (y - y0) * cos A
+ * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
+ * l = sqrt( (x1-x0)^2 + (y1-y0)^2
+ * Then determine inclusion by simple comparisons i.e.:
+ * rectangle: x >= 0 && x < l && y >= 0 && y < h
+ * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
+ */
+static int kp_match_area(const struct keypad_entry *e, int x, int y)
+{
+ double xp, dx = (e->x1 - e->x0);
+ double yp, dy = (e->y1 - e->y0);
+ double l = sqrt(dx*dx + dy*dy);
+ int ret = 0;
+
+ if (l > 1) { /* large enough */
+ xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
+ yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
+ if (e->type == KP_RECT) {
+ ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
+ } else if (e->type == KP_CIRCLE) {
+ dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
+ ret = (dx < 1);
+ }
+ }
+#if 0
+ ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
+ ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
+#endif
+ return ret;
+}
+
+/*
+ * read a keypad entry line in the format
+ * reset
+ * token circle xc yc diameter
+ * token circle xc yc x1 y1 h # ellipse, main diameter and height
+ * token rect x0 y0 x1 y1 h # rectangle with main side and eight
+ * token is the token to be returned, either a character or a symbol
+ * as KEY_* above
+ */
+struct _s_k { const char *s; int k; };
+static struct _s_k gui_key_map[] = {
+ {"PICK_UP", KEY_PICK_UP },
+ {"PICKUP", KEY_PICK_UP },
+ {"HANG_UP", KEY_HANG_UP },
+ {"HANGUP", KEY_HANG_UP },
+ {"MUTE", KEY_MUTE },
+ {"AUTOANSWER", KEY_AUTOANSWER },
+ {"SENDVIDEO", KEY_SENDVIDEO },
+ {"LOCALVIDEO", KEY_LOCALVIDEO },
+ {"REMOTEVIDEO", KEY_REMOTEVIDEO },
+ {"WRITEMESSAGE", KEY_WRITEMESSAGE },
+ {"GUI_CLOSE", KEY_GUI_CLOSE },
+ {NULL, 0 } };
+
+static int keypad_cfg_read(struct gui_info *gui, const char *val)
+{
+ struct keypad_entry e;
+ char s1[16], s2[16];
+ int i, ret = 0;
+
+ bzero(&e, sizeof(e));
+ i = sscanf(val, "%14s %14s %d %d %d %d %d",
+ s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
+
+ switch (i) {
+ default:
+ break;
+ case 1: /* only "reset" is allowed */
+ if (strcasecmp(s1, "reset")) /* invalid */
+ break;
+ if (gui->kp) {
+ gui->kp_used = 0;
+ }
+ break;
+ case 5: /* token circle xc yc diameter */
+ if (strcasecmp(s2, "circle")) /* invalid */
+ break;
+ e.h = e.x1;
+ e.y1 = e.y0; /* map radius in x1 y1 */
+ e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
+ e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
+ /* fallthrough */
+
+ case 7: /* token circle|rect x0 y0 x1 y1 h */
+ if (e.x1 < e.x0 || e.h <= 0) {
+ ast_log(LOG_WARNING, "error in coordinates\n");
+ e.type = 0;
+ break;
+ }
+ if (!strcasecmp(s2, "circle")) {
+ /* for a circle we specify the diameter but store center and radii */
+ e.type = KP_CIRCLE;
+ e.x0 = (e.x1 + e.x0) / 2;
+ e.y0 = (e.y1 + e.y0) / 2;
+ e.h = e.h / 2;
+ } else if (!strcasecmp(s2, "rect")) {
+ e.type = KP_RECT;
+ } else
+ break;
+ ret = 1;
+ }
+ // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
+ if (ret == 0)
+ return 0;
+ /* map the string into token to be returned */
+ i = atoi(s1);
+ if (i > 0 || s1[1] == '\0') /* numbers or single characters */
+ e.c = (i > 9) ? i : s1[0];
+ else {
+ struct _s_k *p;
+ for (p = gui_key_map; p->s; p++) {
+ if (!strcasecmp(p->s, s1)) {
+ e.c = p->k;
+ break;
+ }
+ }
+ }
+ if (e.c == 0) {
+ ast_log(LOG_WARNING, "missing token\n");
+ return 0;
+ }
+ if (gui->kp_size == 0) {
+ gui->kp = ast_calloc(10, sizeof(e));
+ if (gui->kp == NULL) {
+ ast_log(LOG_WARNING, "cannot allocate kp");
+ return 0;
+ }
+ gui->kp_size = 10;
+ }
+ if (gui->kp_size == gui->kp_used) { /* must allocate */
+ struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
+ if (a == NULL) {
+ ast_log(LOG_WARNING, "cannot reallocate kp");
+ return 0;
+ }
+ gui->kp = a;
+ gui->kp_size += 10;
+ }
+ if (gui->kp_size == gui->kp_used)
+ return 0;
+ gui->kp[gui->kp_used++] = e;
+ return 1;
+}
Propchange: trunk/channels/console_gui.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: trunk/channels/console_gui.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: trunk/channels/console_gui.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: trunk/channels/console_video.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/console_video.c?view=diff&rev=94774&r1=94773&r2=94774
==============================================================================
--- trunk/channels/console_video.c (original)
+++ trunk/channels/console_video.c Wed Dec 26 04:14:11 2007
@@ -111,7 +111,7 @@
void console_video_start(struct video_desc *env, struct ast_channel *owner)
{
- ast_log(LOG_WARNING, "console video support not present\n");
+ ast_log(LOG_NOTICE, "voice only, console video support not present\n");
}
void console_video_uninit(struct video_desc *env)
@@ -163,6 +163,9 @@
int pix_fmt;
};
+static void my_scale(struct fbuf_t *in, AVPicture *p_in,
+ struct fbuf_t *out, AVPicture *p_out);
+
struct video_codec_desc; /* forward declaration */
/*
* Descriptor of the local source, made of the following pieces:
@@ -197,9 +200,10 @@
struct fbuf_t keypad_dpy; /* keypad source buffer, XXX */
struct video_codec_desc *enc; /* encoder */
- AVCodecContext *enc_ctx; /* encoding context */
+ void *enc_ctx; /* encoding context */
AVCodec *codec;
- AVFrame *frame; /* The initial part is an AVPicture */
+ AVFrame *enc_in_frame; /* enc_in mapped into avcodec format. */
+ /* The initial part of AVFrame is an AVPicture */
int mtu;
struct timeval last_frame; /* when we read the last frame ? */
@@ -244,41 +248,6 @@
struct fbuf_t dec_in[N_DEC_IN]; /* incoming bitstream, allocated/extended in fbuf_append() */
struct fbuf_t dec_out; /* decoded frame, no buffer (data is in AVFrame) */
struct fbuf_t rem_dpy; /* display remote image, no buffer (it is in win[WIN_REMOTE].bmp) */
-};
-
-/*
- * Each codec is defined by a number of callbacks
- */
-/*! \brief initialize the encoder */
-typedef int (*encoder_init_f)(struct video_out_desc *v);
-
-/*! \brief actually call the encoder */
-typedef int (*encoder_encode_f)(struct video_out_desc *v);
-
-/*! \brief encapsulate the bistream in RTP frames */
-typedef struct ast_frame *(*encoder_encap_f)(struct video_out_desc *out,
- struct ast_frame **tail);
-
-/*! \brief inizialize the decoder */
-typedef int (*decoder_init_f)(struct video_in_desc *v);
-
-/*! \brief extract the bitstream from RTP frames and store in the fbuf.
- * return 0 if ok, 1 on error
- */
-typedef int (*decoder_decap_f)(struct fbuf_t *b, uint8_t *data, int len);
-
-/*! \brief actually call the decoder */
-typedef int (*decoder_decode_f)(struct video_in_desc *v, struct fbuf_t *b);
-
-struct video_codec_desc {
- const char *name; /* format name */
- int format; /* AST_FORMAT_* */
- encoder_init_f enc_init;
- encoder_encap_f enc_encap;
- encoder_encode_f enc_run;
- decoder_init_f dec_init;
- decoder_decap_f dec_decap;
- decoder_decode_f dec_run;
};
/* our representation of a displayed window. SDL can only do one main
@@ -462,917 +431,8 @@
return f;
}
-/* some debugging code to check the bitstream:
- * declare a bit buffer, initialize it, and fetch data from it.
- */
-struct bitbuf {
- const uint8_t *base;
- int bitsize; /* total size in bits */
- int ofs; /* next bit to read */
-};
-
-static struct bitbuf bitbuf_init(const uint8_t *base, int bitsize, int start_ofs)
-{
- struct bitbuf a;
- a.base = base;
- a.bitsize = bitsize;
- a.ofs = start_ofs;
- return a;
-}
-
-static int bitbuf_left(struct bitbuf *b)
-{
- return b->bitsize - b->ofs;
-}
-
-static uint32_t getbits(struct bitbuf *b, int n)
-{
- int i, ofs;
- const uint8_t *d;
- uint8_t mask;
- uint32_t retval = 0;
- if (n> 31) {
- ast_log(LOG_WARNING, "too many bits %d, max 32\n", n);
- return 0;
- }
- if (n + b->ofs > b->bitsize) {
- ast_log(LOG_WARNING, "bitbuf overflow %d of %d\n", n + b->ofs, b->bitsize);
- n = b->bitsize - b->ofs;
- }
- ofs = 7 - b->ofs % 8; /* start from msb */
- mask = 1 << ofs;
- d = b->base + b->ofs / 8; /* current byte */
- for (i=0 ; i < n; i++) {
- retval += retval + (*d & mask ? 1 : 0); /* shift in new byte */
- b->ofs++;
- mask >>= 1;
- if (mask == 0) {
- d++;
- mask = 0x80;
- }
- }
- return retval;
-}
-
-static void check_h261(struct fbuf_t *b)
-{
- struct bitbuf a = bitbuf_init(b->data, b->used * 8, 0);
- uint32_t x, y;
-
- x = getbits(&a, 20); /* PSC, 0000 0000 0000 0001 0000 */
- if (x != 0x10) {
- ast_log(LOG_WARNING, "bad PSC 0x%x\n", x);
- return;
- }
- x = getbits(&a, 5); /* temporal reference */
- y = getbits(&a, 6); /* ptype */
- if (0)
- ast_log(LOG_WARNING, "size %d TR %d PTY spl %d doc %d freeze %d %sCIF hi %d\n",
- b->used,
- x,
- (y & 0x20) ? 1 : 0,
- (y & 0x10) ? 1 : 0,
- (y & 0x8) ? 1 : 0,
- (y & 0x4) ? "" : "Q",
- (y & 0x2) ? 1:0);
- while ( (x = getbits(&a, 1)) == 1)
- ast_log(LOG_WARNING, "PSPARE 0x%x\n", getbits(&a, 8));
- // ast_log(LOG_WARNING, "PSPARE 0 - start GOB LAYER\n");
- while ( (x = bitbuf_left(&a)) > 0) {
- // ast_log(LOG_WARNING, "GBSC %d bits left\n", x);
- x = getbits(&a, 16); /* GBSC 0000 0000 0000 0001 */
- if (x != 0x1) {
- ast_log(LOG_WARNING, "bad GBSC 0x%x\n", x);
- break;
- }
- x = getbits(&a, 4); /* group number */
- y = getbits(&a, 5); /* gquant */
- if (x == 0) {
- ast_log(LOG_WARNING, " bad GN %d\n", x);
- break;
- }
- while ( (x = getbits(&a, 1)) == 1)
- ast_log(LOG_WARNING, "GSPARE 0x%x\n", getbits(&a, 8));
- while ( (x = bitbuf_left(&a)) > 0) { /* MB layer */
- break;
- }
- }
-}
-
-void dump_buf(struct fbuf_t *b);
-void dump_buf(struct fbuf_t *b)
-{
- int i, x, last2lines;
- char buf[80];
-
- last2lines = (b->used - 16) & ~0xf;
- ast_log(LOG_WARNING, "buf size %d of %d\n", b->used, b->size);
- for (i = 0; i < b->used; i++) {
- x = i & 0xf;
- if ( x == 0) { /* new line */
- if (i != 0)
- ast_log(LOG_WARNING, "%s\n", buf);
- bzero(buf, sizeof(buf));
- sprintf(buf, "%04x: ", i);
- }
- sprintf(buf + 6 + x*3, "%02x ", b->data[i]);
- if (i > 31 && i < last2lines)
- i = last2lines - 1;
- }
- if (buf[0])
- ast_log(LOG_WARNING, "%s\n", buf);
-}
-/*
- * Here starts the glue code for the various supported video codecs.
- * For each of them, we need to provide routines for initialization,
- * calling the encoder, encapsulating the bitstream in ast_frames,
- * extracting payload from ast_frames, and calling the decoder.
- */
-
-/*--- h263+ support --- */
-
-/*! \brief initialization of h263p */
-static int h263p_enc_init(struct video_out_desc *v)
-{
- /* modes supported are
- - Unrestricted Motion Vector (annex D)
- - Advanced Prediction (annex F)
- - Advanced Intra Coding (annex I)
- - Deblocking Filter (annex J)
- - Slice Structure (annex K)
- - Alternative Inter VLC (annex S)
- - Modified Quantization (annex T)
- */
- v->enc_ctx->flags |=CODEC_FLAG_H263P_UMV; /* annex D */
- v->enc_ctx->flags |=CODEC_FLAG_AC_PRED; /* annex f ? */
- v->enc_ctx->flags |=CODEC_FLAG_H263P_SLICE_STRUCT; /* annex k */
- v->enc_ctx->flags |= CODEC_FLAG_H263P_AIC; /* annex I */
-
- v->enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
- return 0;
-}
-
-
-/*
- * Create RTP/H.263 fragments to avoid IP fragmentation. We fragment on a
- * PSC or a GBSC, but if we don't find a suitable place just break somewhere.
- * Everything is byte-aligned.
- */
-static struct ast_frame *h263p_encap(struct video_out_desc *out,
- struct ast_frame **tail)
-{
- struct ast_frame *cur = NULL, *first = NULL;
- uint8_t *d = out->enc_out.data;
- int len = out->enc_out.used;
- int l = len; /* size of the current fragment. If 0, must look for a psc */
-
- for (;len > 0; len -= l, d += l) {
- uint8_t *data;
- struct ast_frame *f;
- int i, h;
-
- if (len >= 3 && d[0] == 0 && d[1] == 0 && d[2] >= 0x80) {
- /* we are starting a new block, so look for a PSC. */
- for (i = 3; i < len - 3; i++) {
- if (d[i] == 0 && d[i+1] == 0 && d[i+2] >= 0x80) {
- l = i;
- break;
- }
- }
- }
- if (l > out->mtu || l > len) { /* psc not found, split */
- l = MIN(len, out->mtu);
- }
- if (l < 1 || l > out->mtu) {
- ast_log(LOG_WARNING, "--- frame error l %d\n", l);
- break;
- }
-
- if (d[0] == 0 && d[1] == 0) { /* we start with a psc */
- h = 0;
- } else { /* no psc, create a header */
- h = 2;
- }
-
- f = create_video_frame(d, d+l, AST_FORMAT_H263_PLUS, h, cur);
- if (!f)
- break;
-
- data = f->data;
- if (h == 0) { /* we start with a psc */
- data[0] |= 0x04; // set P == 1, and we are done
- } else { /* no psc, create a header */
- data[0] = data[1] = 0; // P == 0
- }
-
- if (!cur)
- first = f;
- cur = f;
- }
-
- if (cur)
- cur->subclass |= 1; // RTP Marker
-
- *tail = cur; /* end of the list */
- return first;
-}
-
-/*! \brief extract the bitstreem from the RTP payload.
- * This is format dependent.
- * For h263+, the format is defined in RFC 2429
- * and basically has a fixed 2-byte header as follows:
- * 5 bits RR reserved, shall be 0
- * 1 bit P indicate a start/end condition,
- * in which case the payload should be prepended
- * by two zero-valued bytes.
- * 1 bit V there is an additional VRC header after this header
- * 6 bits PLEN length in bytes of extra picture header
- * 3 bits PEBIT how many bits to be ignored in the last byte
- *
- * XXX the code below is not complete.
- */
-static int h263p_decap(struct fbuf_t *b, uint8_t *data, int len)
-{
- int PLEN;
-
- if (len < 2) {
- ast_log(LOG_WARNING, "invalid framesize %d\n", len);
- return 1;
- }
- PLEN = ( (data[0] & 1) << 5 ) | ( (data[1] & 0xf8) >> 3);
-
- if (PLEN > 0) {
- data += PLEN;
- len -= PLEN;
- }
- if (data[0] & 4) /* bit P */
- data[0] = data[1] = 0;
- else {
- data += 2;
- len -= 2;
- }
- return fbuf_append(b, data, len, 0, 0); /* ignore trail bits */
-}
-
-
-/*
- * generic encoder, used by the various protocols supported here.
- * We assume that the buffer is empty at the beginning.
- */
-static int ffmpeg_encode(struct video_out_desc *v)
-{
- struct fbuf_t *b = &v->enc_out;
- int i;
-
- b->used = avcodec_encode_video(v->enc_ctx, b->data, b->size, v->frame);
- i = avcodec_encode_video(v->enc_ctx, b->data + b->used, b->size - b->used, NULL); /* delayed frames ? */
- if (i > 0) {
- ast_log(LOG_WARNING, "have %d more bytes\n", i);
- b->used += i;
- }
- return 0;
-}
-
-/*
- * Generic decoder, which is used by h263p, h263 and h261 as it simply
- * invokes ffmpeg's decoder.
- * av_parser_parse should merge a randomly chopped up stream into
- * proper frames. After that, if we have a valid frame, we decode it
- * until the entire frame is processed.
- */
-static int ffmpeg_decode(struct video_in_desc *v, struct fbuf_t *b)
-{
- uint8_t *src = b->data;
- int srclen = b->used;
- int full_frame = 0;
-
- if (srclen == 0) /* no data */
- return 0;
- if (0)
- check_h261(b);
- // ast_log(LOG_WARNING, "rx size %d\n", srclen);
- while (srclen) {
- uint8_t *data;
- int datalen, ret;
- int len = av_parser_parse(v->parser, v->dec_ctx, &data, &datalen, src, srclen, 0, 0);
-
- src += len;
- srclen -= len;
- /* The parser might return something it cannot decode, so it skips
- * the block returning no data
- */
- if (data == NULL || datalen == 0)
- continue;
- ret = avcodec_decode_video(v->dec_ctx, v->d_frame, &full_frame, data, datalen);
- if (full_frame == 1) /* full frame */
- break;
- if (ret < 0) {
- ast_log(LOG_NOTICE, "Error decoding\n");
- break;
- }
- }
- if (srclen != 0) /* update b with leftover data */
- bcopy(src, b->data, srclen);
- b->used = srclen;
- b->ebit = 0;
- return full_frame;
-}
-
-static struct video_codec_desc h263p_codec = {
- .name = "h263p",
- .format = AST_FORMAT_H263_PLUS,
- .enc_init = h263p_enc_init,
- .enc_encap = h263p_encap,
- .enc_run = ffmpeg_encode,
- .dec_init = NULL,
- .dec_decap = h263p_decap,
- .dec_run = ffmpeg_decode
-};
-
-/*--- Plain h263 support --------*/
-
-static int h263_enc_init(struct video_out_desc *v)
-{
- /* XXX check whether these are supported */
- v->enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
- v->enc_ctx->flags |= CODEC_FLAG_H263P_AIC;
- v->enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
- v->enc_ctx->flags |= CODEC_FLAG_AC_PRED;
-
- v->enc_ctx->gop_size = v->fps*5;
-
- return 0;
-}
-
-/*
- * h263 encapsulation is specified in RFC2190. There are three modes
- * defined (A, B, C), with 4, 8 and 12 bytes of header, respectively.
- * The header is made as follows
- * 0.....................|.......................|.............|....31
- * F:1 P:1 SBIT:3 EBIT:3 SRC:3 I:1 U:1 S:1 A:1 R:4 DBQ:2 TRB:3 TR:8
- * FP = 0- mode A, (only one word of header)
- * FP = 10 mode B, and also means this is an I or P frame
- * FP = 11 mode C, and also means this is a PB frame.
- * SBIT, EBIT nuber of bits to ignore at beginning (msbits) and end (lsbits)
- * SRC bits 6,7,8 from the h263 PTYPE field
- * I = 0 intra-coded, 1 = inter-coded (bit 9 from PTYPE)
- * U = 1 for Unrestricted Motion Vector (bit 10 from PTYPE)
- * S = 1 for Syntax Based Arith coding (bit 11 from PTYPE)
- * A = 1 for Advanced Prediction (bit 12 from PTYPE)
- * R = reserved, must be 0
- * DBQ = differential quantization, DBQUANT from h263, 0 unless we are using
- * PB frames
- * TRB = temporal reference for bframes, also 0 unless this is a PB frame
- * TR = temporal reference for P frames, also 0 unless PB frame.
- *
- * Mode B and mode C description omitted.
- *
- * An RTP frame can start with a PSC 0000 0000 0000 0000 1000 0
[... 2684 lines stripped ...]
More information about the asterisk-commits
mailing list