2012-01-15 170 views
3

我想通過NDK呈現視頻,添加一些功能,只是不支持在sdk中。我使用FFmpeg來解碼視頻,並且可以通過ndk進行編譯,並以this作爲起點。我已經修改了這個例子,而不是使用glDrawTexiOES來繪製紋理,我已經設置了一些頂點並且在這個頂點上渲染了紋理(opengl es渲染四邊形的方式)。android ffmpeg opengl es渲染電影

下面是我正在做的渲染,但創建glTexImage2D是緩慢的。我想知道是否有任何方法可以加快速度,或者提高速度,例如試圖在背景中設置一些紋理並渲染預先設置的紋理。或者,如果有其他方法可以更快速地將視頻幀繪製到Android屏幕上?目前我只能獲得12fps左右。

glClear(GL_COLOR_BUFFER_BIT); 
glEnableClientState(GL_VERTEX_ARRAY); 
glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
glBindTexture(GL_TEXTURE_2D, textureConverted); 

//this is slow 
glTexImage2D(GL_TEXTURE_2D, /* target */ 
0, /* level */ 
GL_RGBA, /* internal format */ 
textureWidth, /* width */ 
textureHeight, /* height */ 
0, /* border */ 
GL_RGBA, /* format */ 
GL_UNSIGNED_BYTE,/* type */ 
pFrameConverted->data[0]); 

glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
glTexCoordPointer(2, GL_FLOAT, 0, texCoords); 
glVertexPointer(3, GL_FLOAT, 0, vertices); 
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices); 
glDisableClientState(GL_VERTEX_ARRAY); 
glDisableClientState(GL_TEXTURE_COORD_ARRAY); 

編輯 我改變了我的代碼來初始化一個gltextImage2D只有一次,並與glSubTexImage2D修改它,它並沒有太大的改進,幀率的。

然後我修改了代碼來修改NDK上的本地位圖對象。通過這種方法,我有一個後臺線程運行處理下一幀並在本機側填充位圖對象。我認爲這有潛力,但我需要提高將AVFrame對象從FFmpeg轉換爲本地位圖的速度。下面是目前我正在使用的一種蠻力方法。有什麼方法可以提高速度或優化此轉換嗎?

static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame) 
{ 
uint8_t *frameLine; 

int yy; 
for (yy = 0; yy < info->height; yy++) { 
    uint8_t* line = (uint8_t*)pixels; 
    frameLine = (uint8_t *)pFrame->data[0] + (yy * pFrame->linesize[0]); 

    int xx; 
    for (xx = 0; xx < info->width; xx++) { 
     int out_offset = xx * 4; 
     int in_offset = xx * 3; 

     line[out_offset] = frameLine[in_offset]; 
     line[out_offset+1] = frameLine[in_offset+1]; 
     line[out_offset+2] = frameLine[in_offset+2]; 
     line[out_offset+3] = 0; 
    } 
    pixels = (char*)pixels + info->stride; 
} 
} 
+0

UPDATE ::使用圖像上方的fill_bitmap方法是黑白從FFMPEG的幀輸出轉換圖像時我得到更好的幀率,但是,我通過側得到像側的重複。 – broschb 2012-01-19 05:30:48

回答

6

是的,紋理(和緩衝區,着色器和幀緩衝區)的創建速度很慢。

這就是爲什麼你只應該創建紋理一次。創建後,您可以通過調用glSubTexImage2D來修改其數據。

爲了更快地上傳紋理數據 - 創建兩個紋理。當你使用一個來顯示時,將紋理數據從ffmpeg上傳到第二個。當你顯示第二個時,上傳數據到第一個。並從頭開始重複。

我認爲它仍然不會很快。您可以嘗試使用允許從NDK訪問位圖對象像素的jnigraphics庫。之後 - 您只需在java側的屏幕上顯示此位圖。

+0

感謝您的建議,請參閱我對上述問題的更新。 – broschb 2012-01-17 14:58:02

+0

位圖方法給了我接近我想要的幀速率。現在的問題是,當我使用原始問題中的方法將輸出幀從mmpeg轉換爲本地位圖時,我得到的是黑白圖像。有什麼想法嗎? – broschb 2012-01-19 05:29:38

+0

我不熟悉ffmpeg,但也許你的像素格式是不同的。 ffmpeg是否可以解碼爲RGB?同時檢查是否使用正確的像素格式創建了位圖 - ARGB8。 – 2012-01-19 06:38:12

0

是的,你可以優化這段代碼:

static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame) 
{ 
uint8_t *frameLine; 

int yy; 
for (yy = 0; yy < info->height; yy++) 
{ 
    uint8_t* line = (uint8_t*)pixels; 
    frameLine = (uint8_t *)pFrame->data[0] + (yy * pFrame->linesize[0]); 

    int xx; 
    for (xx = 0; xx < info->width; xx++) { 
     int out_offset = xx * 4; 
     int in_offset = xx * 3; 

     line[out_offset] = frameLine[in_offset]; 
     line[out_offset+1] = frameLine[in_offset+1]; 
     line[out_offset+2] = frameLine[in_offset+2]; 
     line[out_offset+3] = 0; 
    } 
    pixels = (char*)pixels + info->stride; 
} 
} 

就像這樣:

static void fill_bitmap(AndroidBitmapInfo* info, void *pixels, AVFrame *pFrame) 
{ 
uint8_t *frameLine = (uint8_t *)pFrame->data[0]; 

int yy; 
for (yy = 0; yy < info->height; yy++) 
{ 
    uint8_t* line = (uint8_t*)pixels; 

    int xx; 

    int out_offset = 0; 
    int in_offset = 0; 

    for (xx = 0; xx < info->width; xx++) { 
     int out_offset += 4; 
     int in_offset += 3; 

     line[out_offset] = frameLine[in_offset]; 
     line[out_offset+1] = frameLine[in_offset+1]; 
     line[out_offset+2] = frameLine[in_offset+2]; 
     line[out_offset+3] = 0; 
    } 
    pixels = (char*)pixels + info->stride; 

    frameLine += pFrame->linesize[0]; 
} 
} 

這將爲您節省一些週期。

1

一些小的添加將解決您的問題,首先將您的AVFrame轉換爲swscale的RGB,然後直接將其應用於您的紋理,即:

AVPicture *pFrameConverted; 
struct SwsContext img_convert_ctx; 

void init(){ 
    pFrameConverted=(AVPicture *)avcodec_alloc_frame(); 
    avpicture_alloc(pFrameConverted, AV_PIX_FMT_RGB565, videoWidth, videoHeight); 
    img_convert_ctx = sws_getCachedContext(&img_convert_ctx, 
        videoWidth, 
        videoHeight, 
        pCodecCtx->pix_fmt, 
        videoWidth, 
        videoHeight, 
        AV_PIX_FMT_RGB565, 
        SWS_FAST_BILINEAR, 
        NULL, NULL, NULL); 
    ff_get_unscaled_swscale(img_convert_ctx); 
} 

void render(AVFrame* pFrame){ 
    sws_scale(img_convert_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pFrame->height, pFrameConverted->data, pFrameConverted->lineSize); 
    glClear(GL_COLOR_BUFFER_BIT); 
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoWidth, videoHeight, GL_RGB, GL_UNSIGNED_BYTE, pFrameConverted->data[0]); 
    glDrawTexiOES(0, 0, 0, videoWidth, videoHeight); 
}