On Wed, May 6, 2009 at 2:14 PM, Tom Goossens <[email protected]> wrote:

> Dear list,
>
> Recently I made a c++ implementation of an mpeg4 encoder heavily based on
> the libavcodec/api-example.c. The encoder works. Hoewever, when I use
> mplayer to play the encoded file there is no time information in the status
> bar. Worse, I think this prohibits me from using seek to go to a specific
> frame in the file.
>
> I've set the timbase with
>     m_pCodecCtx->time_base.num = 1;
>     m_pCodecCtx->time_base.den = 25;
>
> Do I need to put in other information in order to get the timing
> information in the file? Must I explicitly set the pts for each frame? This
> does no seem to work with AVFrame->pts.
>
> So far I've had little luck in finding an answer here in the list or
> elsewhere. Any help would be greatly appreciated.
>
> Kind regards,
> Tom Goos
>


Hello all,

I'm including the c++ class I made for encoding my video (It's mostly coming
from a capture card).
I hope anyone can give a suggestion as to what I'm missing to get the
correct timing information in the MPEG4 file.

Kind regards,
Tom

PS I left out the header because most of it is pretty obvious. I can post it
if requested.


#include "stdio.h"
#include "string.h"
#include "ImageBaseClass.h"
#include "CFfmpegEnc.h"
extern "C" {
    #include "ffmpeg/avcodec.h"
    #include "ffmpeg/avformat.h"
    #include "ffmpeg/swscale.h"
    #include "ffmpeg/rational.h"
}

CFfmpegEnc::CFfmpegEnc() {
    m_pCodec    = NULL;
    m_pCodecCtx = NULL;
    m_pAVFrame    = NULL;
    m_pFile        = NULL;
    m_pOutbuf    = NULL;
    m_outbuf_size     = 0;
    m_out_size        = 0;
}

CFfmpegEnc::~CFfmpegEnc() {
    close();
}

int CFfmpegEnc::init(const char *filename, enum CodecID codecId, int width,
int height) {
    uint8_t *    picture_buf;
    int            size;

    /* must be called before using avcodec lib */
    avcodec_init();
    /* register all the codecs */
    avcodec_register_all();
    av_register_all();

    /* find the video encoder */
    m_pCodec = avcodec_find_encoder(codecId); // I'm using CODEC_ID_MPEG4
    if (!m_pCodec) {
        fprintf(stderr, "codec not found\n");
        return 1;
    }

    m_pCodecCtx    = avcodec_alloc_context();
    m_pAVFrame    = avcodec_alloc_frame();

    /* put sample parameters */
    m_pCodecCtx->bit_rate = 4000000;
    /* resolution must be a multiple of two */
    m_pCodecCtx->width = width;
    m_pCodecCtx->height = height;
    /* frames per second */
    m_pCodecCtx->time_base.num = 1;
    m_pCodecCtx->time_base.den = 25;
    m_pCodecCtx->gop_size = 10; /* emit one intra frame every ten frames */
    m_pCodecCtx->max_b_frames = 1;
    m_pCodecCtx->pix_fmt = PIX_FMT_YUV420P;

    /* open it */
    if (avcodec_open(m_pCodecCtx, m_pCodec) < 0) {
        fprintf(stderr, "could not open codec\n");
        return 2; // Could not open codec
    }

    m_pFile = fopen(filename, "wb");
    if (!m_pFile) {
        fprintf(stderr, "could not open file %s\n", filename);
        return 3;
    }

    /* alloc image and output buffer */
    m_outbuf_size = 1000000;
    m_pOutbuf = (uint8_t*)malloc(m_outbuf_size);
    size = m_pCodecCtx->width * m_pCodecCtx->height;
    picture_buf = (uint8_t*)malloc((size * 3) / 2); /* size for YUV 420 */

    m_pAVFrame->data[0] = picture_buf;
    m_pAVFrame->data[1] = m_pAVFrame->data[0] + size;
    m_pAVFrame->data[2] = m_pAVFrame->data[1] + size / 4;
    m_pAVFrame->linesize[0] = m_pCodecCtx->width;
    m_pAVFrame->linesize[1] = m_pCodecCtx->width / 2;
    m_pAVFrame->linesize[2] = m_pCodecCtx->width / 2;

    return 0;
}
int CFfmpegEnc::close() {
    uint8_t outbuf[4];

    /* add sequence end code to have a real mpeg file */
    outbuf[0] = 0x00;
    outbuf[1] = 0x00;
    outbuf[2] = 0x01;
    outbuf[3] = 0xb7;
    if(m_pFile) // !! When error during init, m_pFile will be NULL
    {
        fwrite(outbuf, 1, 4, m_pFile);
        fclose(m_pFile);
    }

    avcodec_close(m_pCodecCtx);
    av_freep(&m_pCodecCtx);
    av_freep(&m_pAVFrame);

    SAFE_DELETE(m_pOutbuf);
    printf("video file closed\n");

    return 0;
}

int CFfmpegEnc::addFrame(CImageBaseClass * image){
    AVFrame * AVFrame444;
    AVFrame444    = avcodec_alloc_frame();
    AVFrame444->data[0] = (uint8_t *)image->GetBuffer(0);
    AVFrame444->data[1] = (uint8_t *)image->GetBuffer(2);
    AVFrame444->data[2] =  (uint8_t *)image->GetBuffer(1);
    AVFrame444->linesize[0] = m_pCodecCtx->width;
    AVFrame444->linesize[1] = m_pCodecCtx->width;
    AVFrame444->linesize[2] = m_pCodecCtx->width;

    // the next section comes from
    //
http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c.html
    static struct SwsContext *img_convert_ctx;
    if(img_convert_ctx == NULL) {
        int w = m_pCodecCtx->width;
        int h = m_pCodecCtx->height;

        img_convert_ctx = sws_getContext(w, h, PIX_FMT_YUV444P,    //src
                                         w, h, PIX_FMT_YUV420P,        //dst
                                         SWS_BICUBIC,
                                         NULL, NULL, NULL);
        if(img_convert_ctx == NULL) {
            fprintf(stderr, "Cannot initialize the conversion context!\n");
            exit(1);
        }
    }
    // function template, for reference
    // int sws_scale(struct SwsContext *context, uint8_t* src[], int
srcStride[], int srcSliceY,
    //                               int srcSliceH, uint8_t* dst[], int
dstStride[]);
    int ret = sws_scale(img_convert_ctx, AVFrame444->data,
AVFrame444->linesize, 0,
            m_pCodecCtx->height, m_pAVFrame->data, m_pAVFrame->linesize);
    (void)ret; // unused variable

    // encode the image
    m_out_size = avcodec_encode_video(m_pCodecCtx, m_pOutbuf, m_outbuf_size,
m_pAVFrame);
//    printf("encoding frame (size=%5d)\n", out_size);
    fwrite(m_pOutbuf, 1, m_out_size, m_pFile);

    delete(AVFrame444);
    return 0;

}
_______________________________________________
libav-user mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/libav-user

Reply via email to