Changeset 5118


Ignore:
Timestamp:
Jun 25, 2015 8:17:52 AM (9 years ago)
Author:
ming
Message:

Re #1861: Initial implementation of video orientation support

  • Utility to resize and rotate video frame
  • Support for iOS + sample
  • pjsua API to set video device's orientation
Location:
pjproject/trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/build/Makefile

    r5027 r5118  
    107107export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev 
    108108export PJMEDIA_VIDEODEV_OBJS +=  errno.o videodev.o avi_dev.o ffmpeg_dev.o \ 
    109                                 colorbar_dev.o v4l2_dev.o opengl_dev.o 
     109                                colorbar_dev.o v4l2_dev.o opengl_dev.o \ 
     110                                util.o 
    110111export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS) 
    111112export PJMEDIA_VIDEODEV_CXXFLAGS += $(_CXXFLAGS) 
  • pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_dev.m

    r5054 r5118  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
    1818 */ 
     19#include "util.h" 
    1920#include <pjmedia-videodev/videodev_imp.h> 
    2021#include <pj/assert.h> 
     
    3738#define DEFAULT_HEIGHT          288 
    3839#define DEFAULT_FPS             15 
     40 
     41/* Define whether we should maintain the aspect ratio when rotating the image. 
     42 * For more details, please refer to vid_util.h. 
     43 */ 
     44#define MAINTAIN_ASPECT_RATIO PJ_TRUE 
    3945 
    4046typedef struct ios_fmt_info 
     
    109115    NSLock                 *frame_lock; 
    110116    void                   *capture_buf; 
     117    void                   *frame_buf; 
     118     
     119    pjmedia_vid_dev_conv    conv; 
     120    pjmedia_rect_size       vid_size; 
    111121     
    112122    AVCaptureSession            *cap_session; 
     
    314324                                                       ios_sizes[m].preset_str]) 
    315325                    { 
     326                        /* Landscape video */ 
    316327                        fmt = &qdi->info.fmt[qdi->info.fmt_cnt++]; 
    317328                        pjmedia_format_init_video(fmt, 
     
    319330                                                  ios_sizes[m].supported_size_w, 
    320331                                                  ios_sizes[m].supported_size_h, 
     332                                                  DEFAULT_FPS, 1); 
     333                        /* Portrait video */ 
     334                        fmt = &qdi->info.fmt[qdi->info.fmt_cnt++]; 
     335                        pjmedia_format_init_video(fmt, 
     336                                                  ios_fmts[l].pjmedia_format, 
     337                                                  ios_sizes[m].supported_size_h, 
     338                                                  ios_sizes[m].supported_size_w, 
    321339                                                  DEFAULT_FPS, 1); 
    322340                    } 
     
    436454{ 
    437455    CVImageBufferRef img; 
     456    pj_status_t status; 
     457    void *frame_buf; 
    438458 
    439459    /* Refrain from calling pjlib functions which require thread registration 
     
    452472    /* Lock the base address of the pixel buffer */ 
    453473    CVPixelBufferLockBaseAddress(img, kCVPixelBufferLock_ReadOnly); 
    454      
    455474 
    456475    [stream->frame_lock lock]; 
     
    464483             */ 
    465484            pj_size_t stride = CVPixelBufferGetBytesPerRowOfPlane(img, 0); 
    466             pj_bool_t need_clip = (stride != stream->size.w); 
     485            pj_size_t height = CVPixelBufferGetHeight(img); 
     486            pj_bool_t need_clip; 
     487             
     488            /* Auto detect rotation */ 
     489            if (height != stream->vid_size.h) { 
     490                stream->vid_size.w = stream->vid_size.h; 
     491                stream->vid_size.h = height; 
     492            } 
     493             
     494            need_clip = (stride != stream->vid_size.w); 
    467495             
    468496            p = (pj_uint8_t*)CVPixelBufferGetBaseAddressOfPlane(img, 0); 
    469             p_len = stream->size.w * stream->size.h; 
     497            p_len = stream->vid_size.w * stream->vid_size.h; 
    470498            Y = (pj_uint8_t*)stream->capture_buf; 
    471499            U = Y + p_len; 
     
    476504            } else { 
    477505                int i = 0; 
    478                 for (;i<stream->size.h;++i) { 
    479                     pj_memcpy(Y+(i*stream->size.w), p+(i*stride), 
    480                               stream->size.w); 
     506                for (; i < stream->vid_size.h; ++i) { 
     507                    pj_memcpy(Y, p, stream->vid_size.w); 
     508                    Y += stream->vid_size.w; 
     509                    p += stride; 
    481510                } 
    482511            } 
     
    493522            } else { 
    494523                int i = 0; 
    495                 for (;i<(stream->size.h)/2;++i) { 
     524                for (;i<(stream->vid_size.h)/2;++i) { 
    496525                    int y=0; 
    497                     for (;y<(stream->size.w)/2;++y) { 
     526                    for (;y<(stream->vid_size.w)/2;++y) { 
    498527                        *U++ = *p++; 
    499528                        *V++ = *p++; 
    500529                    } 
    501                     p += (stride - stream->size.w); 
     530                    p += (stride - stream->vid_size.w); 
    502531                } 
    503532            } 
     
    506535        pj_memcpy(stream->capture_buf, CVPixelBufferGetBaseAddress(img), 
    507536                  stream->frame_size); 
     537    } 
     538     
     539    status = pjmedia_vid_dev_conv_resize_and_rotate(&stream->conv,  
     540                                                    stream->capture_buf, 
     541                                                    &frame_buf); 
     542    if (status == PJ_SUCCESS) { 
     543        stream->frame_buf = frame_buf; 
    508544    } 
    509545 
     
    528564     
    529565    [stream->frame_lock lock]; 
    530     pj_memcpy(frame->buf, stream->capture_buf, stream->frame_size); 
     566    pj_memcpy(frame->buf, stream->frame_buf, stream->frame_size); 
    531567    [stream->frame_lock unlock]; 
    532568     
     
    646682  
    647683        for (i = PJ_ARRAY_SIZE(ios_sizes)-1; i > 0; --i) { 
    648             if ((vfd->size.w == ios_sizes[i].supported_size_w) && 
    649                 (vfd->size.h == ios_sizes[i].supported_size_h)) 
     684            if (((vfd->size.w == ios_sizes[i].supported_size_w) && 
     685                 (vfd->size.h == ios_sizes[i].supported_size_h)) || 
     686                ((vfd->size.w == ios_sizes[i].supported_size_h) && 
     687                 (vfd->size.h == ios_sizes[i].supported_size_w))) 
    650688            { 
    651689                break; 
     
    655693        strm->cap_session.sessionPreset = ios_sizes[i].preset_str; 
    656694         
    657         vfd->size.w = ios_sizes[i].supported_size_w; 
    658         vfd->size.h = ios_sizes[i].supported_size_h; 
     695        /* If the requested size is portrait (or landscape), we make 
     696         * our natural orientation portrait (or landscape) as well. 
     697         */ 
     698        if (vfd->size.w > vfd->size.h) { 
     699            vfd->size.w = ios_sizes[i].supported_size_w; 
     700            vfd->size.h = ios_sizes[i].supported_size_h; 
     701        } else { 
     702            vfd->size.h = ios_sizes[i].supported_size_w; 
     703            vfd->size.w = ios_sizes[i].supported_size_h; 
     704        } 
    659705        strm->size = vfd->size; 
     706        strm->vid_size = vfd->size; 
    660707        strm->bytes_per_row = strm->size.w * vfi->bpp / 8; 
    661708        strm->frame_size = strm->bytes_per_row * strm->size.h; 
     
    724771         
    725772        strm->capture_buf = pj_pool_alloc(strm->pool, strm->frame_size); 
     773        strm->frame_buf = strm->capture_buf; 
    726774        strm->frame_lock = [[NSLock alloc]init]; 
    727775         
     
    730778            ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW, 
    731779                               &param->native_preview); 
     780        } 
     781 
     782        /* Video orientation. 
     783         * If we send in portrait, we need to set up orientation converter 
     784         * as well. 
     785         */ 
     786        if ((param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) || 
     787            (vfd->size.h > vfd->size.w)) 
     788        { 
     789            if (param->orient == PJMEDIA_ORIENT_UNKNOWN) 
     790                param->orient = PJMEDIA_ORIENT_NATURAL; 
     791            ios_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION, 
     792                               &param->orient); 
    732793        } 
    733794         
     
    9881049        } 
    9891050             
    990         /* TODO: orientation for capture device */ 
    9911051        case PJMEDIA_VID_DEV_CAP_ORIENTATION: 
    9921052        { 
     1053            pjmedia_orient orient = *(pjmedia_orient *)pval; 
     1054 
     1055            pj_assert(orient >= PJMEDIA_ORIENT_UNKNOWN && 
     1056                      orient <= PJMEDIA_ORIENT_ROTATE_270DEG); 
     1057 
     1058            if (orient == PJMEDIA_ORIENT_UNKNOWN) 
     1059                return PJ_EINVAL; 
     1060 
    9931061            pj_memcpy(&strm->param.orient, pval, 
    9941062                      sizeof(strm->param.orient)); 
    995             if (strm->param.orient == PJMEDIA_ORIENT_UNKNOWN) 
    996                 return PJ_SUCCESS; 
    997              
    998             dispatch_async(dispatch_get_main_queue(), ^{ 
    999                 strm->render_view.transform = 
    1000                     CGAffineTransformMakeRotation( 
    1001                         ((int)strm->param.orient-1) * -M_PI_2); 
    1002             }); 
     1063         
     1064            if (strm->param.dir == PJMEDIA_DIR_RENDER) { 
     1065                dispatch_async(dispatch_get_main_queue(), ^{ 
     1066                    strm->render_view.transform = 
     1067                        CGAffineTransformMakeRotation( 
     1068                            ((int)strm->param.orient-1) * -M_PI_2); 
     1069                }); 
     1070 
     1071                return PJ_SUCCESS; 
     1072            } 
     1073         
     1074            const AVCaptureVideoOrientation cap_ori[4] = 
     1075            { 
     1076                AVCaptureVideoOrientationLandscapeLeft,      /* NATURAL */ 
     1077                AVCaptureVideoOrientationPortrait,           /* 90DEG   */ 
     1078                AVCaptureVideoOrientationLandscapeRight,     /* 180DEG  */ 
     1079                AVCaptureVideoOrientationPortraitUpsideDown, /* 270DEG  */ 
     1080            }; 
     1081            AVCaptureConnection *vidcon; 
     1082            pj_bool_t support_ori = PJ_TRUE; 
     1083             
     1084            pj_assert(strm->param.dir == PJMEDIA_DIR_CAPTURE); 
     1085             
     1086            if (!strm->video_output) 
     1087                return PJMEDIA_EVID_NOTREADY; 
     1088 
     1089            vidcon = [strm->video_output  
     1090                      connectionWithMediaType:AVMediaTypeVideo]; 
     1091            if ([vidcon isVideoOrientationSupported]) { 
     1092                vidcon.videoOrientation = cap_ori[strm->param.orient-1]; 
     1093            } else { 
     1094                support_ori = PJ_FALSE; 
     1095            } 
     1096             
     1097            if (!strm->conv.conv) { 
     1098                pj_status_t status; 
     1099                pjmedia_rect_size orig_size; 
     1100 
     1101                /* Original native size of device is landscape */ 
     1102                orig_size.w = (strm->size.w > strm->size.h? strm->size.w : 
     1103                               strm->size.h); 
     1104                orig_size.h = (strm->size.w > strm->size.h? strm->size.h : 
     1105                               strm->size.w); 
     1106 
     1107                if (!support_ori) { 
     1108                    PJ_LOG(4, (THIS_FILE, "Native video capture orientation "  
     1109                                          "unsupported, will use converter's " 
     1110                                          "rotation.")); 
     1111                } 
     1112 
     1113                status = pjmedia_vid_dev_conv_create_converter( 
     1114                                                 &strm->conv, strm->pool, 
     1115                                                 &strm->param.fmt, 
     1116                                                 orig_size, strm->size, 
     1117                                                 (support_ori?PJ_FALSE:PJ_TRUE), 
     1118                                                 MAINTAIN_ASPECT_RATIO); 
     1119                 
     1120                if (status != PJ_SUCCESS) 
     1121                    return status; 
     1122            } 
     1123             
     1124            pjmedia_vid_dev_conv_set_rotation(&strm->conv, strm->param.orient); 
     1125             
     1126            PJ_LOG(5, (THIS_FILE, "Video capture orientation set to %d", 
     1127                                  strm->param.orient)); 
     1128 
    10031129            return PJ_SUCCESS; 
    10041130        } 
     
    11411267        stream->frame_lock = nil; 
    11421268    } 
     1269     
     1270    pjmedia_vid_dev_conv_destroy_converter(&stream->conv); 
    11431271 
    11441272    pj_pool_release(stream->pool); 
  • pjproject/trunk/pjsip-apps/src/pjsua/ios/ipjsua/ipjsuaAppDelegate.m

    r5041 r5118  
    4040static char           **restartArgv; 
    4141static int              restartArgc; 
    42 static pj_thread_desc   a_thread_desc; 
    43 static pj_thread_t     *a_thread; 
    4442 
    4543static void displayMsg(const char *msg) 
     
    118116        } 
    119117     
     118        /* Setup device orientation change notification */ 
     119        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 
     120        [[NSNotificationCenter defaultCenter] addObserver:app 
     121            selector:@selector(orientationChanged:) 
     122            name:UIDeviceOrientationDidChangeNotification 
     123            object:[UIDevice currentDevice]]; 
     124         
    120125        status = pjsua_app_run(PJ_TRUE); 
    121126        if (status != PJ_SUCCESS) { 
     
    124129            displayMsg(errmsg); 
    125130        } 
     131         
     132        [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; 
    126133     
    127134        pjsua_app_destroy(); 
     
    158165} 
    159166 
     167- (void)orientationChanged:(NSNotification *)note 
     168{ 
     169#if PJSUA_HAS_VIDEO 
     170    const pjmedia_orient pj_ori[4] = 
     171    { 
     172        PJMEDIA_ORIENT_ROTATE_90DEG,  /* UIDeviceOrientationPortrait */ 
     173        PJMEDIA_ORIENT_ROTATE_270DEG, /* UIDeviceOrientationPortraitUpsideDown */ 
     174        PJMEDIA_ORIENT_ROTATE_180DEG, /* UIDeviceOrientationLandscapeLeft, 
     175                                         home button on the right side */ 
     176        PJMEDIA_ORIENT_NATURAL        /* UIDeviceOrientationLandscapeRight, 
     177                                         home button on the left side */ 
     178    }; 
     179    static pj_thread_desc a_thread_desc; 
     180    static pj_thread_t *a_thread; 
     181    static UIDeviceOrientation prev_ori = 0; 
     182    UIDeviceOrientation dev_ori = [[UIDevice currentDevice] orientation]; 
     183     
     184    if (dev_ori == prev_ori && !note) return; 
     185     
     186    NSLog(@"Device orientation changed: %d", (prev_ori = dev_ori)); 
     187     
     188    if (dev_ori >= UIDeviceOrientationPortrait && 
     189        dev_ori <= UIDeviceOrientationLandscapeRight) 
     190    { 
     191        if (!pj_thread_is_registered()) { 
     192            pj_thread_register("ipjsua", a_thread_desc, &a_thread); 
     193        } 
     194         
     195        pjsua_vid_dev_set_orient(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, 
     196                                 pj_ori[dev_ori-1]); 
     197    } 
     198#endif 
     199} 
     200 
    160201- (void)keepAlive { 
     202    static pj_thread_desc a_thread_desc; 
     203    static pj_thread_t *a_thread; 
    161204    int i; 
    162205     
    163     if (!pj_thread_is_registered()) 
    164     { 
     206    if (!pj_thread_is_registered()) { 
    165207        pj_thread_register("ipjsua", a_thread_desc, &a_thread); 
    166208    } 
     
    230272    i = (wid == PJSUA_INVALID_ID) ? 0 : wid; 
    231273    last = (wid == PJSUA_INVALID_ID) ? PJSUA_MAX_VID_WINS : wid+1; 
     274 
     275    [app orientationChanged:NULL]; 
    232276     
    233277    for (;i < last; ++i) { 
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h

    r4999 r5118  
    66016601 
    66026602/** 
     6603 * Check whether the video capture device is currently active, i.e. if 
     6604 * a video preview has been started or there is a video call using 
     6605 * the device. This function will return PJ_FALSE for video renderer device. 
     6606 * 
     6607 * @param id            The video device index. 
     6608 * 
     6609 * @return              PJ_TRUE if active, PJ_FALSE otherwise. 
     6610 */ 
     6611PJ_DECL(pj_bool_t) pjsua_vid_dev_is_active(pjmedia_vid_dev_index id); 
     6612 
     6613/** 
     6614 * Set the orientation of the video device. The function only works 
     6615 * for video capture device and if the device is currently active (i.e. 
     6616 * a video preview has been started or there is a video call using the device). 
     6617 * Application can check if a video device is active by calling 
     6618 * #pjsua_vid_dev_is_active(). 
     6619 * 
     6620 * @param id            The video device index. 
     6621 * @param orient        Video device orientation. 
     6622 * 
     6623 * @return              PJ_SUCCESS on success, or the appropriate error code. 
     6624 */ 
     6625PJ_DECL(pj_status_t) pjsua_vid_dev_set_orient(pjmedia_vid_dev_index id, 
     6626                                              pjmedia_orient orient); 
     6627 
     6628/** 
    66036629 * Enum all video devices installed in the system. 
    66046630 * 
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c

    r5098 r5118  
    3333 
    3434 
     35static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id, 
     36                                            pj_bool_t running_only); 
    3537static void free_vid_win(pjsua_vid_win_id wid); 
    3638 
     
    212214{ 
    213215    return pjmedia_vid_dev_get_info(id, vdi); 
     216} 
     217 
     218/* 
     219 * Check whether the video device is currently active. 
     220 */ 
     221PJ_DEF(pj_bool_t) pjsua_vid_dev_is_active(pjmedia_vid_dev_index id) 
     222{ 
     223    pjsua_vid_win_id wid = vid_preview_get_win(id, PJ_FALSE); 
     224     
     225    return (wid != PJSUA_INVALID_ID? PJ_TRUE: PJ_FALSE); 
     226} 
     227 
     228/* 
     229 * Set the orientation of the video device. 
     230 */ 
     231PJ_DEF(pj_status_t) pjsua_vid_dev_set_orient( pjmedia_vid_dev_index id, 
     232                                              pjmedia_orient orient) 
     233{ 
     234    pjsua_vid_win *w; 
     235    pjmedia_vid_dev_stream *cap_dev; 
     236    pjsua_vid_win_id wid = vid_preview_get_win(id, PJ_FALSE); 
     237     
     238    if (wid == PJSUA_INVALID_ID) { 
     239        PJ_LOG(3, (THIS_FILE, "Unable to set orientation for video dev %d: " 
     240                              "device not active", id)); 
     241        return PJ_ENOTFOUND; 
     242    } 
     243 
     244    w = &pjsua_var.win[wid]; 
     245         
     246    cap_dev = pjmedia_vid_port_get_stream(w->vp_cap); 
     247 
     248    return pjmedia_vid_dev_stream_set_cap(cap_dev, 
     249                                          PJMEDIA_VID_DEV_CAP_ORIENTATION, 
     250                                          &orient); 
    214251} 
    215252 
Note: See TracChangeset for help on using the changeset viewer.