h264 encoding of bgr images into .mp4 file with libav

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

h264 encoding of bgr images into .mp4 file with libav

laddoe
I'm trying to encode(h264) a series of .png into a mp4 file. A cv::Mat holds the png data (BGR) and that is converted to YUV420P which is then encoded and written to a .mp4 file. I have added a block statement in the code to store image on the disk (before encoding) and that image is correct so the frame holds the correct data before it gets passed. The avcodec_send_frame returns 0 so up to that point everything works. But I get an mp4 file of 1 MB and I can't open it with vlc. Below is a short summary of my code


ecodec.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)
        {
            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);
}
_______________________________________________
ffmpeg-user mailing list
[hidden email]
https://ffmpeg.org/mailman/listinfo/ffmpeg-user

To unsubscribe, visit link above, or email
[hidden email] with subject "unsubscribe".
Reply | Threaded
Open this post in threaded view
|

Re: h264 encoding of bgr images into .mp4 file with libav

Paul B Mahol
On Wed, Feb 10, 2021 at 12:55 PM laddoe <[hidden email]> wrote:

> I'm trying to encode(h264) a series of .png into a mp4 file. A cv::Mat
> holds the png data (BGR) and that is converted to YUV420P which is then
> encoded and written to a .mp4 file. I have added a block statement in the
> code to store image on the disk (before encoding) and that image is correct
> so the frame holds the correct data before it gets passed. The
> avcodec_send_frame returns 0 so up to that point everything works. But I
> get an mp4 file of 1 MB and I can't open it with vlc. Below is a short
> summary of my code
>
>
> ecodec.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)
>         {
>             fwrite(pkt->data, 1, pkt->size, m_file );
>

You are writing raw encoded frames here.

You nowhere use mov muxer code for muxing.

            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);
> }
> _______________________________________________
> ffmpeg-user mailing list
> [hidden email]
> https://ffmpeg.org/mailman/listinfo/ffmpeg-user
>
> To unsubscribe, visit link above, or email
> [hidden email] with subject "unsubscribe".
_______________________________________________
ffmpeg-user mailing list
[hidden email]
https://ffmpeg.org/mailman/listinfo/ffmpeg-user

To unsubscribe, visit link above, or email
[hidden email] with subject "unsubscribe".