video.c

00001 /*
00002  * iaxclient: a cross-platform IAX softphone library
00003  *
00004  * Copyrights:
00005  * Copyright (C) 2005-2006, Horizon Wimba, inc.
00006  * Copyright (C) 2007, Wimba, Inc.
00007  *
00008  * Contributors:
00009  * Steve Kann <stevek@stevek.com>
00010  * Mihai Balea <mihai AT hates DOT ms>
00011  * Peter Grayson <jpgrayson@gmail.com>
00012  *
00013  * This program is free software, distributed under the terms of
00014  * the GNU Lesser (Library) General Public License.
00015  */
00016 
00017 #include <assert.h>
00018 
00019 #include "video.h"
00020 #include "iaxclient_lib.h"
00021 #include "videoLib/video_grab.h"
00022 #include "iax-client.h"
00023 #ifdef USE_FFMPEG
00024 #include "codec_ffmpeg.h"
00025 #endif
00026 #ifdef USE_THEORA
00027 #include "codec_theora.h"
00028 #endif
00029 
00030 #define VIDEO_BUFSIZ (1<<19)
00031 
00032 extern int selected_call;
00033 extern struct iaxc_call * calls;
00034 
00035 static int iaxc_video_width = 320;
00036 static int iaxc_video_height = 240;
00037 static int iaxc_video_framerate = 10; //15;
00038 static int iaxc_video_bitrate = 150000;
00039 static int iaxc_video_fragsize = 1500;
00040 static int iaxc_video_format_preferred = 0;
00041 static int iaxc_video_format_allowed = 0;
00042 static struct iaxc_video_driver video_driver;
00043 
00044 /* Set the default so that the local and remote raw video is
00045  * sent to the client application and encoded video is sent out.
00046  */
00047 static int iaxc_video_prefs = IAXC_VIDEO_PREF_RECV_LOCAL_RAW |
00048                               IAXC_VIDEO_PREF_RECV_REMOTE_RAW;
00049 
00050 #if 0
00051 /* debug: check a yuv420p buffer to ensure it's within the CCIR range */
00052 static int check_ccir_yuv(char *data) {
00053     int i;
00054     unsigned char pix;
00055     int err = 0;
00056 
00057     for(i=0;i<iaxc_video_width * iaxc_video_height; i++) {
00058         pix = *data++;
00059         if( (pix < 16) || pix > 235) {
00060             fprintf(stderr, "check_ccir_yuv: Y pixel[%d] out of range: %d\n", i, pix);
00061             err++;
00062         }
00063     }
00064     for(i=0;i< iaxc_video_width * iaxc_video_height / 2; i++) {
00065         pix = *data++;
00066         if( (pix < 16) || pix > 239) {
00067             fprintf(stderr, "check_ccir_yuv: U/V pixel[%d] out of range: %d\n", i, pix);
00068             err++;
00069         }
00070     }
00071     return err;
00072 }
00073 #endif
00074 
00075 EXPORT unsigned int iaxc_get_video_prefs(void)
00076 {
00077         return iaxc_video_prefs;
00078 }
00079 
00080 EXPORT int iaxc_set_video_prefs(unsigned int prefs)
00081 {
00082         const unsigned int prefs_mask =
00083                 IAXC_VIDEO_PREF_RECV_LOCAL_RAW      |
00084                 IAXC_VIDEO_PREF_RECV_LOCAL_ENCODED  |
00085                 IAXC_VIDEO_PREF_RECV_REMOTE_RAW     |
00086                 IAXC_VIDEO_PREF_RECV_REMOTE_ENCODED |
00087                 IAXC_VIDEO_PREF_SEND_DISABLE        |
00088                 IAXC_VIDEO_PREF_RECV_RGB32          |
00089                 IAXC_VIDEO_PREF_CAPTURE_DISABLE;
00090 
00091         if ( prefs & ~prefs_mask )
00092                 return -1;
00093 
00094         /* Not sending any video and not needing any form of
00095          * local video implies that we do not need to capture
00096          * video.
00097          */
00098         if ( prefs & IAXC_VIDEO_PREF_CAPTURE_DISABLE ||
00099                         ((prefs & IAXC_VIDEO_PREF_SEND_DISABLE) &&
00100                          !(prefs & IAXC_VIDEO_PREF_RECV_LOCAL_RAW) &&
00101                          !(prefs & IAXC_VIDEO_PREF_RECV_LOCAL_ENCODED)) )
00102         {
00103                 /* Note that in both the start and stop cases, we
00104                  * rely on the start/stop function to be idempotent.
00105                  */
00106                 if (video_driver.stop)
00107                         video_driver.stop(&video_driver);
00108         }
00109         else
00110         {
00111                 if ( video_driver.start )
00112                 {
00113                         video_driver.start(&video_driver);
00114 
00115                         // Driver may fail to start
00116                         if ( !video_driver.is_camera_working(&video_driver) )
00117                                 return -1;
00118                 }
00119         }
00120 
00121         iaxc_video_prefs = prefs;
00122 
00123         return 0;
00124 }
00125 
00126 EXPORT void iaxc_video_format_set_cap(int preferred, int allowed)
00127 {
00128         iaxc_video_format_preferred = preferred;
00129         iaxc_video_format_allowed = allowed;
00130 }
00131 
00132 EXPORT void iaxc_video_format_get_cap(int *preferred, int *allowed)
00133 {
00134         *preferred = iaxc_video_format_preferred;
00135         *allowed = iaxc_video_format_allowed;
00136 }
00137 
00138 EXPORT void iaxc_video_format_set(int preferred, int allowed, int framerate,
00139                 int bitrate, int width, int height, int fs)
00140 {
00141         int real_pref = 0;
00142         int real_allowed = 0;
00143 #ifdef USE_FFMPEG
00144         int tmp_allowed;
00145         int i;
00146 #endif
00147 
00148         // Make sure resolution is in range
00149         if ( width < IAXC_VIDEO_MIN_WIDTH )
00150                 width = IAXC_VIDEO_MIN_WIDTH;
00151         else if ( width > IAXC_VIDEO_MAX_WIDTH )
00152                 width = IAXC_VIDEO_MAX_WIDTH;
00153 
00154         if ( height < IAXC_VIDEO_MIN_HEIGHT )
00155                 height = IAXC_VIDEO_MIN_HEIGHT;
00156         else if ( height > IAXC_VIDEO_MAX_HEIGHT )
00157                 height = IAXC_VIDEO_MAX_HEIGHT;
00158 
00159         iaxc_video_framerate = framerate;
00160         iaxc_video_bitrate = bitrate;
00161         iaxc_video_width = width;
00162         iaxc_video_height = height;
00163         iaxc_video_fragsize = fs;
00164 
00165         iaxc_video_format_allowed = 0;
00166         iaxc_video_format_preferred = 0;
00167 
00168         if ( preferred && (preferred & ~IAXC_VIDEO_FORMAT_MASK) )
00169         {
00170                 fprintf(stderr, "ERROR: Preferred video format invalid.\n");
00171                 preferred = 0;
00172         }
00173 
00174         /* This check:
00175          * 1. Check if preferred is a supported and compiled in codec. If
00176          *    not, switch to the default preferred format.
00177          * 2. Check if allowed contains a list of all supported and compiled
00178          *    in codec. If there are some unavailable codec, remove it from
00179          *    this list.
00180          */
00181 
00182         if ( preferred & IAXC_FORMAT_THEORA )
00183                 real_pref = IAXC_FORMAT_THEORA;
00184 
00185 #ifdef USE_FFMPEG
00186         if ( codec_video_ffmpeg_check_codec(preferred) )
00187                 real_pref = preferred;
00188 #endif
00189 
00190         if ( !real_pref )
00191         {
00192                 // If preferred codec is not available switch to the
00193                 // supported default codec.
00194                 fprintf(stderr, "Preferred codec (0x%08x) is not available. Switching to default", preferred);
00195                 real_pref = IAXC_FORMAT_THEORA;
00196         }
00197 
00198         /* Check on allowed codecs availability */
00199 
00200         if ( allowed & IAXC_FORMAT_THEORA )
00201                 real_allowed |= IAXC_FORMAT_THEORA;
00202 
00203 #ifdef USE_FFMPEG
00204         /* TODO: This codec_video_ffmpeg_check_codec stuff is bogus. We
00205          * need a standard interface in our codec wrappers that allows us to
00206          * (1) test if a selected codec is valid and/or (2) return the set of
00207          * available valid codecs. With that, we should be able to come up
00208          * with a more elegant algorithm here for determining the video codec.
00209          */
00210         for ( i = 0; i <= 24; i++)
00211         {
00212                 tmp_allowed = 1 << i;
00213                 if ( (allowed & tmp_allowed)  &&
00214                                  codec_video_ffmpeg_check_codec(tmp_allowed) )
00215                         real_allowed |= tmp_allowed;
00216         }
00217 #endif
00218 
00219         if ( !real_pref )
00220         {
00221                 fprintf(stderr, "Audio-only client!\n");
00222         } else
00223         {
00224                 iaxc_video_format_preferred = real_pref;
00225 
00226                 /*
00227                  * When a client use a 'preferred' format, it can force to
00228                  * use allowed formats using a non-zero value for 'allowed'
00229                  * parameter. If it is left 0, the client will use all
00230                  * capabilities set by default in this code.
00231                  */
00232                 if ( real_allowed )
00233                 {
00234                         iaxc_video_format_allowed = real_allowed;
00235                 } else
00236                 {
00237 #ifdef USE_FFMPEG
00238                         iaxc_video_format_allowed |= IAXC_FORMAT_H263_PLUS
00239                                 | IAXC_FORMAT_H263
00240                                 | IAXC_FORMAT_MPEG4
00241                                 | IAXC_FORMAT_H264;
00242 #endif
00243                         iaxc_video_format_allowed |= IAXC_FORMAT_THEORA;
00244                 }
00245         }
00246 }
00247 
00248 void iaxc_video_params_change(int framerate, int bitrate, int width,
00249                 int height, int fs)
00250 {
00251         struct iaxc_call *call;
00252 
00253         /* set default video params */
00254         if ( framerate > 0 )
00255                 iaxc_video_framerate = framerate;
00256         if ( bitrate > 0 )
00257                 iaxc_video_bitrate = bitrate;
00258         if ( width > 0 )
00259                 iaxc_video_width = width;
00260         if ( height > 0 )
00261                 iaxc_video_height = height;
00262         if ( fs > 0 )
00263                 iaxc_video_fragsize = fs;
00264 
00265         if ( selected_call < 0 )
00266                 return;
00267 
00268         call = &calls[selected_call];
00269 
00270         if ( !call || !call->vencoder )
00271                 return;
00272 
00273         call->vencoder->params_changed = 1;
00274 
00275         if ( framerate > 0 )
00276                 call->vencoder->framerate = framerate;
00277         if ( bitrate > 0 )
00278                 call->vencoder->bitrate = bitrate;
00279         if ( width > 0 )
00280                 call->vencoder->width = width;
00281         if ( height > 0 )
00282                 call->vencoder->height = height;
00283         if ( fs > 0 )
00284                 call->vencoder->fragsize = fs;
00285 }
00286 
00287 static void reset_codec_stats(struct iaxc_video_codec *vcodec)
00288 {
00289         if ( !vcodec )
00290                 return;
00291 
00292         memset(&vcodec->video_stats, 0, sizeof(struct iaxc_video_stats));
00293         gettimeofday(&vcodec->video_stats.start_time, 0);
00294 }
00295 
00296 static void reset_video_stats(struct iaxc_call *call)
00297 {
00298         if ( !call )
00299                 return;
00300 
00301         reset_codec_stats(call->vdecoder);
00302         reset_codec_stats(call->vencoder);
00303 }
00304 
00305 /* Collect and return video statistics. Also reset statistics if required.
00306  * Returns a pointer to the data.
00307  * Right now we use two different codecs for encoding and decoding. We need
00308  * to collate information from both and wrap it into one nice struct.
00309  */
00310 static int get_stats(struct iaxc_call *call, struct iaxc_video_stats *stats,
00311                 int reset)
00312 {
00313         if ( !call || !stats )
00314                 return -1;
00315 
00316         memset(stats, 0, sizeof(*stats));
00317 
00318         if ( call->vencoder )
00319         {
00320                 stats->sent_slices = call->vencoder->video_stats.sent_slices;
00321                 stats->acc_sent_size = call->vencoder->video_stats.acc_sent_size;
00322                 stats->outbound_frames = call->vencoder->video_stats.outbound_frames;
00323                 stats->avg_outbound_bps = call->vencoder->video_stats.avg_outbound_bps;
00324                 stats->avg_outbound_fps = call->vencoder->video_stats.avg_outbound_fps;
00325         }
00326 
00327         if ( call->vdecoder )
00328         {
00329                 stats->received_slices = call->vdecoder->video_stats.received_slices;
00330                 stats->acc_recv_size = call->vdecoder->video_stats.acc_recv_size;
00331                 stats->inbound_frames = call->vdecoder->video_stats.inbound_frames;
00332                 stats->dropped_frames = call->vdecoder->video_stats.dropped_frames;
00333                 stats->avg_inbound_bps = call->vdecoder->video_stats.avg_inbound_bps;
00334                 stats->avg_inbound_fps = call->vdecoder->video_stats.avg_inbound_fps;
00335         }
00336 
00337         if ( reset )
00338                 reset_video_stats(call);
00339 
00340         return 0;
00341 }
00342 
00343 /* TODO: The encode parameter to this function is unused within this
00344  * function. However, clients of this function still use this parameter.
00345  * What ends up happening is we instantiate the codec encoder/decoder
00346  * pairs multiple times. This seems not the best. For example, it is
00347  * not clear that all codecs are idempotent to the extent that they can
00348  * be initialized multiple times. Furthermore, within iaxclient itself,
00349  * we have a nasty habit of using global statically allocated data.
00350  * Multiple codec_video_*_new calls could easily result in race
00351  * conditions or more blatantly bad problems.
00352  *
00353  * Splitting encoder and decoder creation has some merit.
00354  *
00355  *  - Avoid allocating encoder or decoder resources when not needed.
00356  *  - Allows using different codecs for sending and receiving.
00357  *  - Allows different codec parameters for sending and receiving.
00358  *
00359  */
00360 static struct iaxc_video_codec *create_codec(int format, int encode)
00361 {
00362         struct iaxc_video_codec * vcodec = 0;
00363 
00364         iaxci_usermsg(IAXC_TEXT_TYPE_NOTICE, "Creating codec format 0x%x",
00365                         format);
00366 
00367         switch ( format )
00368         {
00369         case IAXC_FORMAT_H261:
00370         case IAXC_FORMAT_H263:
00371         case IAXC_FORMAT_H263_PLUS:
00372         case IAXC_FORMAT_MPEG4:
00373         case IAXC_FORMAT_H264:
00374 #ifdef USE_FFMPEG
00375                 vcodec = codec_video_ffmpeg_new(format,
00376                                 iaxc_video_width,
00377                                 iaxc_video_height,
00378                                 iaxc_video_framerate,
00379                                 iaxc_video_bitrate,
00380                                 iaxc_video_fragsize);
00381 #endif
00382                 break;
00383 
00384         case IAXC_FORMAT_THEORA:
00385 #ifdef USE_THEORA
00386                 vcodec = codec_video_theora_new(format,
00387                                 iaxc_video_width,
00388                                 iaxc_video_height,
00389                                 iaxc_video_framerate,
00390                                 iaxc_video_bitrate,
00391                                 iaxc_video_fragsize);
00392 #endif
00393                 break;
00394         }
00395 
00396         reset_codec_stats(vcodec);
00397 
00398         return vcodec;
00399 }
00400 
00401 /*
00402  * show_video_frame - returns video data to the main application
00403  * using the callback mechanism
00404  * This function creates a dynamic copy of the video data.  The memory is freed
00405  * in iaxci_post_event. This is because the event we post may be queued and the
00406  * frame data must live until after it is dequeued.
00407  * Parameters: - videobuf: buffer containing raw or encoded video data
00408  *             - size - size of video data block
00409  *             - cn - call number
00410  *             - source - either IAXC_SOURCE_LOCAL or IAXC_SOURCE_REMOTE
00411  *             - encoded - true if data is encoded
00412  *             - rgb32 - if true, convert data to RGB32 before showing
00413 
00414  \todo For encoded data, set the event format to the calls video format. For raw data, set the format to 0.
00415  */
00416 void show_video_frame(char *videobuf, int size, int cn, int source, int encoded,
00417                 unsigned int ts, int rgb32)
00418 {
00419         iaxc_event e;
00420         char * buffer;
00421 
00422         e.type = IAXC_EVENT_VIDEO;
00423         e.ev.video.ts = ts;
00424 
00425         if ( size <= 0 )
00426                 fprintf(stderr, "WARNING: size %d in show_video_frame\n", size);
00427 
00428         if ( !encoded && rgb32 )
00429         {
00430                 e.ev.video.size = iaxc_video_height * iaxc_video_width * 4;
00431                 buffer = (char *)malloc(e.ev.video.size);
00432                 assert(buffer);
00433                 e.ev.video.data = buffer;
00434                 iaxc_YUV420_to_RGB32(iaxc_video_width, iaxc_video_height,
00435                                 videobuf, buffer);
00436         } else
00437         {
00438                 buffer = (char *)malloc(size);
00439                 assert(buffer);
00440                 memcpy(buffer, videobuf, size);
00441                 e.ev.video.data = buffer;
00442                 e.ev.video.size = size;
00443         }
00444 
00445         e.ev.video.format = iaxc_video_format_preferred;
00446         e.ev.video.width = iaxc_video_width;
00447         e.ev.video.height = iaxc_video_height;
00448         e.ev.video.callNo = cn;
00449         e.ev.video.encoded = encoded;
00450         assert(source == IAXC_SOURCE_REMOTE || source == IAXC_SOURCE_LOCAL);
00451         e.ev.video.source = source;
00452 
00453         iaxci_post_event(e);
00454 }
00455 
00456 /* try to get the next frame, encode and send */
00457 int video_send_video(struct iaxc_call *call, int sel_call)
00458 {
00459         static struct slice_set_t slice_set;
00460         int format;
00461         int i = 0;
00462         const int inlen = iaxc_video_width * iaxc_video_height * 6 / 4;
00463         char * videobuf;
00464         struct timeval now;
00465         long time;
00466 
00467         video_driver.input(&video_driver, &videobuf);
00468 
00469         /* It is okay if we do not get any video; video capture may be
00470          * disabled.
00471          */
00472         if ( !videobuf || (iaxc_video_prefs & IAXC_VIDEO_PREF_CAPTURE_DISABLE) )
00473                 return 0;
00474 
00475         // Send the raw frame to the main app, if necessary
00476         if ( iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_LOCAL_RAW )
00477         {
00478                 show_video_frame(videobuf, inlen, -1, IAXC_SOURCE_LOCAL, 0, 0,
00479                                 iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_RGB32);
00480         }
00481 
00482         if ( sel_call < 0 || !call ||
00483                         !(call->state & (IAXC_CALL_STATE_COMPLETE |
00484                                         IAXC_CALL_STATE_OUTGOING)) )
00485         {
00486                 return -1;
00487         }
00488 
00489         // use the calls format, not random preference
00490         format = call->vformat;
00491 
00492         if ( format == 0 )
00493         {
00494 //              fprintf(stderr, "video_send_video: Format is zero (should't happen)!\n");
00495                 return -1;
00496         }
00497 
00498         // If we don't need to send encoded video to the network or back
00499         // to the main application, just return here.
00500         if ( ( !(iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_LOCAL_ENCODED) &&
00501                (iaxc_video_prefs & IAXC_VIDEO_PREF_SEND_DISABLE) ) || 
00502              (format == 0) )
00503         {
00504                 if ( call->vencoder )
00505                 {
00506                         // We don't need to encode video so just destroy the encoder
00507                         fprintf(stderr, "Destroying codec %s\n", call->vencoder->name);
00508                         call->vencoder->destroy(call->vencoder);
00509                         call->vencoder = NULL;
00510                 }
00511                 return 0;
00512         }else
00513         {
00514                 /* destroy vencoder if it is incorrect type */
00515                 if ( call->vencoder &&
00516                      (call->vencoder->format != format || call->vencoder->params_changed)
00517                    )
00518                 {
00519                         call->vencoder->destroy(call->vencoder);
00520                         call->vencoder = NULL;
00521                 }
00522 
00523                 /* create encoder if necessary */
00524                 if ( !call->vencoder )
00525                 {
00526                         call->vencoder = create_codec(format, 1);
00527                         fprintf(stderr,"**** Created encoder codec %s\n",call->vencoder->name);
00528                 }
00529 
00530                 if ( !call->vencoder )
00531                 {
00532                         fprintf(stderr,
00533                                 "ERROR: Video codec could not be created: 0x%08x\n",
00534                                 format);
00535                         return -1;
00536                 }
00537 
00538                 // encode the frame
00539                 if ( call->vencoder->encode(call->vencoder, inlen, videobuf,
00540                                         &slice_set) )
00541                 {
00542                         fprintf(stderr, "video_send_video: encode failed\n");
00543                         return -1;
00544                 }
00545         }
00546 
00547         // Statistics
00548         gettimeofday(&now, 0);
00549         call->vencoder->video_stats.outbound_frames++;
00550         time = iaxci_msecdiff(&now, &call->vencoder->video_stats.start_time);
00551         if ( time > 0 )
00552                 call->vencoder->video_stats.avg_outbound_fps =
00553                         (float)call->vencoder->video_stats.outbound_frames *
00554                         1000 / time;
00555 
00556         // send the frame!
00557 
00558         if ( !call->session )
00559                 return -1;
00560 
00561         for ( i = 0; i < slice_set.num_slices; i++ )
00562         {
00563                 //Pass the encoded frame to the main app
00564                 // \todo Fix the call number
00565                 if ( iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_LOCAL_ENCODED )
00566                 {
00567                         show_video_frame(slice_set.data[i], slice_set.size[i],
00568                                         -1, IAXC_SOURCE_LOCAL, 1, 0, 0);
00569                 }
00570 
00571                 if ( !(iaxc_video_prefs & IAXC_VIDEO_PREF_SEND_DISABLE) )
00572                 {
00573                         if ( iax_send_video_trunk(call->session, format,
00574                                                 slice_set.data[i],
00575                                                 slice_set.size[i],
00576                                                 0, i) == -1 )
00577                         {
00578                                 fprintf(stderr, "Failed to send a slice, call %d, size %d\n",
00579                                                 sel_call, slice_set.size[i]);
00580                                 return -1;
00581                         }
00582 
00583                         // Statistics
00584                         call->vencoder->video_stats.sent_slices++;
00585                         call->vencoder->video_stats.acc_sent_size +=
00586                                 slice_set.size[i];
00587                         if ( time > 0 )
00588                                 call->vencoder->video_stats.avg_outbound_bps =
00589                                         call->vencoder->video_stats.acc_sent_size *
00590                                         8000 / time;
00591                 }
00592         }
00593 
00594         return 0;
00595 }
00596 
00597 /* process an incoming video frame */
00598 int video_recv_video(struct iaxc_call *call, int sel_call,
00599                 void *encoded_video, int encoded_video_len,
00600                 unsigned int ts, int format)
00601 {
00602         static char videobuf[VIDEO_BUFSIZ];
00603         int outsize = VIDEO_BUFSIZ;
00604         int ret_dec;
00605         struct timeval now;
00606         long time;
00607 
00608         if ( !call )
00609                 return 0;
00610 
00611         if ( format == 0 )
00612         {
00613                 fprintf(stderr, "video_recv_video: Format is zero (should't happen)!\n");
00614                 return -1;
00615         }
00616 
00617         // Send the encoded frame to the main app if necessary
00618         if ( iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_REMOTE_ENCODED )
00619         {
00620                 show_video_frame((char *)encoded_video, encoded_video_len, -1,
00621                                 IAXC_SOURCE_REMOTE, 1, ts, 0);
00622         }
00623 
00624         /* destroy vdecoder if it is incorrect type */
00625         if ( call->vdecoder && call->vdecoder->format != format )
00626         {
00627                 call->vdecoder->destroy(call->vdecoder);
00628                 call->vdecoder = NULL;
00629         }
00630 
00631         /* If the user does not need decoded video, then do not decode. */
00632         if ( !(iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_REMOTE_RAW) )
00633                 return 0;
00634 
00635         /* create decoder if necessary */
00636         if ( !call->vdecoder )
00637         {
00638                 call->vdecoder = create_codec(format, 0);
00639                 fprintf(stderr,"**** Created decoder codec %s\n",call->vdecoder->name);
00640         }
00641 
00642         if ( !call->vdecoder )
00643         {
00644                 fprintf(stderr, "ERROR: Video codec could not be created: %d\n",
00645                                 format);
00646                 return -1;
00647         }
00648 
00649         /* Statistics */
00650         gettimeofday(&now, 0);
00651         time = iaxci_msecdiff(&now, &call->vdecoder->video_stats.start_time);
00652         call->vdecoder->video_stats.received_slices++;
00653         call->vdecoder->video_stats.acc_recv_size += encoded_video_len;
00654         if ( time > 0 )
00655                 call->vdecoder->video_stats.avg_inbound_bps =
00656                         call->vdecoder->video_stats.acc_recv_size * 8000 / time;
00657 
00658         ret_dec = call->vdecoder->decode(call->vdecoder, encoded_video_len,
00659                         (char *)encoded_video, &outsize, videobuf);
00660 
00661         if ( ret_dec < 0 )
00662         {
00663                 fprintf(stderr, "ERROR: decode error\n");
00664                 return -1;
00665         }
00666         else if ( ret_dec > 0 )
00667         {
00668                 /* This indicates that a complete frame cannot yet
00669                  * be decoded. This is okay.
00670                  */
00671                 return 0;
00672         }
00673 
00674         /* Statistics */
00675         call->vdecoder->video_stats.inbound_frames++;
00676         if ( time > 0 )
00677                 call->vdecoder->video_stats.avg_inbound_fps =
00678                         call->vdecoder->video_stats.inbound_frames *
00679                         1000.0F / time;
00680 
00681         if ( outsize > 0 )
00682         {
00683                 show_video_frame(videobuf, outsize, sel_call,
00684                                 IAXC_SOURCE_REMOTE, 0, ts,
00685                                 iaxc_video_prefs & IAXC_VIDEO_PREF_RECV_RGB32);
00686         }
00687 
00688         return 0;
00689 }
00690 
00691 int video_initialize(void)
00692 {
00693         if ( pv_initialize(&video_driver, iaxc_video_width, iaxc_video_height,
00694                         iaxc_video_framerate) )
00695         {
00696                 fprintf(stderr, "ERROR: cannot initialize pv\n");
00697                 return -1;
00698         }
00699 
00700         /* We reset the existing video preferences to yield the side-effect
00701          * of potentially starting or stopping the video capture.
00702          */
00703         iaxc_set_video_prefs(iaxc_video_prefs);
00704 
00705         return 0;
00706 }
00707 
00708 int video_destroy(void)
00709 {
00710         if ( video_driver.destroy )
00711                 return video_driver.destroy(&video_driver);
00712         else
00713                 return -1;
00714 }
00715 
00716 /*
00717  * YUV to RGB conversion
00718  */
00719 
00720 #define CLIP_SIZE   811
00721 #define CLIP_OFFSET 277
00722 
00723 #define YMUL  298
00724 #define RMUL  409
00725 #define BMUL  516
00726 #define G1MUL -100
00727 #define G2MUL -208
00728 
00729 static int           yuv2rgb_y[256];
00730 static int           yuv2rgb_r[256];
00731 static int           yuv2rgb_b[256];
00732 static int           yuv2rgb_g1[256];
00733 static int           yuv2rgb_g2[256];
00734 static unsigned long yuv2rgb_clip[CLIP_SIZE];
00735 static unsigned long yuv2rgb_clip8[CLIP_SIZE];
00736 static unsigned long yuv2rgb_clip16[CLIP_SIZE];
00737 static int           yuv2rgb_tables_initialized = 0;
00738 
00739 #define RED(y, v)     yuv2rgb_clip16[CLIP_OFFSET + yuv2rgb_y[y] + yuv2rgb_r[v]]
00740 #define GREEN(y,v,u)  yuv2rgb_clip8[CLIP_OFFSET + yuv2rgb_y[y] + yuv2rgb_g1[v] + yuv2rgb_g2[u]]
00741 #define BLUE(y,u)     yuv2rgb_clip[CLIP_OFFSET + yuv2rgb_y[y] + yuv2rgb_b[u]]
00742 
00743 static void iaxc_init_yuv2rgb_tables(void)
00744 {
00745         int i;
00746 
00747         for (i = 0; i < 256; i++)
00748         {
00749                 yuv2rgb_y[i] = (YMUL * (i - 16) + 128) >> 8;
00750                 yuv2rgb_r[i] = (RMUL * (i - 128)) >> 8;
00751                 yuv2rgb_b[i] = (BMUL * (i - 128)) >> 8;
00752                 yuv2rgb_g1[i] = (G1MUL * (i - 128)) >> 8;
00753                 yuv2rgb_g2[i] = (G2MUL * (i - 128)) >> 8;
00754         }
00755         for ( i = 0 ; i < CLIP_OFFSET ; i++ )
00756         {
00757                 yuv2rgb_clip[i] = 0;
00758                 yuv2rgb_clip8[i] = 0;
00759                 yuv2rgb_clip16[i] = 0;
00760         }
00761         for ( ; i < CLIP_OFFSET + 256 ; i++ )
00762         {
00763                 yuv2rgb_clip[i] = i - CLIP_OFFSET;
00764                 yuv2rgb_clip8[i] = (i - CLIP_OFFSET) << 8;
00765                 yuv2rgb_clip16[i] = (i - CLIP_OFFSET) << 16;
00766         }
00767         for ( ; i < CLIP_SIZE ; i++ )
00768         {
00769                 yuv2rgb_clip[i] = 255;
00770                 yuv2rgb_clip8[i] = 255 << 8;
00771                 yuv2rgb_clip16[i] = 255 << 16;
00772         }
00773 
00774         yuv2rgb_tables_initialized = 1;
00775 }
00776 
00777 /*
00778  * Faster function to convert YUV420 images to RGB32
00779  * RGB32: 0xFFRRGGBB
00780  * This function uses precalculated tables that are initialized
00781  * on the first run.
00782  * Make sure the src and dest buffers have enough room
00783  * dest should be width * height * 4 bytes in size
00784  * Based on the formulas found at http://en.wikipedia.org/wiki/YUV
00785  */
00786 void iaxc_YUV420_to_RGB32(int width, int height, char *src, char *dest)
00787 {
00788         unsigned char *y, *u, *v;
00789         unsigned int *dst;
00790         int i;
00791 
00792         if ( !yuv2rgb_tables_initialized )
00793                 iaxc_init_yuv2rgb_tables();
00794 
00795         dst = (unsigned int *)dest;
00796         y  = (unsigned char *)src;
00797         u  = y + width * height;
00798         v  = u + width * height / 4;
00799 
00800         for (i = 0; i < height; i++)
00801         {
00802                 unsigned char *uu,*vv;
00803                 unsigned int *d;
00804                 int j;
00805 
00806                 d = dst;
00807                 uu = u;
00808                 vv = v;
00809                 for ( j = 0; j < width >> 1; j++ )
00810                 {
00811                         *(d++) = 0xff000000 |
00812                                  RED(*y,*vv) |
00813                                  GREEN(*y,*vv,*uu) |
00814                                  BLUE(*y,*uu);
00815                         y++;
00816                         *(d++) = 0xff000000 |
00817                                  RED(*y,*vv) |
00818                                  GREEN(*y,*vv,*uu) |
00819                                  BLUE(*y,*uu);
00820                         y++;
00821                         uu++;
00822                         vv++;
00823                 }
00824                 if ( i & 1 )
00825                 {
00826                         u += width >> 1;
00827                         v += width >> 1;
00828                 }
00829                 dst += width;
00830         }
00831 }
00832 
00833 int iaxc_is_camera_working()
00834 {
00835         return video_driver.is_camera_working(&video_driver);
00836 }
00837 
00838 int video_send_stats(struct iaxc_call * call)
00839 {
00840         const long video_stats_interval = 1000; /* milliseconds */
00841         static struct timeval video_stats_start = {0, 0};
00842         iaxc_event e;
00843         struct timeval now;
00844 
00845         if ( !call )
00846                 return -1;
00847 
00848         if ( video_stats_start.tv_sec == 0 && video_stats_start.tv_usec == 0 )
00849                 gettimeofday(&video_stats_start, 0);
00850 
00851         gettimeofday(&now, 0);
00852 
00853         if ( iaxci_msecdiff(&now, &video_stats_start) > video_stats_interval )
00854         {
00855                 get_stats(call, &e.ev.videostats.stats, 1);
00856                 e.type = IAXC_EVENT_VIDEOSTATS;
00857                 e.ev.videostats.callNo = selected_call;
00858                 iaxci_post_event(e);
00859 
00860                 video_stats_start = now;
00861         }
00862 
00863         return 0;
00864 }
00865 

Generated on Mon Sep 24 15:43:29 2007 for IAXClient by  doxygen 1.5.3