avformat_write_header is not working properly in ffmepg - c

I was working on mp4 file creation project using FFMPEG, i tried to convert the stream information of video packet based on FFMPEG muxing,remuxing code, but header get damaged after convert into so file is corrupted.
/* this code used to set the stream information */
AVFormatContext *input_context,*output_context;
AVDictionary *opt;
AVStream *out_stream;
AVCodecContext *newcontext = NULL;
out_stream= avformat_new_stream(output_context,NULL);
newcontext = avcodec_alloc_context3(codec);
newcontext->codec_id=Output_fmt->video_codec;
newcontext->bit_rate =in_stream->codec->bit_rate;
newcontext->width = in_stream->codec->width;
newcontext->height = in_stream->codec->height;
newcontext->timecode_frame_start = in_stream->codec->timecode_frame_start;
newcontext->gop_size = in_stream->codec->gop_size;
newcontext->profile = in_stream->codec->profile;
newcontext->level = in_stream->codec->level;
newcontext->pix_fmt = PIX_FMT_YUV420P;
newcontext->frame_size = in_stream->codec->frame_size;
newcontext->sample_fmt = in_stream->codec->sample_fmt;
newcontext->sample_rate = in_stream->codec->sample_rate;
time_base = (double)in_stream->time_base.num / (double)in_stream->time_base.den;
duration = (double)in_stream->duration * time_base * 1000.0;
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
return;
}
ret = avcodec_copy_context(out_stream->codec,newcontext);
if (ret < 0) {
fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
goto end;
}
out_stream->codec->codec_tag = 0;
if (output_context->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
Changed the Header Information using:
/* this code used to set the metadata */
av_dict_set(&opt, "major_brand", "mp42", 0);
av_dict_set(&opt, "minor_version","512" , 0);
av_dict_set(&opt, "compatible_brands","isomiso2avc1mp41",0);
av_dict_set(&opt, "comment","Hash=855738390",0);
output_context->metadata = opt;
ret = avformat_write_header(output_context,NULL);
after create the mp4 file check file using ffmpeg in terminal. getting Error like this:
/this error message/
[mpeg4 # 0x7ff2b9811c00] header damaged Last message repeated 39
times [mov,mp4,m4a,3gp,3g2,mj2 # 0x7ff2ba800000] decoding for stream 0
failed [mov,mp4,m4a,3gp,3g2,mj2 # 0x7ff2ba800000] Could not find codec
parameters for stream 0 (Video: mpeg4 (mp4v / 0x7634706D), none, 376
kb/s): unspecified size Consider increasing the value for the
'analyzeduration' and 'probesize' options.

Easiest thing is to just download a freeware hex editor (for your specific O.S). Next is use desktop (commandline) version of FFmpeg (download a static build)
Use the commandline FFmpeg to convert Source to MP4 (ie: as mp4_ffmpeg.mp4)
Use your code to convert Source to MP4 (ie: as mp4_code.mp4)
Open both mp4_ffmpeg.mp4 & mp4_code.mp4 and compare bytes. The working one should be mp4_ffmpeg.mp4 so what's different from bytes produced with your code?
Things to look for :
All begin with ftyp?
moov is header and should be at start (sometimes at back after mdat which holds all a/v data in one chunk. To move the header of any mp4 to front or beginning bytes then use -movflags +faststart for example in commandline use : ffmpeg -i myfile.avi -movflags +faststart newfile.mp4)
Before each of the words moov or mdat, the previous 4 bytes are the size (in bytes) after you skip the 4 letters of word... are these sizes correct?
Do you have all the MP4 atoms (metadata sections) defined? They match what FFmpeg produced for its version of the MP4 converting?

Related

No-op remuxing of .avi file

I want to demux and then mux .avi file without changing anything.
My program is this (redacted for brevity):
AVFormatContext *input_format_context = NULL;
avformat_open_input(
&input_format_context,
input_url,
NULL, // fmt
NULL // options
);
avformat_find_stream_info(input_format_context, NULL);
AVFormatContext *output_format_context = NULL;
avformat_alloc_output_context2(
&output_format_context,
NULL, // oformat
NULL, // format_name
output_url
);
avio_open2(
&output_format_context->pb,
output_url,
AVIO_FLAG_WRITE,
NULL, // int_cb,
NULL // options
);
for (int i = 0; i < input_format_context->nb_streams; i++) {
avformat_new_stream(output_format_context, NULL);
AVStream *input_stream = input_format_context->streams[i];
AVStream *output_stream = output_format_context->streams[i];
AVCodecParameters *params = avcodec_parameters_alloc();
avcodec_parameters_copy(params, input_stream->codecpar);
output_stream->codecpar = params;
}
avformat_write_header(output_format_context, NULL))
AVPacket *input_packet = NULL;
input_packet = av_packet_alloc();
while (!av_read_frame(
input_format_context,
input_packet
)) {
av_write_frame(output_format_context, input_packet);
av_packet_unref(input_packet);
}
av_write_trailer(output_format_context);
Problem:
Output file is created but instead of close to 10 minute video it is a 24-second slide show consisting of around 3 frames.
It seems that the problem is (perhaps not the only one) lack of PTS on the packet.
When I explicitly print it (input_packet->pts) for each packet it is -9223372036854775808. And also the following warning is printed:
[avi # 0x562868c6c000] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
How do I then "fix my code to set the timestamps properly"?
I just found a solution.
I added this:
output_stream->time_base = input_stream->time_base;
which then, I understand, allows the video player to calculate PTS on the fly.
This does not remove the warning itself, though. I understand that .avi simply does not have PTS, so it's not a bug as such. To get rid of the warning one can manually set PTS on the packets:
input_packet->pts = calculated_ts;
I would think I should be able to also just do:
output_format_context->oformat->flags |= AVFMT_NOTIMESTAMPS;
However, I cannot do that:
error: assignment of member ‘flags’ in read-only object
So, it looks like ffmpeg is requiring PTS even for .avi or there's a bug or I'm still doing something wrong.

av_write_header - Error with sample format

I'm writing program with libav/ffmpeg to download internet radio stream and play it on soundcard with alsa.
I've managed to download stream and extract packet and frame.
I'm having problem with av_write_header() function which (according to this https://www.ffmpeg.org/doxygen/3.2/group__lavf__encoding.html#details) I must call. It crashes and gives me the following error:
[alsa # 0x55d7ba32e580] sample format 0x15001 is not supported
Number 0x15001 is 86017 in decimal, which is index in enum AVCodecID of MP3 format(AV_CODEC_ID_MP3) used by this stream. The sample format has index 3. I can't figure out why libav parses the header wrong.
Here is a part of my code that is responsible for configuring output:
avdevice_register_all();
AVOutputFormat *output = av_guess_format("alsa",NULL,NULL);
AVFormatContext *outputFormatContext = avformat_alloc_context();
outputFormatContext->oformat = output;
outputFormatContext->flags = AVFMT_NOFILE;
AVStream *stream = avformat_new_stream(outputFormatContext,NULL);
AVCodecParameters *oCodecParameters = avcodec_parameters_alloc();
ret = avcodec_parameters_copy(oCodecParameters,iCodecParameters);
if(ret < 0){
printf("avformat_parameters_copy\n");
exit(0);
}
stream->codecpar = oCodecParameters;
if(avformat_write_header(outputFormatContext,NULL)<0){
dumpParameters(stream->codecpar);
printf("avformat_write_header\n");
exit(0);
}
The full code is here: https://github.com/szymonbarszcz99/C-internet-radio
It seems that in libav we can't do simple copy. Instead I have to manually give it requested parameters. Changing avcodec_parameters_copy() to this
AVCodecParameters *oCodecParameters = avcodec_parameters_alloc();
oCodecParameters->format = 8;
oCodecParameters->codec_type = 1;
oCodecParameters->sample_rate = 44100;
oCodecParameters->channels = 2;
stream->codecpar = oCodecParameters;
fixes this problem

FFmpeg - H264 encoder could not find a valid device and can't configure encoder

I am trying to encode using the H264 encoder, but when I do, I get the following error:
[h264_v4l2m2m # 0x55682d2416c0] Could not find a valid device
[h264_v4l2m2m # 0x55682d2416c0] can't configure encoder
I made sure that I enabled the encoder when I configured FFmpeg. When I run the command ffmpeg -codecs I see that the H264 codec is listed as an encoder:
DEV.LS h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
(decoders: h264 h264_v4l2m2m )
(encoders: libx264 libx264rgb h264_v4l2m2m h264_vaapi )
The source video's video codec is H264 so I am not sure why I am not able to encode with H264 when there is both a decoder and encoder for it. Even when I run avcodec_find_encoder_by_name to find the libx264 encoder, it isn't able to.
This is the chunk of code where it fails:
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec) {
printf("[ERROR] Failed to find video output codec\n");
return -1;
}
outputCodecContext = avcodec_alloc_context3(codec);
if (!outputCodecContext) {
printf("[ERROR] Failed to allocate memory for video output codec context\n");
return -1;
}
av_opt_set(outputCodecContext->priv_data, "preset", "slow", 0);
outputCodecContext->bit_rate = inputCodecContext->bit_rate;
outputCodecContext->width = inputCodecContext->width;
outputCodecContext->height = inputCodecContext->height;
outputCodecContext->time_base = (AVRational){1, 60};
outputCodecContext->framerate = (AVRational){60, 1};
outputCodecContext->pix_fmt = inputCodecContext->pix_fmt;
outputCodecContext->extradata = inputCodecContext->extradata;
outputCodecContext->extradata_size = inputCodecContext->extradata_size;
// This if statement fails as a result of the encoder error
if (avcodec_open2(outputCodecContext, codec, NULL) < 0) {
printf("[ERROR] Failed to open video output codec\n");
return -1;
}
return 0;
When I encode with H264 using the ffmpeg command, I don't receive any of these errors. Any help is appreciated.
As it turns out, I kept forgetting to call make and make install (I'm on Ubuntu) after I used the command ./configure --enable-shared --enable-libx264 --enable-gpl. This removed the error I was receiving and my code was able to find the libx264 encoder.

How to set pts, dts and duration in ffmpeg library?

I want to pack some compressed video packets(h.264) to ".mp4" container.
One word, Muxing, no decoding and no encoding.
And I have no idea how to set pts, dts and duration.
I get the packets with "pcap" library.
I removed headers before compressed video data show up. e.g. Ethernet, VLAN.
I collected data until one frame and decoded it for getting information of data. e.g. width, height. (I am not sure that it is necessary)
I initialized output context, stream and codec context.
I started to receive packets with "pcap" library again. (now for muxing)
I made one frame and put that data in AVPacket structure.
I try to set PTS, DTS and duration. (I think here is wrong part, not sure though)
*7-1. At the first frame, I saved time(msec) with packet header structure.
*7-2. whenever I made one frame, I set parameters like this : PTS(current time - start time), DTS(same PTS value), duration(current PTS - before PTS)
I think it has some error because :
I don't know how far is suitable long for dts from pts.
At least, I think duration means how long time show this frame from now to next frame, so It should have value(next PTS - current PTS), but I can not know the value next PTS at that time.
It has I-frame only.
// make input context for decoding
AVFormatContext *&ic = gInputContext;
ic = avformat_alloc_context();
AVCodec *cd = avcodec_find_decoder(AV_CODEC_ID_H264);
AVStream *st = avformat_new_stream(ic, cd);
AVCodecContext *cc = st->codec;
avcodec_open2(cc, cd, NULL);
// make packet and decode it after collect packets is be one frame
gPacket.stream_index = 0;
gPacket.size = gPacketLength[0];
gPacket.data = gPacketData[0];
gPacket.pts = AV_NOPTS_VALUE;
gPacket.dts = AV_NOPTS_VALUE;
gPacket.flags = AV_PKT_FLAG_KEY;
avcodec_decode_video2(cc, gFrame, &got_picture, &gPacket);
// I checked automatically it initialized after "avcodec_decode_video2"
// put some info that I know that not initialized
cc->time_base.den = 90000;
cc->time_base.num = 1;
cc->bit_rate = 2500000;
cc->gop_size = 1;
// make output context with input context
AVFormatContext *&oc = gOutputContext;
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
AVFormatContext *&ic = gInputContext;
AVStream *ist = ic->streams[0];
AVCodecContext *&icc = ist->codec;
AVStream *ost = avformat_new_stream(oc, icc->codec);
AVCodecContext *occ = ost->codec;
avcodec_copy_context(occ, icc);
occ->flags |= CODEC_FLAG_GLOBAL_HEADER;
avio_open(&(oc->pb), filename, AVIO_FLAG_WRITE);
// repeated part for muxing
AVRational Millisecond = { 1, 1000 };
gPacket.stream_index = 0;
gPacket.data = gPacketData[0];
gPacket.size = gPacketLength[0];
gPacket.pts = av_rescale_rnd(pkthdr->ts.tv_sec * 1000 /
+ pkthdr->ts.tv_usec / 1000 /
- gStartTime, Millisecond.den, ost->time_base.den, /
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
gPacket.dts = gPacket.pts;
gPacket.duration = gPacket.pts - gPrev;
gPacket.flags = AV_PKT_FLAG_KEY;
gPrev = gPacket.pts;
av_interleaved_write_frame(gOutputContext, &gPacket);
Expected and actual results is a .mp4 video file that can play.

Using BASS_StreamCreateFile in WPF

BASS_StreamCreateFile(path,offset,length,BassFlags) always returns '0'. I am not understanding how to use this function. Need help on the usage of BassFlags.
PS : Using this with the help of WPF Sound Visualization Library.
Since 0 only informs you that there's an error, you should check what kind of error it is:
int BASS_ErrorGetCode();
This gives you the errorcode for the recent error.
Here's the list of possible error codes (= return values):
BASS_ERROR_INIT // BASS_Init has not been successfully called.
BASS_ERROR_NOTAVAIL // Only decoding channels (BASS_STREAM_DECODE) are allowed when using the "no sound" device. The BASS_STREAM_AUTOFREE // flag is also unavailable to decoding channels.
BASS_ERROR_ILLPARAM // The length must be specified when streaming from memory.
BASS_ERROR_FILEOPEN // The file could not be opened.
BASS_ERROR_FILEFORM // The file's format is not recognised/supported.
BASS_ERROR_CODEC // The file uses a codec that is not available/supported. This can apply to WAV and AIFF files, and also MP3 files when using the "MP3-free" BASS version.
BASS_ERROR_FORMAT // The sample format is not supported by the device/drivers. If the stream is more than stereo or the BASS_SAMPLE_FLOAT flag is used, it could be that they are not supported.
BASS_ERROR_SPEAKER // The specified SPEAKER flags are invalid. The device/drivers do not support them, they are attempting to assign a stereo stream to a mono speaker or 3D functionality is enabled.
BASS_ERROR_MEM // There is insufficient memory.
BASS_ERROR_NO3D // Could not initialize 3D support.
BASS_ERROR_UNKNOWN // Some other mystery problem!
(from bass.h)
Also make shure you have initialised BASS properly - BASS_Init() must get called before you create a stream:
BOOL BASS_Init(
int device, // The device to use... -1 = default device, 0 = no sound, 1 = first real output device
DWORD freq, // Output sample rate
DWORD flags, // A combination of flags
HWND win, // The application's main window... 0 = the current foreground window (use this for console applications)
GUID *clsid // Class identifier of the object to create, that will be used to initialize DirectSound... NULL = use default
);
Example:
int device = -1; // Default device
int freq = 44100; // Sample rate
BASS_Init(device, freq, 0, 0, NULL); // Init BASS

Resources