h4tr3d / avcpp

C++ wrapper for FFmpeg
Other
459 stars 80 forks source link

Why do you use new Codec() object in examples? #96

Closed suratovvlad closed 8 months ago

suratovvlad commented 3 years ago

Hello! I am examining your project and decode/encode examples.

I found some strange places in the examples

Decode:

    if (vst.isValid()) {
        vdec = VideoDecoderContext(vst);
        vdec.setRefCountedFrames(true);

        vdec.open(Codec(), ec);
        if (ec) {
            cerr << "Can't open codec\n";
            return 1;
        }
    }

During initialization of VideoDecoderContext your code initializes Codec object and sets the code to the context

    } else {
        m_raw->codec_id   = !codec.isNull() ? codec.raw()->id : AV_CODEC_ID_NONE;
        m_raw->codec_type = type;
        m_raw->codec      = codec.raw();

        if (!codec.isNull()) {
            if (codec.raw()->pix_fmts != 0)
                m_raw->pix_fmt = *(codec.raw()->pix_fmts); // assign default value
            if (codec.raw()->sample_fmts != 0)
                m_raw->sample_fmt = *(codec.raw()->sample_fmts);
        }
    }

After that you call open() function with new object Codec().

However, in the example from ffmpeg documentation the codec initialized with avcodec_find_decoder is passed to the avcodec_open2.

The similar strange thing is with encode example:

    OutputFormat  ofrmt;
    FormatContext octx;

    ofrmt.setFormat(string(), out);
    octx.setFormat(ofrmt);

    Codec        ocodec  = findEncodingCodec(ofrmt);
    Stream      ost     = octx.addStream(ocodec);
    VideoEncoderContext encoder {ost};
....
    encoder.open(Codec(), ec);
    if (ec) {
        cerr << "Can't opent encodec\n";
        return 1;
    }

You create ocodec, but pass to encoder some new object.

Could you please explain why do you use uninitialized Codec object when you open decoder and encoder ?

h4tr3d commented 2 years ago

Hi,

All magic located here:

vdec = VideoDecoderContext(vst);

Just look into CodecContext2::CodecContext2(const Stream &st, const Codec &codec, Direction direction, AVMediaType type)

To simplify research, CodecContext2() called like:

CodecContext2(vst, Codec(), Direction::Decoding, AVMEDIA_TYPE_VIDEO)

Null Codec here means "use vst for the codec selection" and needed only for some corner cases, when codec overriding are needed:

... CodecContext2()...
...
    Codec c = codec;
    if (codec.isNull())
    {
        if (st.direction() == Direction::Decoding)
            c = findDecodingCodec(codecId);
        else
            c = findEncodingCodec(codecId);
    }
...

Open routine can be simplified to:

vdec.open(ec);

This mostly same to

vdec.open(Codec(), ec);

For opening this routine are used:

void CodecContext2::open(const Codec &codec, AVDictionary **options, OptionalErrorCode ec)
{
    clear_if(ec);

    if (isOpened() || !isValid()) {
        throws_if(ec, isOpened() ? Errors::CodecAlreadyOpened : Errors::CodecInvalid);
        return;
    }

    int stat = avcodec_open2(m_raw, codec.raw(), options);
    if (stat < 0)
        throws_if(ec, stat, ffmpeg_category());
}

You may be confused, that avcodec_open2() calls with NULL codec. But, look into avcodec_open2() description:

 * @param codec The codec to open this context for. If a non-NULL codec has been
 *              previously passed to avcodec_alloc_context3() or
 *              for this context, then this parameter MUST be either NULL or
 *              equal to the previously passed codec.

We are initialized VideoDecoderContext with the Stream reference. And already pass correct codec to the avcodec_alloc_context3(). So, now you have two ways:

  1. Add extra unuseful code to pass correct codec into open()
  2. Or just pass Empty/Null codec in to it )
h4tr3d commented 2 years ago

Ah! Codec() provides Null Codec :-)

And sorry for the long delay :-)