ffmpegvideo-encodinglibavcodecmuxeravcodec

How to replace avcodec_encode_audio2()/avcodec_encode_video2() with avcodec_send_frame() and avcodec_receive_packet()?


In muxing example in link am attempting to use avcodec_send_frame() and avcodec_receive_packet() instead of avcodec_encode_audio2()/avcodec_encode_video2() as they are deprecated. In

  352     ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
  353     if (ret < 0) {
  354         fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
  355         exit(1);
  356     }
  357 
  358     if (got_packet) {
  359         ret = write_frame(oc, &c->time_base, ost->st, &pkt);
  360         if (ret < 0) {
  361             fprintf(stderr, "Error while writing audio frame: %s\n",
  362                     av_err2str(ret));
  363             exit(1);
  364         }
  365     }
  366 
  367     return (frame || got_packet) ? 0 : 1;

and in

  522     ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
  523     if (ret < 0) {
  524         fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
  525         exit(1);
  526     }
  527 
  528     if (got_packet) {
  529         ret = write_frame(oc, &c->time_base, ost->st, &pkt);
  530     } else {
  531         ret = 0;
  532     }
  533 
  534     if (ret < 0) {
  535         fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
  536         exit(1);
  537     }
  538 
  539     return (frame || got_packet) ? 0 : 1;

What to assign got_packet variable with when using avcodec_send_frame() and avcodec_receive_packet() functions and how to change the code if I do?. I have tried this so far

ret = avcodec_send_frame(c, frame);
if (ret < 0) {
    fprintf(stderr, "Error sending the frame to the audio encoder\n");
    exit(1);
}
if (ret = 0) {
    got_packet = avcodec_receive_packet(c, &pkt);
    if (got_packet == AVERROR(EAGAIN) || got_packet == AVERROR_EOF){
        fprintf(stderr, "Error receiving packet\n");
        return -1;}
    else if (got_packet < 0) {
        fprintf(stderr, "Error encoding audio frame\n");
        exit(1);
    }
    ret1 = write_frame(oc, &c->time_base, ost->st, &pkt);
    if (ret1 < 0) {
        fprintf(stderr, "Error while writing audio frame: %s\n",
                av_err2str(ret1));
        exit(1);
    }
    av_packet_unref(&pkt);
}
return (frame || got_packet) ? 0 : 1;

but isn't working and am having hard time getting it to work.


Solution

  • In avcodec_encode_video2(), got_packet_ptr will be 0 when output packet is empty. The got_packet_ptr will equal 1 when output packet is non-empty and during draining mode (at end-of-file situations) when NULL is sent to avcodec_encode_video2() until all buffered items are flushed after which it becomes 0 again.

    In new API, return value from avcodec_receive_packet() will be equal to 0 when buffer is full or when draining mode is entered, which is similar to got_packet_ptr being equal to 1. The avcodec_receive_packet() otherwise returns negative error code AVERROR(EAGAIN) which means output is not available in the current state - user must try to send input or AVERROR_EOF i.e, the encoder has been fully flushed, and there will be no more output packets.

    Using the new API in muxing.c example in the following way worked for me:

    ret = avcodec_send_frame(c, frame);
    if (ret < 0) {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }
    while (ret >= 0) {
        ret = avcodec_receive_packet(c, &pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return (ret==AVERROR(EAGAIN)) ? 0:1;
         }
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }
        ret = write_frame(oc, &c->time_base, ost->st, &pkt);
        if (ret < 0) {
           fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
           exit(1);
        }
        av_packet_unref(&pkt);
    }
    return (frame) ? 0 : 1;