2016-12-17 10 views
-1

픽셀을 행 하나씩 순서대로 한 채널 씩 저장하는 배열로 AVFrame을 복사하려고합니다.FFMPEG - AVFrame 대 채널당 배열 변환

상세 사항 :

나는 비디오에서 프레임을 읽어 FFMPEG의 API를 사용하고 있습니다. I는 frame_arr 화소의 범위에 vid_frame 모두 복사해야

unsigned char* frame_arr = new unsigned char [cdc_ctx->width * cdc_ctx->height * 3]; 

:

AVFormatContext* fmt_ctx = NULL; 
avformat_open_input(&fmt_ctx, filepath, NULL, NULL); 
... 
int video_stream_idx; // stores the stream index for the video 
... 
AVFrame* vid_frame = NULL; 
vid_frame = av_frame_alloc(); 
AVPacket vid_pckt; 
int frame_finish; 
... 
while (av_read_frame(fmt_ctx, &vid_pckt) >= 0) { 
    if (b_vid_pckt.stream_index == video_stream_idx) { 
     avcodec_decode_video2(cdc_ctx, vid_frame, &frame_finish, &vid_pckt); 
     if (frame_finish) { 
      /* perform conversion */ 
     } 
    } 
} 

대상 배열이 다음과 같다 : 다음과 같이 I는 AVFrame 각 프레임을 가져 avcodec_decode_video2을 사용한 값은 [0, 255]이어야합니다. 문제는 배열이 한 번에 한 채널, 즉 R11, R12, ... R21, R22, ... G11, G12, ... G21, G22, .. 같은 행 주요 순서로 프레임을 저장해야한다는 것입니다. B11, B12, ... B21, B22, ... (표색 [색상 색인] [열 색인] [열 색인]을 사용했습니다. 즉, G21은 2 행 1 열의 픽셀의 녹색 채널 값입니다) . 나는 sws_scale을 보았지만 그 기능이 그러한 변환을 할 수 있는지 여부를 이해할만큼 충분히 이해하지 못합니다. 누군가 도와 줄 수 있어요 !! :)

답변

1

"한 번에 하나의 채널"이라고하는 형식은 planar이라는 용어가 있습니다. (btw, 반대 형식의 이름은 packed입니다.) 그리고 거의 모든 픽셀 형식은 행 순서입니다.

입력 형식이 다를 수 있으며 모든 형식을 하나의 형식으로 변환해야합니다. 그것이 sws_scale()입니다.

그러나 ffmpeg 라이브러리에는 아직 planar RGB 형식이 없습니다. ffmpeg 소스 코드 libavutil/pixdesc.c에 자신의 픽셀 형식 설명을 작성하고 libs를 다시 빌드해야합니다.

또는 프레임을 AV_PIX_FMT_GBRP 형식으로 변환 할 수 있습니다.이 형식은 원하는 것과 가장 유사한 형식입니다. AV_PIX_FMT_GBRP은 평면 형식이며 녹색 채널은 처음에는 빨간색이고 마침내 빨간색 (파란색 가운데)입니다. 그런 다음이 채널을 재정렬하십시오.

// Create a SwsContext first: 
SwsContext* sws_ctx = sws_getContext(cdc_ctx->width, cdc_ctx->height, cdc_ctx->pix_fmt, cdc_ctx->width, cdc_ctx->height, AV_PIX_FMT_GBRP, 0, 0, 0, 0); 
// alloc some new space for storing converted frame 
AVFrame* gbr_frame = av_frame_alloc(); 
picture->format = AV_PIX_FMT_GBRP; 
picture->width = cdc_ctx->width; 
picture->height = cdc_ctx->height; 
av_frame_get_buffer(picture, 32); 
.... 

while (av_read_frame(fmt_ctx, &vid_pckt) >=0) { 
    ret = avcodec_send_packet(cdc_ctx, &vid_pckt); 
    // In particular, we don't expect AVERROR(EAGAIN), because we read all 
    // decoded frames with avcodec_receive_frame() until done. 
    if (ret < 0) 
     break; 

    ret = avcodec_receive_frame(cdc_ctx, vid_frame); 
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) 
     break; 
    if (ret >= 0) { 
     // convert image from native format to planar GBR 
     sws_scale(sws_ctx, vid_frame->data, 
        vid_frame->linesize, 0, vid_frame->height, 
        gbr_frame->data, gbr_frame->linesize); 

     // rearrange gbr channels in gbr_frame as you like 
     // g channel is gbr_frame->data[0] 
     // b channel is gbr_frame->data[1] 
     // r channel is gbr_frame->data[2] 
     // ...... 
    } 
} 

av_frame_free(gbr_frame); 
av_frame_free(vid_frame); 
sws_freeContext(sws_ctx); 
avformat_free_context(fmt_ctx) 
+0

감사합니다. @halfelf. 몇 가지 초기 테스트를했는데 sws_scale을 GBR로 변환하는 것이 픽셀 단위로 복사하는 것과 비교하면 상당히 느린 것 같습니다. 나는 다시보고 할 것이다! – ahmadh