2017-06-30 18 views
5

Tôi mới vào libav và tôi đang viết một phần mềm thao tác video sử dụng opencv làm trái tim của nó. Những gì tôi đã làm là một thời gian ngắn như sau:Libav AVFrame để Opencv Mat để chuyển đổi AVPacket

1- đọc các gói tin Video

2- decode các gói tin vào AVFrame

3 convert các AVFrame để CV Mat

4 thao tác với Mat

5- chuyển đổi CV Mat thành AVFrame

6- encode AVFrame vào AVPacket

7- viết gói

8- goto 1

tôi đọc dranger hướng dẫn trong http://dranger.com/ffmpeg/tutorial01.html và tôi cũng sử dụng decoding_encoding ví dụ. Tôi có thể đọc video, trích xuất các khung hình video và chuyển đổi chúng thành CV Mat. Vấn đề của tôi bắt đầu từ chuyển đổi từ cv Mat sang AVFrame và mã hóa nó thành AVPacket.

Bạn vui lòng giúp tôi với điều này?

Đây là mã của tôi:

int main(int argc, char **argv) 
{ 
AVOutputFormat *ofmt = NULL; 
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; 
AVPacket pkt; 
AVCodecContext *pCodecCtx = NULL; 
AVCodec   *pCodec = NULL; 
AVFrame   *pFrame = NULL; 
AVFrame   *pFrameRGB = NULL; 
int videoStream=-1; 
int audioStream=-1; 
int    frameFinished; 
int    numBytes; 
uint8_t   *buffer = NULL; 
struct SwsContext *sws_ctx = NULL; 
FrameManipulation *mal_frame; 

const char *in_filename, *out_filename; 
int ret, i; 
if (argc < 3) { 

    printf("usage: %s input output\n" 
      "API example program to remux a media file with libavformat and libavcodec.\n" 
      "The output format is guessed according to the file extension.\n" 
      "\n", argv[0]); 
    return 1; 
} 
in_filename = arg[1]; 
out_filename = arg[2]; 
av_register_all(); 
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) { 
    fprintf(stderr, "Could not open input file '%s'", in_filename); 
    goto end; 
} 

if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { 
    fprintf(stderr, "Failed to retrieve input stream information"); 
    goto end; 
} 

av_dump_format(ifmt_ctx, 0, in_filename, 0); 
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); 

if (!ofmt_ctx) { 
    fprintf(stderr, "Could not create output context\n"); 
    ret = AVERROR_UNKNOWN; 
    goto end; 
} 

ofmt = ofmt_ctx->oformat; 

for (i = 0; i < ifmt_ctx->nb_streams; i++) { 
    AVStream *in_stream = ifmt_ctx->streams[i]; 
    AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec); 

    if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && 
     videoStream < 0) { 
      videoStream=i; 
    } 

    if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && 
     audioStream < 0) { 
      audioStream=i; 
    } 

    if (!out_stream) { 
     fprintf(stderr, "Failed allocating output stream\n"); 
     ret = AVERROR_UNKNOWN; 
     goto end; 
    } 

    ret = avcodec_copy_context(out_stream->codec, in_stream->codec); 

    if (ret < 0) { 
     fprintf(stderr, "Failed to copy context from input to output stream codec context\n"); 
     goto end; 
    } 

    out_stream->codec->codec_tag = 0; 

    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) 
     out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 
} 

pCodec=avcodec_find_decoder(ifmt_ctx->streams[videoStream]->codec->codec_id); 
pCodecCtx = avcodec_alloc_context3(pCodec); 

if(avcodec_copy_context(pCodecCtx, ifmt_ctx->streams[videoStream]->codec) != 0) { 
    fprintf(stderr, "Couldn't copy codec context"); 
    return -1; // Error copying codec context 
} 

// Open codec 
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) 
    return -1; // Could not open codec 

// Allocate video frame 
pFrame=av_frame_alloc(); 

// Allocate an AVFrame structure 
pFrameRGB=av_frame_alloc(); 

// Determine required buffer size and allocate buffer 
numBytes=avpicture_get_size(AV_PIX_FMT_RGB24, ifmt_ctx->streams[videoStream]->codec->width, 
       ifmt_ctx->streams[videoStream]->codec->height); 

buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); 

// Assign appropriate parts of buffer to image planes in pFrameRGB 
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset 
// of AVPicture 
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, 
     ifmt_ctx->streams[videoStream]->codec->width, ifmt_ctx->streams[videoStream]->codec->height); 

av_dump_format(ofmt_ctx, 0, out_filename, 1); 

if (!(ofmt->flags & AVFMT_NOFILE)) { 
    ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); 
    if (ret < 0) { 
     fprintf(stderr, "Could not open output file '%s'", out_filename); 
     goto end; 
    } 
} 

ret = avformat_write_header(ofmt_ctx, NULL); 
if (ret < 0) { 
    fprintf(stderr, "Error occurred when opening output file\n"); 
    goto end; 
} 

// Assign appropriate parts of buffer to image planes in pFrameRGB 
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset 
// of AVPicture 

avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, 
        ifmt_ctx->streams[videoStream]->codec->width, 
        ifmt_ctx->streams[videoStream]->codec->height); 

// initialize SWS context for software scaling 
sws_ctx = sws_getContext(
      ifmt_ctx->streams[videoStream]->codec->width, 
      ifmt_ctx->streams[videoStream]->codec->height, 
      ifmt_ctx->streams[videoStream]->codec->pix_fmt, 
      ifmt_ctx->streams[videoStream]->codec->width, 
      ifmt_ctx->streams[videoStream]->codec->height, 
      AV_PIX_FMT_BGR24, 
      SWS_BICUBIC, 
      NULL, 
      NULL, 
      NULL 
      ); 
// Loop through packets 
while (1) { 

    AVStream *in_stream, *out_stream; 
    ret = av_read_frame(ifmt_ctx, &pkt); 
    if(pkt.stream_index==videoStream) 

    // Decode video frame 
     avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &pkt); 

     if(frameFinished) { 
       sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, 
       pFrame->linesize, 0, pCodecCtx->height, 
       pFrameRGB->data, pFrameRGB->linesize); 
       cv::Mat img= mal_frame->process(
          pFrameRGB,pFrame->width,pFrame->height); 
/* My problem is Here ------------*/ 


    avpicture_fill((AVPicture*)pFrameRGB, 
        img.data, 
        PIX_FMT_BGR24, 
        outStream->codec->width, 
        outStream->codec->height); 

    pFrameRGB->width = ifmt_ctx->streams[videoStream]->codec->width; 
    pFrameRGB->height = ifmt_ctx->streams[videoStream]->codec->height; 

      avcodec_encode_video2(ifmt_ctx->streams[videoStream]->codec , 
                &pkt , pFrameRGB , &gotPacket); 
/* 
I get this error 
[swscaler @ 0x14b58a0] bad src image pointers 
[swscaler @ 0x14b58a0] bad src image pointers 
*/ 

/* My Problem Ends here ---------- */ 

    } 

    if (ret < 0) 

     break; 

    in_stream = ifmt_ctx->streams[pkt.stream_index]; 

    out_stream = ofmt_ctx->streams[pkt.stream_index]; 



    //log_packet(ifmt_ctx, &pkt, "in"); 

    /* copy packet */ 

    pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, 

           AV_ROUND_NEAR_INF); 



    pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); 

    pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base); 

    pkt.pos = -1; 

    log_packet(ofmt_ctx, &pkt, "out"); 

    ret = av_interleaved_write_frame(ofmt_ctx, &pkt); 

    if (ret < 0) { 

     fprintf(stderr, "Error muxing packet\n"); 

     break; 

    } 

    av_free_packet(&pkt); 

} 

av_write_trailer(ofmt_ctx); 

end: 

avformat_close_input(&ifmt_ctx); 

/* close output */ 

if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) 

    avio_closep(&ofmt_ctx->pb); 

avformat_free_context(ofmt_ctx); 

if (ret < 0 && ret != AVERROR_EOF) { 

    return 1; 

} 

return 0; 

} 

Khi tôi chạy mã này, tôi nhận lỗi nghiêm trọng chưa được biết trong phần này:

/* My problem is Here ------------*/ 


    avpicture_fill((AVPicture*)pFrameRGB, 
        img.data, 
        PIX_FMT_BGR24, 
        outStream->codec->width, 
        outStream->codec->height); 

    pFrameRGB->width = ifmt_ctx->streams[videoStream]->codec->width; 
    pFrameRGB->height = ifmt_ctx->streams[videoStream]->codec->height; 

      avcodec_encode_video2(ifmt_ctx->streams[videoStream]->codec , 
                &pkt , pFrameRGB , &gotPacket); 
/* 
I get this error 
[swscaler @ 0x14b58a0] bad src image pointers 
[swscaler @ 0x14b58a0] bad src image pointers 
*/ 

/* My Problem Ends here ---------- */ 

Đây là nơi tôi muốn chuyển đổi trở lại cv Mat để AVFrame và mã hóa nó thành AVPacket. Tôi đánh giá cao sự giúp đỡ của bạn.

+0

Tại sao các bạn làm việc đó bằng tay khi OpenCV hỗ trợ libav như backend để giải mã/mã hóa? – Aram

+1

@Aram opencv VideoWriter không bao gồm luồng âm thanh trong video được mã hóa. Tôi không muốn thêm một giai đoạn phụ để xử lý video của mình. –

+0

Bạn đang sử dụng phiên bản ffmpeg/libav nào? Và phiên bản opencv? Cấu trúc 'FrameManipulation' là gì? Bạn phải đưa ra một ví dụ có thể kiểm chứng để chúng tôi chạy. Và tôi nhận thấy rằng bạn đang sử dụng ví dụ 'remuxing', không phải 'decoding_encoding', cũng rất cũ, rất nhiều API không dùng nữa. – halfelf

Trả lời

3

Sau khi đọc một số ví dụ, đọc mã nguồn và một số giúp mọi người được cung cấp, tôi đã quản lý để chạy mã. Tôi đã sử dụng các ví dụ mã hóa và chuyển mã và trộn chúng lên. Here is my code

dưới đây là các điểm nổi bật: 1- libswscale nên được sử dụng để chuyển đổi AVFrame với định dạng gói yêu cầu để được cấp vào openCV Mat. Để làm như vậy, chúng tôi xác định

struct SwsContext *sws_ctx = NULL; 
sws_ctx = sws_getContext(pCodecCtx->width, 
      pCodecCtx->height, 
      pCodecCtx->pix_fmt, 
      pCodecCtx->width, 
      pCodecCtx->height, 
      AV_PIX_FMT_BGR24, 
      SWS_BICUBIC, 
      NULL, 
      NULL, 
      NULL 
      ); 

Để chuyển đổi lại opencv Mat thành AVFrame, một lần nữa, hãy sử dụng định dạng khung tròn OPPHv BGR sang YUV. Vì vậy, tôi làm điều này:

struct SwsContext *sws_ctx_bgr_yuv = NULL; 
sws_ctx_bgr_yuv = sws_getContext(pCodecCtx->width, 
           pCodecCtx->height, 
           AV_PIX_FMT_BGR24, 
           pCodecCtx->width, 
           pCodecCtx->height, 
           pCodecCtx->pix_fmt //AV_PIX_FMT_YUV420p 
           ,0,0,NULL,NULL); 

Và, đây là khung đọc/giải mã/mã hóa vòng lặp:

while (1) { 
    if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0) 
     break; 
    stream_index = packet.stream_index; 
    type = ifmt_ctx->streams[packet.stream_index]->codec->codec_type; 
    av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n", 
      stream_index); 
    if (filter_ctx[stream_index].filter_graph) { 
     av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); 
     frame = av_frame_alloc(); 
     if (!frame) { 
      ret = AVERROR(ENOMEM); 
      break; 
     } 
     av_packet_rescale_ts(&packet, 
          ifmt_ctx->streams[stream_index]->time_base, 
          ifmt_ctx->streams[stream_index]->codec->time_base); 
     dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : 
      avcodec_decode_audio4; 
     ret = dec_func(ifmt_ctx->streams[stream_index]->codec, frame, 
       &got_frame, &packet); 
     if (ret < 0) { 
      av_frame_free(&frame); 
      av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); 
      break; 
     } 
     if (got_frame) { 
      if(stream_index==video_index){ 

       sws_scale(sws_ctx, (uint8_t const * const *)frame->data, 
         frame->linesize, 0, pCodecCtx->height, 
         pFrameRGB->data, pFrameRGB->linesize); 
    /*------------------------------------------------------------------------ 
    /* Frame converts to opencv Mat 
    /*------------------------------------------------------------------------*/ 
       cv::Mat img(frame->height,frame->width,CV_8UC3,pFrameRGB->data[0]); 
       img=manipulate_image(img); //this is opencv Mat, do whatever you want, but don't change its dimensions and format 
    //manipulate_function can be considered as as simple as blurring 
       const int stride[] = {img.step[0] }; 
    /* opencv Mat converts back to AVFrame   */ 
       sws_scale(sws_ctx_bgr_yuv, &img.data, stride, 0, img.rows, frame->data, frame->linesize); 

      } 
      frame->pts = av_frame_get_best_effort_timestamp(frame); 
    /* AVFrame re-encodes to AVPacket and will be sent to encoder */ 
      ret = filter_encode_write_frame(frame, stream_index); 
      av_frame_free(&frame); 

      if (ret < 0) 
       goto end; 
     } else { 
      av_frame_free(&frame); 
     } 
    } else { 
     /* remux this frame without reencoding */ 
     av_packet_rescale_ts(&packet, 
          ifmt_ctx->streams[stream_index]->time_base, 
          ofmt_ctx->streams[stream_index]->time_base); 
     ret = av_interleaved_write_frame(ofmt_ctx, &packet); 
     if (ret < 0) 
      goto end; 
    } 
    av_free_packet(&packet); 
} 
+0

nếu có thể, thêm mã chuyển đổi vào câu trả lời của bạn thay vì chỉ liên kết nó. – Micka

+0

@ Micka bạn có nghĩa là toàn bộ nội dung không? Tôi có nghĩa là không để làm cho chủ đề này dài hơn so với những gì nó bây giờ. –

+0

@DavoodFalahati Những thứ liên quan. Nếu đó là 648 thì cũng vậy. Nó tốt hơn so với ai đó cần câu trả lời này trong một năm, nhấp vào liên kết và nó hướng đến một lỗi 404. –

Các vấn đề liên quan