首页 > 解决方案 > H264编解码器编码、解码和写入文件

问题描述

我尝试使用 ffmpeg 和 h264 编解码器实时翻译视频。但是在解码编码帧的状态下,我得到了一些“坏”的图像。初始化编码器和解码器:

    VCSession *vc_new_x264(Logger *log, ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data,
                       VCSession *vc)
{

// ------ ffmpeg encoder ------
    AVCodec *codec2 = NULL;
    vc->h264_encoder_ctx = NULL;//AVCodecContext type

    codec2 = NULL;
    avcodec_register_all();
    codec2 = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (codec2 == NULL)
    {
        LOGGER_WARNING(log, "h264: not find encoder");
    }

    vc->h264_encoder_ctx = avcodec_alloc_context3(codec2);

    vc->h264_out_pic2 = av_packet_alloc();

    vc->h264_encoder_ctx->bit_rate = 10 *1000 * 1000;
    vc->h264_encoder_ctx->width = 800;
    vc->h264_encoder_ctx->height = 600;

    vc->h264_enc_width = vc->h264_encoder_ctx->width;
    vc->h264_enc_height = vc->h264_encoder_ctx->height;
    vc->h264_encoder_ctx->time_base = (AVRational) {
            1, 30
    };
    vc->h264_encoder_ctx->gop_size = 30;
    vc->h264_encoder_ctx->max_b_frames = 1;
    vc->h264_encoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;


    av_opt_set(vc->h264_encoder_ctx->priv_data, "preset", "veryfast", 0);


    av_opt_set(vc->h264_encoder_ctx->priv_data, "annex_b", "1", 0);
    av_opt_set(vc->h264_encoder_ctx->priv_data, "repeat_headers", "1", 0);
    av_opt_set(vc->h264_encoder_ctx->priv_data, "tune", "zerolatency", 0);
    av_opt_set_int(vc->h264_encoder_ctx->priv_data, "zerolatency", 1, 0);

    vc->h264_encoder_ctx->time_base.num = 1;
    vc->h264_encoder_ctx->time_base.den = 1000;

    vc->h264_encoder_ctx->framerate = (AVRational) {
        1000, 40
    };

    AVDictionary *opts = NULL;

    if (avcodec_open2(vc->h264_encoder_ctx, codec2, &opts) < 0) {
        LOGGER_ERROR(log, "could not open codec H264 on encoder");
    }

    av_dict_free(&opts);



    AVCodec *codec = NULL;
    vc->h264_decoder_ctx = NULL;// AVCodecContext - type
    codec = NULL;

    codec = avcodec_find_decoder(AV_CODEC_ID_H264);

    if (!codec) {
        LOGGER_WARNING(log, "codec not found H264 on decoder");
    }

    vc->h264_decoder_ctx = avcodec_alloc_context3(codec);

    if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
        vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
    }

    if (codec->capabilities & AV_CODEC_FLAG_LOW_DELAY) {
        vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
    }

    vc->h264_decoder_ctx->flags |= AV_CODEC_FLAG2_SHOW_ALL;

     vc->h264_decoder_ctx->refcounted_frames = 0;

    vc->h264_decoder_ctx->delay = 0;
    vc->h264_decoder_ctx->sw_pix_fmt = AV_PIX_FMT_YUV420P;
    av_opt_set_int(vc->h264_decoder_ctx->priv_data, "delay", 0, AV_OPT_SEARCH_CHILDREN);
    vc->h264_decoder_ctx->time_base = (AVRational) {
            40, 1000
};
    vc->h264_decoder_ctx->framerate = (AVRational) {
        1000, 40
    };

    if (avcodec_open2(vc->h264_decoder_ctx, codec, NULL) < 0) {
        LOGGER_WARNING(log, "could not open codec H264 on decoder");
    }
    vc->h264_decoder_ctx->refcounted_frames = 0;

    return vc;
}

编码(在这个函数中,我对帧进行编码并用于调试解码并将他保存在文件中):

uint32_t encode_frame_h264_p(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height,
                           const uint8_t *y,
                           const uint8_t *u, const uint8_t *v, ToxAVCall *call,
                           uint64_t *video_frame_record_timestamp,
                           int vpx_encode_flags,
                           x264_nal_t **nal,
                           int *i_frame_size)
{
    AVFrame *frame;
    int ret;
    uint32_t result = 1;

    frame = av_frame_alloc();

    frame->format = call->video->h264_encoder_ctx->pix_fmt;
    frame->width  = width;
    frame->height = height;

    ret = av_frame_get_buffer(frame, 32);

    if (ret < 0) {
        LOGGER_ERROR(av->m->log, "av_frame_get_buffer:Could not allocate the video frame data");
    }

    /* make sure the frame data is writable */
    ret = av_frame_make_writable(frame);

    if (ret < 0) {
        LOGGER_ERROR(av->m->log, "av_frame_make_writable:ERROR");
    }

    frame->pts = (int64_t)(*video_frame_record_timestamp);


    // copy YUV frame data into buffers
    memcpy(frame->data[0], y, width * height);
    memcpy(frame->data[1], u, (width / 2) * (height / 2));
    memcpy(frame->data[2], v, (width / 2) * (height / 2));

    // encode the frame
    ret = avcodec_send_frame(call->video->h264_encoder_ctx, frame);

    if (ret < 0) {
        LOGGER_ERROR(av->m->log, "Error sending a frame for encoding:ERROR");
    }


    ret = avcodec_receive_packet(call->video->h264_encoder_ctx, call->video->h264_out_pic2);



    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        *i_frame_size = 0;
    } else if (ret < 0) {
        *i_frame_size = 0;
        // fprintf(stderr, "Error during encoding\n");
    } else {

        // Decoded encoded frame and save him to file

        saveInFile(call->video->h264_decoder_ctx, frame, call->video->h264_out_pic2, "/home/user/testSave");

        // printf("Write packet %3"PRId64" (size=%5d)\n", call->video->h264_out_pic2->pts, call->video->h264_out_pic2->size);
        // fwrite(call->video->h264_out_pic2->data, 1, call->video->h264_out_pic2->size, outfile);

        global_encoder_delay_counter++;

        if (global_encoder_delay_counter > 60) {
            global_encoder_delay_counter = 0;
            LOGGER_DEBUG(av->m->log, "enc:delay=%ld",
                         (long int)(frame->pts - (int64_t)call->video->h264_out_pic2->pts)
                        );
        }


        *i_frame_size = call->video->h264_out_pic2->size;
        *video_frame_record_timestamp = (uint64_t)call->video->h264_out_pic2->pts;

        result = 0;
    }

    av_frame_free(&frame);

    return result;

}

解码并保存帧代码:

void saveInFile(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, const char *filename)
{
    if (!pkt)
        return;
    char buf[1024];
    int ret;
    static int curNumber = 0;
    ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0 && ret != AVERROR_EOF)
    {
        fprintf(stderr, "Error sending a packet for decoding'\n");
        if ( ret == AVERROR(EAGAIN))
            return;
        if (ret == AVERROR(EINVAL))
            return;
        if (ret == AVERROR(ENOMEM))
            return;

    }

        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) )
            return;
        if (ret == AVERROR_EOF)
        {
            return;
        }
        else if (ret < 0)
        {
            fprintf(stderr, "Error during decoding\n");
        }
        printf("saving frame %3d\n", dec_ctx->frame_number);
        sprintf(buf, "%s%d", filename, curNumber);
        curNumber++;
        pgm_save(frame->data[0], frame->linesize[0], frame->width, frame->height, buf);

}

void pgm_save(unsigned char* buf, int wrap, int xsize, int ysize, char *filename)
{
    FILE *f;
    int i;
    f = fopen(filename, "w");
    fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
    for (i =0; i < ysize; i++)
        fwrite(buf + i* wrap, 1, xsize, f);
    fclose(f);
}

在这个操作之后,我有这样的事情: 形象不佳

标签: cffmpegh.264encoderdecoder

解决方案


推荐阅读