使用 libav 将 bgr 图像的 h264 编码为 .mp4 文件

时间:2021-02-09 23:09:54

标签: c++ opencv ffmpeg h.264 libavcodec

我正在尝试将 (h264) 一系列 .png 编码为 mp4 文件。 cv::Mat 保存 png 数据 (BGR),然后将其转换为 YUV420P,然后将其编码并写入 .mp4 文件。我在代码中添加了两个块语句来将图像存储在磁盘上(编码前后)。编码前的第一个图像是正确的,但编码后的第二个图像不正确。 avcodec_send_frame 返回 0,所以到那时一切正常。 编辑:我得到一个 1 MB 的 mp4 文件,但我无法用 vlc 打开它 生态解码器.h

class ECodec
{
public:

    MovieCodec();

    ~MovieCodec();

    void MatToFrame( cv::Mat& image );

    void encode( AVFrame *frame, AVPacket *pkt );

private:
 
    FILE* m_file;

    AVCodec* m_encoder = NULL;

    AVCodecContext* m_codecContextOut = NULL;

    AVPacket* m_packet = NULL;

};

ecodec.cpp

ECodec::ECodec() :
//    m_encoder( avcodec_find_encoder_by_name( videoCodec.c_str()))
    m_encoder( avcodec_find_encoder( AV_CODEC_ID_H264 ))
{
    m_file = fopen( "c:\\tmp\\outputVideo.mp4", "wb");
}



void ECodec::MatToFrame( cv::Mat& image )
{
    int ret( 0 );
    int frameRate( 24 );
    AVFrame *frame = NULL;

    m_encoder( avcodec_find_encoder( AV_CODEC_ID_H264 ))
    m_codecContextOut = avcodec_alloc_context3( m_encoder );

    m_codecContextOut->width = 800;
    m_codecContextOut->height = 640;
    m_codecContextOut->bit_rate = 400000;//m_codecContextOut->width * m_codecContextOut->height * 3;
    m_codecContextOut->time_base = (AVRational){1, 24};
    m_codecContextOut->framerate = (AVRational){24, 1};
    m_codecContextOut->codec_tag = AV_CODEC_ID_H264;
    m_codecContextOut->pix_fmt = AV_PIX_FMT_YUV420P;
    m_codecContextOut->codec_type = AVMEDIA_TYPE_VIDEO;
    m_codecContextOut->gop_size = 1;
    m_codecContextOut->max_b_frames = 1;

    av_log_set_level(AV_LOG_VERBOSE);

    ret = av_opt_set(m_codecContextOut->priv_data, "preset", "slow", 0);

    ret = avcodec_open2(m_codecContextOut, m_encoder, NULL);

    frame = av_frame_alloc();

    frame->format = AV_PIX_FMT_YUV420P;
    frame->width = image.cols();
    frame->height = image.rows();


    ret = av_image_alloc(frame->data, frame->linesize, frame->width,  frame->height, AV_PIX_FMT_YUV420P, 1);

    if (ret < 0)
    {
        return;
    }

    struct SwsContext *sws_ctx;
    sws_ctx = sws_getContext((int)image.cols(), (int)image.rows(), AV_PIX_FMT_RGB24,
                             (int)image.cols(), (int)image.rows(), AV_PIX_FMT_YUV420P,
                             0, NULL, NULL, NULL);

    const uint8_t* rgbData[1] = { (uint8_t* )image.getData() };
    int rgbLineSize[1] = { 3 * image.cols() };

    sws_scale(sws_ctx, rgbData, rgbLineSize, 0, image.rows(), frame->data, frame->linesize);

    frame->pict_type = AV_PICTURE_TYPE_I;

cv::Mat yuv420p(frame->height + frame->height/2, frame->width, CV_8UC1,frame->data[0]);
cv::Mat cvmIm;
cv::cvtColor(yuv420p,cvmIm,CV_YUV420p2BGR);
cv::imwrite("c:\\tmp\\rawimage.png", cvmIm);
//OK

    m_packet = av_packet_alloc();
    ret = av_new_packet( m_packet, m_codecContextOut->width * m_codecContextOut->height * 3 );

    /* encode the image */
    encode( frame, m_packet );


    avcodec_free_context(&m_codecContextOut);
    av_frame_free(&frame);
    av_packet_free( &m_packet );
}




void ECodec::encode( AVFrame *frame, AVPacket *pkt )
{
    int ret;
    
    /* send the frame to the encoder */
    ret = avcodec_send_frame( m_codecContextOut, frame);

    if (ret < 0)
    {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }

    do
    {
        ret = avcodec_receive_packet(m_codecContextOut, pkt);
        if (ret == 0)
        {

cv::Mat yuv420p(frame->height + frame->height/2, frame->width, CV_8UC1,pkt->data);
cv::Mat cvmIm;
cv::cvtColor(yuv420p,cvmIm,CV_YUV420p2BGR);
cv::imwrite("c:\\tmp\\rawencodedimage.png", cvmIm);
//NOT OK
            fwrite(pkt->data, 1, pkt->size, m_file );
            av_packet_unref(pkt);

            break;
        }
        else if ((ret < 0) && (ret != AVERROR(EAGAIN)))
        {
            return;
        }
        else if (ret == AVERROR(EAGAIN))
        {
             ret = avcodec_send_frame(m_codecContextOut, NULL);
             if (0 > ret)
             {
                 return;
             }
        }
    } while (ret == 0);
}

0 个答案:

没有答案