Changeset 4821


Ignore:
Timestamp:
Apr 22, 2014 5:04:43 AM (8 years ago)
Author:
ming
Message:

Closed #1757: iOS OpenGL renderer

  • Optimize rendering speed and CPU usage by avoiding buffer copy from frame's buffer
  • Add various video dev capabilities
Location:
pjproject/trunk/pjmedia
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/include/pjmedia-videodev/opengl_dev.h

    r4812 r4821  
    2222#include <pjmedia-videodev/videodev_imp.h> 
    2323 
    24 /* OpenGL implementation on each platform needs to implement this and 
    25  * stream operations. 
     24/* OpenGL implementation on each platform needs to implement these functions 
     25 * and stream operations. 
    2626 */ 
     27/* Get capabilities of the implementation */ 
     28int pjmedia_vid_dev_opengl_imp_get_cap(void); 
     29 
     30/* Create OpenGL stream */ 
    2731pj_status_t 
    2832pjmedia_vid_dev_opengl_imp_create_stream(pj_pool_t *pool, 
     
    3236                                         pjmedia_vid_dev_stream **p_vid_strm); 
    3337 
     38/****************************************************************************/ 
    3439/* OpenGL buffers opaque structure. */ 
    3540typedef struct gl_buffers gl_buffers; 
  • pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m

    r4812 r4821  
    6363    pj_timestamp            frame_ts; 
    6464    unsigned                ts_inc; 
     65    pjmedia_rect_size       vid_size; 
    6566     
    6667    gl_buffers                  *gl_buf; 
     
    6970    CVOpenGLESTextureCacheRef    vid_texture; 
    7071    CVImageBufferRef             pb; 
     72    void                        *pb_addr; 
    7173    CVOpenGLESTextureRef         texture; 
    7274}; 
     
    101103}; 
    102104 
     105int pjmedia_vid_dev_opengl_imp_get_cap(void) 
     106{ 
     107    return PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW | 
     108           PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE | 
     109           PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION | 
     110           PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE | 
     111           PJMEDIA_VID_DEV_CAP_ORIENTATION; 
     112} 
     113 
    103114static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id) 
    104115{ 
     
    175186    const pjmedia_video_format_detail *vfd; 
    176187    pj_status_t status = PJ_SUCCESS; 
    177     iosgl_fmt_info *ifi; 
    178      
    179     if (!(ifi = get_iosgl_format_info(param->fmt.id))) 
    180         return PJMEDIA_EVID_BADFORMAT; 
     188    CGRect rect; 
     189    CVReturn err; 
    181190     
    182191    strm = PJ_POOL_ZALLOC_T(pool, struct iosgl_stream); 
     
    189198    strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); 
    190199     
    191     if (param->dir & PJMEDIA_DIR_RENDER) { 
    192         CVReturn err; 
    193         UIWindow *window; 
    194         CGRect rect; 
    195          
    196         if ((param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) && 
    197             param->window.info.ios.window) 
    198         { 
    199             /* Get output window handle provided by the application */ 
    200             window = (UIWindow *)param->window.info.ios.window; 
    201             rect = window.bounds; 
    202         } else { 
    203             rect = CGRectMake(0, 0, strm->param.disp_size.w, 
    204                               strm->param.disp_size.h); 
    205         } 
    206          
    207         strm->gl_view = [[GLView alloc] initWithFrame:rect]; 
    208         if (!strm->gl_view) 
    209             return PJ_ENOMEM; 
    210         strm->gl_view->stream = strm; 
    211  
    212         /* Perform OpenGL buffer initializations in the main thread. */ 
    213         strm->status = PJ_SUCCESS; 
    214         [strm->gl_view performSelectorOnMainThread:@selector(init_buffers) 
    215                        withObject:nil waitUntilDone:YES]; 
    216         if ((status = strm->status) != PJ_SUCCESS) { 
    217             PJ_LOG(3, (THIS_FILE, "Unable to create and init OpenGL buffers")); 
    218             goto on_error; 
    219         } 
    220          
    221         /*  Create a new CVOpenGLESTexture cache */ 
    222         err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, 
    223                                            strm->ogl_context, NULL, 
    224                                            &strm->vid_texture); 
    225         if (err) { 
    226             PJ_LOG(3, (THIS_FILE, "Unable to create OpenGL texture cache %d", 
    227                        err)); 
    228             status = PJMEDIA_EVID_SYSERR; 
    229             goto on_error; 
    230         } 
    231  
    232         PJ_LOG(4, (THIS_FILE, "iOS OpenGL ES renderer successfully created")); 
     200    /* If OUTPUT_RESIZE flag is not used, set display size to default */ 
     201    if (!(param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE)) { 
     202        pj_bzero(&strm->param.disp_size, sizeof(strm->param.disp_size)); 
     203    } 
     204     
     205    /* Set video format */ 
     206    status = iosgl_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_FORMAT, 
     207                                  &param->fmt); 
     208    if (status != PJ_SUCCESS) 
     209        goto on_error; 
     210     
     211    rect = CGRectMake(0, 0, strm->param.disp_size.w, strm->param.disp_size.h); 
     212    strm->gl_view = [[GLView alloc] initWithFrame:rect]; 
     213    if (!strm->gl_view) 
     214        return PJ_ENOMEM; 
     215    strm->gl_view->stream = strm; 
     216     
     217    /* Perform OpenGL buffer initializations in the main thread. */ 
     218    strm->status = PJ_SUCCESS; 
     219    [strm->gl_view performSelectorOnMainThread:@selector(init_buffers) 
     220                                    withObject:nil waitUntilDone:YES]; 
     221    if ((status = strm->status) != PJ_SUCCESS) { 
     222        PJ_LOG(3, (THIS_FILE, "Unable to create and init OpenGL buffers")); 
     223        goto on_error; 
     224    } 
     225     
     226    /*  Create a new CVOpenGLESTexture cache */ 
     227    err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, 
     228                                       strm->ogl_context, NULL, 
     229                                       &strm->vid_texture); 
     230    if (err) { 
     231        PJ_LOG(3, (THIS_FILE, "Unable to create OpenGL texture cache %d", 
     232                   err)); 
     233        status = PJMEDIA_EVID_SYSERR; 
     234        goto on_error; 
    233235    } 
    234236     
    235237    /* Apply the remaining settings */ 
    236     /* 
    237      if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { 
    238      iosgl_stream_set_cap(&strm->base, 
    239      PJMEDIA_VID_DEV_CAP_INPUT_SCALE, 
    240      &param->fmt); 
    241      } 
    242      */ 
     238    if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { 
     239        iosgl_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW, 
     240                             param->window.info.ios.window); 
     241    } 
     242    if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) { 
     243        iosgl_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION, 
     244                             &param->window_pos); 
     245    } 
     246    if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) { 
     247        iosgl_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, 
     248                             &param->window_hide); 
     249    } 
     250    if (param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) { 
     251        iosgl_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION, 
     252                             &param->orient); 
     253    } 
     254     
     255    PJ_LOG(4, (THIS_FILE, "iOS OpenGL ES renderer successfully created")); 
     256                     
    243257    /* Done */ 
    244258    strm->base.op = &stream_op; 
     
    283297    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); 
    284298     
    285     if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { 
    286         return PJMEDIA_EVID_INVCAP; 
    287     } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { 
     299    if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { 
    288300        pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval; 
    289301        wnd->info.ios.window = strm->gl_view; 
     
    305317    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); 
    306318     
    307     if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { 
     319    if (cap==PJMEDIA_VID_DEV_CAP_FORMAT) { 
     320        const pjmedia_video_format_info *vfi; 
     321        pjmedia_video_format_detail *vfd; 
     322        pjmedia_format *fmt = (pjmedia_format *)pval; 
     323        iosgl_fmt_info *ifi; 
     324         
     325        if (!(ifi = get_iosgl_format_info(fmt->id))) 
     326            return PJMEDIA_EVID_BADFORMAT; 
     327         
     328        vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(), 
     329                                            fmt->id); 
     330        if (!vfi) 
     331            return PJMEDIA_EVID_BADFORMAT; 
     332         
     333        pjmedia_format_copy(&strm->param.fmt, fmt); 
     334         
     335        vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE); 
     336        pj_memcpy(&strm->vid_size, &vfd->size, sizeof(vfd->size)); 
     337        if (strm->param.disp_size.w == 0 || strm->param.disp_size.h == 0) 
     338            pj_memcpy(&strm->param.disp_size, &vfd->size, sizeof(vfd->size)); 
     339         
     340        /* Invalidate the buffer */ 
     341        if (strm->pb) { 
     342            CVPixelBufferRelease(strm->pb); 
     343            strm->pb = NULL; 
     344        } 
     345         
    308346        return PJ_SUCCESS; 
     347    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { 
     348        UIView *view = (UIView *)pval; 
     349        strm->param.window.info.ios.window = (void *)pval; 
     350        dispatch_async(dispatch_get_main_queue(), 
     351                       ^{[view addSubview:strm->gl_view];}); 
     352        return PJ_SUCCESS; 
     353    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) { 
     354        pj_memcpy(&strm->param.disp_size, pval, sizeof(strm->param.disp_size)); 
     355        dispatch_async(dispatch_get_main_queue(), ^{ 
     356            strm->gl_view.bounds = CGRectMake(0, 0, strm->param.disp_size.w, 
     357                                              strm->param.disp_size.h); 
     358        }); 
     359        return PJ_SUCCESS; 
     360    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) { 
     361        pj_memcpy(&strm->param.window_pos, pval, sizeof(strm->param.window_pos)); 
     362        dispatch_async(dispatch_get_main_queue(), ^{ 
     363            strm->gl_view.center = CGPointMake(strm->param.window_pos.x + 
     364                                               strm->param.disp_size.w/2.0, 
     365                                               strm->param.window_pos.y + 
     366                                               strm->param.disp_size.h/2.0); 
     367        }); 
     368        return PJ_SUCCESS; 
     369    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) { 
     370        dispatch_async(dispatch_get_main_queue(), ^{ 
     371            strm->gl_view.hidden = (BOOL)(*((pj_bool_t *)pval)); 
     372        }); 
     373        return PJ_SUCCESS; 
     374    } else if (cap == PJMEDIA_VID_DEV_CAP_ORIENTATION) { 
     375        pj_memcpy(&strm->param.orient, pval, sizeof(strm->param.orient)); 
     376        if (strm->param.orient == PJMEDIA_ORIENT_UNKNOWN) 
     377            return PJ_SUCCESS; 
     378        dispatch_async(dispatch_get_main_queue(), ^{ 
     379            strm->gl_view.transform = 
     380                CGAffineTransformMakeRotation(((int)strm->param.orient-1) * 
     381                                              -M_PI_2); 
     382        }); 
     383        return PJ_SUCCESS; 
    309384    } 
    310385     
     
    331406    CVReturn err; 
    332407 
    333     err = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, 
    334                                        stream->param.disp_size.w, 
    335                                        stream->param.disp_size.h, 
    336                                        kCVPixelFormatType_32BGRA, 
    337                                        frame->buf, 
    338                                        stream->param.disp_size.w * 4, 
    339                                        NULL, NULL, NULL, &stream->pb); 
    340     if (err) { 
    341         PJ_LOG(3, (THIS_FILE, "Unable to create pixel buffer %d", err)); 
    342         return PJMEDIA_EVID_SYSERR; 
     408    /* Pixel buffer will only create a wrapper for the frame's buffer, 
     409     * so if the frame buffer changes, we have to recreate pb 
     410     */ 
     411    if (!stream->pb || (frame->buf && stream->pb_addr != frame->buf)) { 
     412        if (stream->pb) { 
     413            CVPixelBufferRelease(stream->pb); 
     414            stream->pb = NULL; 
     415        } 
     416        err = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, 
     417                                           stream->vid_size.w, 
     418                                           stream->vid_size.h, 
     419                                           kCVPixelFormatType_32BGRA, 
     420                                           frame->buf, 
     421                                           stream->vid_size.w * 4, 
     422                                           NULL, NULL, NULL, &stream->pb); 
     423        if (err) { 
     424            PJ_LOG(3, (THIS_FILE, "Unable to create pixel buffer %d", err)); 
     425            return PJMEDIA_EVID_SYSERR; 
     426        } 
     427        stream->pb_addr = frame->buf; 
    343428    } 
    344429 
     
    348433                                                     stream->pb, NULL, 
    349434                                                     GL_TEXTURE_2D, GL_RGBA, 
    350                                                      stream->param.disp_size.w, 
    351                                                      stream->param.disp_size.h, 
     435                                                     stream->vid_size.w, 
     436                                                     stream->vid_size.h, 
    352437                                                     GL_BGRA, 
    353438                                                     GL_UNSIGNED_BYTE, 
     
    355440    if (!stream->texture || err) { 
    356441        PJ_LOG(3, (THIS_FILE, "Unable to create OpenGL texture %d", err)); 
    357         CVPixelBufferRelease(stream->pb); 
    358442        return PJMEDIA_EVID_SYSERR; 
    359443    } 
     
    370454    CVOpenGLESTextureCacheFlush(stream->vid_texture, 0); 
    371455    CFRelease(stream->texture); 
    372     CVPixelBufferRelease(stream->pb); 
    373456     
    374457    return PJ_SUCCESS; 
     
    396479     
    397480    iosgl_stream_stop(strm); 
     481     
     482    if (stream->pb) { 
     483        CVPixelBufferRelease(stream->pb); 
     484        stream->pb = NULL; 
     485    } 
    398486     
    399487    if (stream->vid_texture) { 
  • pjproject/trunk/pjmedia/src/pjmedia-videodev/opengl_dev.c

    r4812 r4821  
    337337    struct opengl_factory *qf = (struct opengl_factory*)f; 
    338338    struct opengl_dev_info *qdi; 
    339     unsigned i, l; 
     339    unsigned l; 
    340340     
    341341    /* Initialize input and output devices here */ 
     
    350350    qdi->info.dir = PJMEDIA_DIR_RENDER; 
    351351    qdi->info.has_callback = PJ_FALSE; 
    352     qdi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; 
    353  
    354     for (i = 0; i < qf->dev_count; i++) { 
    355         qdi = &qf->dev_info[i]; 
    356         qdi->info.fmt_cnt = PJ_ARRAY_SIZE(opengl_fmts); 
    357         qdi->info.caps |= PJMEDIA_VID_DEV_CAP_FORMAT; 
     352    qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; 
     353    qdi->info.fmt_cnt = PJ_ARRAY_SIZE(opengl_fmts); 
     354    qdi->info.caps |= pjmedia_vid_dev_opengl_imp_get_cap(); 
    358355         
    359         for (l = 0; l < PJ_ARRAY_SIZE(opengl_fmts); l++) { 
    360             pjmedia_format *fmt = &qdi->info.fmt[l]; 
    361             pjmedia_format_init_video(fmt, 
    362                                       opengl_fmts[l], 
    363                                       DEFAULT_WIDTH, 
    364                                       DEFAULT_HEIGHT, 
    365                                       DEFAULT_FPS, 1); 
    366         } 
    367     } 
    368      
    369     PJ_LOG(4, (THIS_FILE, "OpenGL initialized with %d devices", 
    370                qf->dev_count)); 
     356    for (l = 0; l < PJ_ARRAY_SIZE(opengl_fmts); l++) { 
     357        pjmedia_format *fmt = &qdi->info.fmt[l]; 
     358        pjmedia_format_init_video(fmt, opengl_fmts[l], DEFAULT_WIDTH, 
     359                                  DEFAULT_HEIGHT, DEFAULT_FPS, 1); 
     360    } 
     361     
     362    PJ_LOG(4, (THIS_FILE, "OpenGL device initialized")); 
    371363     
    372364    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.