[Libav-user] Transcode with the some properties

Thomas tfreudi1 at gmx.de
Mon Nov 4 15:42:05 EET 2019


В сообщении от Monday 04 November 2019 14:22:32 Boris написал(а):
> Le lun. 4 nov. 2019 à 11:47, Andrew Randrianasulu <randrianasulu at gmail.com>
> a écrit :
> 
> > В сообщении от Monday 04 November 2019 12:29:10 Boris написал(а):
> > > Hello,
> > > I want to transcode video with exactly the same properties of the input
> > > video in the output video.
> > > I use the transcoding.cc code. In the static int open_output_file(const
> > > char *filename) function, I set  encoder parameters like this :
> > >
> > > if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
> > >                         {
> > >                                 enc_ctx->height = dec_ctx->height;
> > >                                 enc_ctx->width = dec_ctx->width;
> > >                                 enc_ctx->sample_aspect_ratio =
> > > dec_ctx->sample_aspect_ratio;
> > >
> > > enc_ctx->global_quality=dec_ctx->global_quality;
> > >                                 enc_ctx->gop_size=dec_ctx->gop_size;
> > >                                 enc_ctx->bit_rate=dec_ctx->bit_rate;
> > >                                 enc_ctx->time_base=dec_ctx->time_base;
> > >                                 enc_ctx->delay=dec_ctx->delay;
> > >
> > > //**********************************************
> > >
> >  enc_ctx->rc_max_rate=dec_ctx->rc_max_rate;
> > >
> >  enc_ctx->rc_min_rate=dec_ctx->rc_min_rate;
> > >                                 /* take first format from list of
> > supported
> > > formats */
> > >                                 if (encoder->pix_fmts)
> > >                                 {
> > >                                         enc_ctx->pix_fmt =
> > > encoder->pix_fmts[0];
> > >                                 }
> > >                                 else
> > >                                 {
> > >                                         enc_ctx->pix_fmt =
> > dec_ctx->pix_fmt;
> > >                                 }
> > >                                 // video time_base can be set to whatever
> > > is handy and supported by encoder
> > >                                 // enc_ctx->time_base =
> > > av_inv_q(dec_ctx->framerate);
> > >                         }
> > >                         else
> > >                         {
> > >                                 enc_ctx->sample_rate =
> > dec_ctx->sample_rate;
> > >                                 enc_ctx->channel_layout =
> > > dec_ctx->channel_layout;
> > >                                 enc_ctx->channels =
> > > av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
> > >                                 /* take first format from list of
> > supported
> > > formats */
> > >                                 enc_ctx->sample_fmt =
> > > encoder->sample_fmts[0];
> > >                                 enc_ctx->time_base = (AVRational){1,
> > > enc_ctx->sample_rate};
> > >                         }
> > >                         /* Third parameter can be used to pass settings
> > to
> > > encoder */
> > >                         ret = avcodec_open2(enc_ctx, encoder, NULL);
> > >                         if (ret < 0)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_ERROR, "Cannot open
> > > video encoder for stream #%u\n", i);
> > >                                 return ret;
> > >                         }
> > >                         ret =
> > > avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
> > >                         if (ret < 0)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_ERROR, "Failed to
> > copy
> > > encoder parameters to output stream #%u\n", i);
> > >                                 return ret;
> > >                         }
> > >                         if (ofmt_ctx->oformat->flags &
> > AVFMT_GLOBALHEADER)
> > >                                 enc_ctx->flags |=
> > > AV_CODEC_FLAG_GLOBAL_HEADER;
> > >
> > >                         out_stream->time_base = enc_ctx->time_base;
> > >
> > >
> > > But when I run exiftool on output  video and on input video, some values
> > > like video duration, encoder, bit rate,  are differente.
> >
> > Hm, video duration chnage sounds like most unwelcome one ...
> > But are codec parameters even supposed to be the same after different
> > encoder run over output of another encoder?
> 
> I recall term 'bit exact', but is this applicable to re-encoding with lossy
> > codecs?
> >
> 
> I'm not sure but I thought it could be done. If you have things that I do
> not know, do not hesitate to share them with me,please.

Addition (I have those links open, may be they will be useful here for getting bigger picture ..)

https://koushtav.me/jpeg/tutorial/c++/decoder/2019/03/02/lets-write-a-simple-jpeg-library-part-2/
https://koushtav.me//jpeg/tutorial/2017/11/25/lets-write-a-simple-jpeg-library-part-1/

basically anatomy (and logic/math) behind jpeg image compression (for video  this will be motion jpeg)

As you hopefully see - it all just run over blocks of numbers, with some throwning away (so, this is lossy codec as opposed to lossless).

After first thrown away run you will have different bunch of numbers to play with, 
so even if you configure encoder to do exactly the same - input already will be different ...

mpeg1/2/4/etc all add complexity over such basic idea ....

http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2013/11/3_Warhol.html

some bit of history explainiing (hopefully) why compression was used and still used, 
and not just compression but most often lossy one:

--------
 It was hard because it required very high data throughput. The way you want video is the way it is now, full screen at 24 frames per second or better. Full screen in 1987 meant 640x480 pixels. To render that size image in 24 bits-per-pixel color took over 900,000 bytes per frame. At 24 frames per second (the standard for film) this meant 22,118,400 bytes (21.09 MBytes) per second. The video had to come from your hard disk or from CDROM. There was a lot of excitement about CDROM at that time, but the CDROM drives at that time could only hold about 30 seconds of uncompressed video at that rate, and anyway, they could only transfer 150 Kbytes per second. It would take seconds to move a single frame from a CDROM into a computer's memory. Transfer rates were nominally better for hard disks, but for the most popular personal computer at the time, the IBM-AT and its clones, the hard disk could only move about 100-200 KBytes into the computer every second. This was nominally faster than a CDROM, but a far cry from what it would take to play a digital episode of The Cosby Show on your computer screen.
------end of quotation-----

There is also question of chroma (color) subsampling and color spaces ..not directly about codec, but playing their role in making image not mathematically-exact, after few transforms (usually)

> 
> Regards
> 
> Hello
> 
> > > Can someone tells me how can I do to keep the same parameters (metada) of
> > > the input video in the output video, please?
> > >
> > > The entire open_output_file function is the following :
> > >
> > > static int open_output_file(const char *filename)
> > > {
> > >          AVStream *out_stream;
> > >         AVStream *in_stream;
> > >         AVCodecContext *dec_ctx, *enc_ctx;
> > >         AVCodec *encoder;
> > >         int ret;
> > >         unsigned int i;
> > >         ofmt_ctx = NULL;
> > >         avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
> > >         if (!ofmt_ctx)
> > >         {
> > >                 av_log(NULL, AV_LOG_ERROR, "Could not create output
> > > context\n");
> > >                 return AVERROR_UNKNOWN;
> > >         }
> > >
> > >         for (i = 0; i < ifmt_ctx->nb_streams; i++)
> > >         {
> > >                 out_stream = avformat_new_stream(ofmt_ctx, NULL);
> > >                 if (!out_stream)
> > >                 {
> > >                         av_log(NULL, AV_LOG_ERROR, "Failed allocating
> > > output stream\n");
> > >                         return AVERROR_UNKNOWN;
> > >                 }
> > >
> > >                 in_stream = ifmt_ctx->streams[i];
> > >                 dec_ctx = stream_ctx[i].dec_ctx;
> > >
> > >                 if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
> > > dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
> > >                 {
> > >                         //AVCodecID codec_id = dec_ctx->codec_type ==
> > > AVMEDIA_TYPE_VIDEO ? AV_CODEC_ID_MPEG4:dec_ctx->codec_id;
> > >                         /* in this example, we choose transcoding to same
> > > codec */
> > >                         //encoder = dec_ctx->codec_type ==
> > > AVMEDIA_TYPE_VIDEO ?
> > >
> > avcodec_find_encoder(AV_CODEC_ID_H264):avcodec_find_encoder(dec_ctx->codec_id);//(AV_CODEC_ID_MPEG4):avcodec_find_encoder(dec_ctx->codec_id);
> > >                         encoder = dec_ctx->codec_type ==
> > AVMEDIA_TYPE_VIDEO
> > > ?
> > >
> > avcodec_find_encoder(AV_CODEC_ID_MPEG4):avcodec_find_encoder(dec_ctx->codec_id);
> > >                         // if(dec_ctx->codec_type ==
> > >
> > AVMEDIA_TYPE_VIDEO){encoder=avcodec_find_encoder(AV_CODEC_ID_MPEG4);}else{encoder=avcodec_find_encoder(dec_ctx->codec_id);}
> > >                         //encoder =
> > avcodec_find_encoder(dec_ctx->codec_id);
> > >                         if (!encoder)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_FATAL, "Necessary
> > > encoder not found\n");
> > >                                 return AVERROR_INVALIDDATA;
> > >                         }
> > >                         enc_ctx = avcodec_alloc_context3(encoder);
> > >                         if (!enc_ctx)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_FATAL, "Failed to
> > > allocate the encoder context\n");
> > >                                 return AVERROR(ENOMEM);
> > >  }
> > >
> > >                         /* In this example, we transcode to same
> > properties
> > > (picture size,
> > >                         * sample rate etc.). These properties can be
> > > changed for output
> > >                         * streams easily using filters */
> > >                         if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
> > >                         {
> > >                                 enc_ctx->height = dec_ctx->height;
> > >                                 enc_ctx->width = dec_ctx->width;
> > >                                 enc_ctx->sample_aspect_ratio =
> > > dec_ctx->sample_aspect_ratio;
> > >
> > > enc_ctx->global_quality=dec_ctx->global_quality;
> > >                                 enc_ctx->gop_size=dec_ctx->gop_size;
> > >                                 enc_ctx->bit_rate=dec_ctx->bit_rate;
> > >                                 enc_ctx->time_base=dec_ctx->time_base;
> > >                                 enc_ctx->delay=dec_ctx->delay;
> > >
> > > //**********************************************
> > >
> >  enc_ctx->rc_max_rate=dec_ctx->rc_max_rate;
> > >
> >  enc_ctx->rc_min_rate=dec_ctx->rc_min_rate;
> > >                                 /* take first format from list of
> > supported
> > > formats */
> > >                                 if (encoder->pix_fmts)
> > >                                 {
> > >                                         enc_ctx->pix_fmt =
> > > encoder->pix_fmts[0];
> > >                                 }
> > >                                 else
> > >                                 {
> > >                                         enc_ctx->pix_fmt =
> > dec_ctx->pix_fmt;
> > >                                 }
> > >                                 // video time_base can be set to whatever
> > > is handy and supported by encoder
> > >                                 // enc_ctx->time_base =
> > > av_inv_q(dec_ctx->framerate);
> > >                         }
> > >                         else
> > >                         {
> > >                                 enc_ctx->sample_rate =
> > dec_ctx->sample_rate;
> > >                                 enc_ctx->channel_layout =
> > > dec_ctx->channel_layout;
> > >                                 enc_ctx->channels =
> > > av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
> > >                                 /* take first format from list of
> > supported
> > > formats */
> > >                                 enc_ctx->sample_fmt =
> > > encoder->sample_fmts[0];
> > >                                 enc_ctx->time_base = (AVRational){1,
> > > enc_ctx->sample_rate};
> > >                         }
> > >                         /* Third parameter can be used to pass settings
> > to
> > > encoder */
> > >                         ret = avcodec_open2(enc_ctx, encoder, NULL);
> > >                         if (ret < 0)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_ERROR, "Cannot open
> > > video encoder for stream #%u\n", i);
> > >                                 return ret;
> > >                         }
> > >                         ret =
> > > avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
> > >                         if (ret < 0)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_ERROR, "Failed to
> > copy
> > > encoder parameters to output stream #%u\n", i);
> > >                                 return ret;
> > >                         }
> > >                         if (ofmt_ctx->oformat->flags &
> > AVFMT_GLOBALHEADER)
> > >  enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
> > >
> > >                         out_stream->time_base = enc_ctx->time_base;
> > >                         stream_ctx[i].enc_ctx = enc_ctx;
> > >                 }
> > >                 else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN)
> > >                 {
> > >                         av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d
> > > is of unknown type, cannot proceed\n", i);
> > >                         return AVERROR_INVALIDDATA;
> > >                 }
> > >                 else
> > >                 {
> > >                         // if this stream must be remuxed
> > >                         ret =
> > avcodec_parameters_copy(out_stream->codecpar,
> > > in_stream->codecpar);
> > >                         //ifmt_ctx->streams[i]->codec);
> > >                         if (ret < 0)
> > >                         {
> > >                                 av_log(NULL, AV_LOG_ERROR, "Copying
> > > parameters for stream #%u failed\n", i);
> > >                                 return ret;
> > >                         }
> > >                         out_stream->time_base = in_stream->time_base;
> > >                 }
> > >
> > >         }
> > >         av_dump_format(ofmt_ctx, 0, filename, 1);
> > >
> > >         if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
> > >         {
> > >                 ret = avio_open(&ofmt_ctx->pb, filename,
> > AVIO_FLAG_WRITE);
> > >                 if (ret < 0)
> > >                 {
> > >                         av_log(NULL, AV_LOG_ERROR, "Could not open output
> > > file '%s'", filename);
> > >                         return ret;
> > >                 }
> > >         }
> > >
> > >         /* init muxer, write output file header */
> > >         ret = avformat_write_header(ofmt_ctx, NULL);
> > >         if (ret < 0)
> > >         {
> > >                 av_log(NULL, AV_LOG_ERROR, "Error occurred when opening
> > > output file\n");
> > >                 return ret;
> > >         }
> > >         return 0;
> > > }
> > >
> > > Rgards
> > >
> >
> >
> > _______________________________________________
> > Libav-user mailing list
> > Libav-user at ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/libav-user
> >
> > To unsubscribe, visit link above, or email
> > libav-user-request at ffmpeg.org with subject "unsubscribe".
> 

I'm not an expert in that but I guess also if you use multithreading the result on the same hardware, same OS, same source material will likely not produce  bit-comparable results, because different threads work on different parts of the image/stream. 



More information about the Libav-user mailing list