Muxing AVPackets into mp4 file - c++

I am developing a tool which receives a h.264 live stream from the network (the sender is a hardware encoder), buffers the last x-minutes and creates a video file of the last x-minutes if triggered.
My tool is able to receive the live stream and also to buffer AVPackets the with boost::serialization. Furthermore I am able to load the buffered AVPackets.
But when I try to create/mux a video file (mp4) with the loaded AVPackets there occurs an error in the mp4-file or mp4-header.
I am able to create a mp4-file and also to write Packets to the file with av_interleaved_write_frame(). The mp4-file has also a plausible size. So it seams that the AVPackets were written to the file.
But when I try to open the mp4-file with a player like vlc, the video doesn't play. The player provides an error like: the header isn't correct and doesn't play any frame. But I write the header with the writeHeader() function to the AVFormatContext.
I guess that the reason is that the AVFormatContext isn't correctly initialized but with the example of ffmpeg/libav (muxing-examples) I can't solve the problem.
Has any one a solution (example code) or an other example how to correctly initialize the AVFormatContext?
Thanks for your help.
Olgen
avcodec_init();
avcodec_register_all();
av_register_all();
av_log_set_level(AV_LOG_DEBUG);
char *outputFilename = "/tmp/output.mp4";
// define AVOutputFormat
AVOutputFormat *outputFormat = NULL;
outputFormat = av_guess_format("mp4", NULL, NULL);
if (!outputFormat) {
fprintf(stderr, "Could not find suitable output format\n");
return 1;
}
// define AVFormatContext
AVFormatContext *formatContext = NULL;
// lallocate the output media context
std::cout << "alloc" << std::endl;
formatContext = avformat_alloc_context();
// avformat_alloc_output_context(&formatContext, outputFormat, NULL, NULL);
if (!formatContext) {
fprintf(stderr, "Memory error\n");
return 1;
}
std::cout << "stream" << std::endl;
AVStream * video_st = av_new_stream(formatContext, 0);
AVCodec * codec = NULL;
avcodec_get_context_defaults3(video_st->codec, codec);
video_st->codec->coder_type = AVMEDIA_TYPE_VIDEO;
video_st->codec->flags = fc->streams[0]->codec->flags;
video_st->codec->sample_aspect_ratio = fc->streams[0]->codec->sample_aspect_ratio;
video_st->disposition = fc->streams[0]->disposition;
video_st->codec->codec_tag = fc->streams[0]->codec->codec_tag;
video_st->codec->bits_per_raw_sample = fc->streams[0]->codec->bits_per_raw_sample;
video_st->codec->chroma_sample_location = fc->streams[0]->codec->chroma_sample_location;
video_st->codec->codec_id = fc->streams[0]->codec->codec_id;
video_st->codec->codec_tag = fc->streams[0]->codec->codec_tag;
video_st->codec->time_base = fc->streams[0]->codec->time_base;
video_st->codec->extradata = fc->streams[0]->codec->extradata;
video_st->codec->extradata_size = fc->streams[0]->codec->extradata_size;
video_st->codec->pix_fmt = fc->streams[0]->codec->pix_fmt;
video_st->codec->width = fc->streams[0]->codec->width;
video_st->codec->height = fc->streams[0]->codec->height;
video_st->codec->sample_aspect_ratio = fc->streams[0]->codec->sample_aspect_ratio;
std::cout << "file" << std::endl;
avio_open(&formatContext->pb, outputFilename, AVIO_FLAG_WRITE);
std::cout << "header" << std::endl;
avformat_write_header(formatContext, NULL);
// av_write_header(formatContext);
// loop to write AVPackets with av_write_frame
The programm crashes after the avformat_write_header(formatContext, NULL) with a memory error. I also tried avformat_alloc_output_context() instead of avformat_alloc_output_context2() but both functions don't work. So I used avformat_alloc_context() (see above)
I think there is an alloction problem but why I can't use avformat_alloc_output_context2 or avformat_alloc_output_context. Could somebody figure out my problem here?

This is a general scheme shows how to mux video file from an existing packets
AVOutputFormat * outFmt = av_guess_format("mp4", NULL, NULL);
AVFormatContext *outFmtCtx = NULL;
avformat_alloc_output_context2(&outFmtCtx, outFmt, NULL, NULL);
AVStream * outStrm = av_new_stream(outFmtCtx, 0);
AVCodec * codec = NULL;
avcodec_get_context_defaults3(outStrm->codec, codec);
outStrm->codec->coder_type = AVMEDIA_TYPE_VIDEO;
///....
/// set some required value, such as
/// outStrm->codec->flags
/// outStrm->codec->sample_aspect_ratio
/// outStrm->disposition
/// outStrm->codec->codec_tag
/// outStrm->codec->bits_per_raw_sample
/// outStrm->codec->chroma_sample_location
/// outStrm->codec->codec_id
/// outStrm->codec->codec_tag
/// outStrm->codec->time_base
/// outStrm->codec->extradata
/// outStrm->codec->extradata_size
/// outStrm->codec->pix_fmt
/// outStrm->codec->width
/// outStrm->codec->height
/// outStrm->codec->sample_aspect_ratio
/// see ffmpeg.c for details
avio_open(&outFmtCtx->pb, outputFileName, AVIO_FLAG_WRITE);
avformat_write_header(outFmtCtx, NULL);
for (...)
{
av_write_frame(outFmtCtx, &pkt);
}
av_write_trailer(outFmtCtx);
avio_close(outFmtCtx->pb);
avformat_free_context(outFmtCtx);
For troubleshooting, it is useful to set detailed logging: av_log_set_level(AV_LOG_DEBUG);

This might not be the case, but avformat_write_header function should be used for writing header instead of function pointer in AVOutputFormat.write_header

Related

Live555 truncates encoded data of FFMpeg

I am trying to stream H264 based data using Live555 over RTSP.
I am capturing data using V4L2, and then encodes it using FFMPEG and then passing data to Live555's DeviceSource file, in that I using H264VideoStreamFramer class,
Below is my codec settings to configure AVCodecContext of encoder,
codec = avcodec_find_encoder_by_name(CODEC_NAME);
if (!codec) {
cerr << "Codec " << codec_name << " not found\n";
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
cerr << "Could not allocate video codec context\n";
exit(1);
}
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = PIC_HEIGHT;
c->height = PIC_WIDTH;
/* frames per second */
c->time_base = (AVRational){1, FPS};
c->framerate = (AVRational){FPS, 1};
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
c->rtp_payload_size = 30000;
if (codec->id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "fast", 0);
av_opt_set_int(c->priv_data, "slice-max-size", 30000, 0);
/* open it */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
cerr << "Could not open codec\n";
exit(1);
}
And I am getting encoded data using avcodec_receive_packet() function. which will return AVPacket.
And I am passing AVPacket's data into DeviceSource file below is code snippet of my Live555 code:
void DeviceSource::deliverFrame() {
if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet
u_int8_t* newFrameDataStart = (u_int8_t*) pkt->data;
unsigned newFrameSize = pkt->size; //%%% TO BE WRITTEN %%%
// Deliver the data here:
if (newFrameSize > fMaxSize) { // Condition becomes true many times
fFrameSize = fMaxSize;
fNumTruncatedBytes = newFrameSize - fMaxSize;
} else {
fFrameSize = newFrameSize;
}
gettimeofday(&fPresentationTime, NULL); // If you have a more accurate time - e.g., from an encoder - then use that instead.
// If the device is *not* a 'live source' (e.g., it comes instead from a file or buffer), then set "fDurationInMicroseconds" here.
memmove(fTo, newFrameDataStart, fFrameSize);
}
But here, sometimes my packet's size is getting more than fMaxSize value and as per LIVE555 logic it will truncate frame data, so that sometimes I am getting bad frames on my VLC,
From Live555 forum, I get to know that encoder should not send packet whose size is more than fMaxSize value, so my question is:
How to restrict encoder to limit size of packet?
Thanks in Advance,
Harshil
You can increase the maximum allowed sample size by changing "maxSize" in the OutPacketBuffer class in MediaSink.cpp. This worked for me. There are cases we may require high-quality video to be streamed, I don't think we will always be able to restrict the encoder to not to produce samples of size more than a particular value which would result in video quality issues. In fact, the samples are fragmented by the UDP sink live555 to match the default MTU (1500), so increasing the max sample size limit has no side effects.

FFMPEG: Decode video in h264rgb/libx264rgb error

I did a small program to encode raw images in h264rgb codec with ffmpeg.
I use this codec because I needed to encode lossless rgb images (not possible with the classic h264 codec).
But now, I have a problem. I'm not able to decode the video generated with ffmpeg. I did a second small program for that, but I get a segfault when I reach the avcodec_decode_video2() function.
I did all the initialisation correctly. I didn't forget the avcodec_register_all() and av_init_packet() functions.
Here is the code for initialisation:
_c = NULL;
_frame_nb = 0;
// Register all formats and codecs
#pragma omp critical
{
avcodec_register_all();
}
_pkt = new AVPacket;
av_init_packet(_pkt); // a defaut de pouvoir ecrire : pkt = av_packet_alloc();
if(!_pkt)
exit(1);
_codec = avcodec_find_encoder_by_name("libx264rgb");
if (!_codec) {
fprintf(stderr, "codec not found\n");
exit(1);
}
_c = avcodec_alloc_context3(_codec);
if (!_c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
_c->debug = true;
_c->pix_fmt = (AVPixelFormat)AV_PIX_FMT_RGB24;
_c->width = this->_headerCam[this->_currentCam]->iNbCol;
_c->height = this->_headerCam[this->_currentCam]->iNbLine;
_picture = av_frame_alloc();
if (!_picture) {
fprintf(stderr, "Could not allocate video _picture\n");
exit(1);
}
_tmp_picture = av_frame_alloc();
if (!_tmp_picture) {
fprintf(stderr, "Could not allocate video _tmp_picture\n");
exit(1);
}
_tmp_picture->format = (AVPixelFormat)AV_PIX_FMT_RGB24;
_tmp_picture->width = this->_headerCam[this->_currentCam]->iNbCol;
_tmp_picture->height = this->_headerCam[this->_currentCam]->iNbLine;
_tmp_picture->linesize[0] = this->_headerCam[this->_currentCam]->iNbCol;
/* open it */
if (avcodec_open2(_c, _codec, NULL) < 0) {
fprintf(stderr, "could not open codec\n");
exit(1);
}
And the decode function:
_pkt->data = NULL; // packet data will be allocated by the encoder
_pkt->size = 0;
unsigned char * inbuf;
inbuf = (uint8_t*)av_malloc(w*h*3);
//! read img size
int size_img;
fread(&size_img, sizeof(int), 1, this->_pFile);
_pkt->size = fread(inbuf, 1, size_img, this->_pFile);
_pkt->data = (unsigned char*)inbuf;
if(_pkt->size)
{
len = avcodec_decode_video2(_c, _tmp_picture, &got_picture, _pkt);
...
}
Any idea?
+What the comment says, instead of inbuf = (uint8_t*)av_malloc(w*h*3); you should use this:
int buffer_size = av_image_get_buffer_size((AVPixelFormat)AV_PIX_FMT_RGB24, w, h, 1);
inbuf = (uint8_t*)av_malloc(buffer_size);
Because of portability for alignment and such issues.
And also fix this line _tmp_picture->linesize[0] = ...
with this:
_tmp_picture->linesize[0] = av_image_get_linesize((AVPixelFormat)AV_PIX_FMT_RGB24, w, 0);
Hope that helps.
Well, I finally found a beginning of answer:
I used avcodec_find_encoder_by_name() instead of the avcodec_find_decoder_by_name()... It's certainly better if I want to decode data...
But now, I have the problem that _codec = avcodec_find_decoder_by_name("libx264rgb"); doesn't find the codec. But it works for _codec = avcodec_find_encoder_by_name("libx264rgb"); when I encoded my video.
EDIT : With _codec = avcodec_find_decoder(AV_CODEC_ID_H264); instead of _codec = avcodec_find_decoder_by_name("libx264rgb");, it is able to decode my video. It's weird but ok. Now, I hope I get lossless images.
EDIT2: Now, everything works! I got lossless decoded images with the AV_CODEC_ID_H264 decoder. To summarize, if you want to decode a video encoded with libx264rgb, you can use the classic AV_CODEC_ID_H264 codec.

Using libavformat to mux H.264 frames into RTP

I have an encoder that produces a series of H.264 I-frames and P-frames. I'm trying to use libavformat to mux and transmit these frames over RTP, but I'm stuck.
My program sends RTP data, but the RTP timestamp increments by 1 each successive frame, instead of 90000/fps. It also doesn't look like it's doing the proper framing for H.264 NAL, since I can't decode the stream as H.264 in Wireshark.
I suspect that I'm not setting up the codec information properly, but it appears in many places in the output format context, so it's unclear what exactly needs to be setup. The examples seem to all copy codec context info from encoders, which isn't my use case.
This is what I'm trying:
int main() {
AVFormatContext context = avformat_alloc_context();
if (!context) {
printf("avformat_alloc_context failed\n");
return;
}
AVOutputFormat *format = av_guess_format("rtp", NULL, NULL);
if (!format) {
printf("av_guess_format failed\n");
return;
}
context->oformat = format;
snprintf(context->filename, sizeof(context->filename), "rtp://%s:%d", "192.168.2.16", 10000);
if (avio_open(&(context->pb), context->filename, AVIO_FLAG_READ_WRITE) < 0) {
printf("avio_open failed\n");
return;
}
stream = avformat_new_stream(context, NULL);
if (!stream) {
printf("avformat_new_stream failed\n");
return;
}
stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
stream->codecpar->codec_id = AV_CODEC_ID_H264;
stream->codecpar->width = 1920;
stream->codecpar->height = 1080;
avformat_write_header(context, NULL);
...
write packets
...
}
Example write packet:
int write_packet(uint8_t *data, int size) {
AVPacket p;
av_init_packet(&p);
p.data = buffer;
p.size = size;
p.stream_index = stream->index;
av_interleaved_write_frame(context, &p);
}
I've even went so far to build in libx264, find the encoder, and copy the codec context info from there into the stream codecpar, with the same result. My goal is to build without libx264, and any other libs that aren't required, but it isn't clear whether libx264 is required for defaults such as time base.
How can the libavformat RTP muxer be initialized to properly send H.264 frames over RTCP+RTP?

(FFMPEG) avformat_write_header crashes (MSVC2013) (C++) (Qt)

I just downloaded FFMPEG and now I'm trying to use it in Qt with MSVC2013 compiler.
To understand how it works, I started reading the documentation and the API.
According to this figure, I was trying to make a little test with libavformat.
I did all they said in the demuxing module, then the muxing module. But, my program crashes when I call the avformat_write_header() function.
I would like to know what I did wrong and if you could help me to understand that.
In the main:
av_register_all();
if(!decode())
return;
The decode() methode:
bool MainWindow::decode()
{
AVFormatContext *formatContext = NULL;
AVPacket packet;
/**************** muxing varaiables ******************/
AVFormatContext *muxingContext = avformat_alloc_context();
AVOutputFormat *outputFormat = NULL;
AVIOContext *contextIO = NULL;
AVCodec *codecEncode = avcodec_find_encoder(AV_CODEC_ID_WMAV2);
AVStream *avStream = NULL;
AVCodecContext *codecContext = NULL;
/******************* demuxing **************************/
//open a media file
if(avformat_open_input(&formatContext,"h.mp3",NULL,NULL)!=0)
{
qDebug() << "paka ouve fichier";
return false;
}
//function which tries to read and decode a few frames to find missing
information.
if(avformat_find_stream_info(formatContext,NULL)<0)
{
qDebug()<<"paka find stream";
return false;
}
/**************** muxing *************************/
//The oformat field must be set to select the muxer that will be used.
muxingContext->oformat = outputFormat;
//Unless the format is of the AVFMT_NOFILE type, the pb field must be set to
//an opened IO context, either returned from avio_open2() or a custom one.
if(avio_open2(&contextIO,"out.wma",AVIO_FLAG_WRITE,NULL,NULL)<0)
{
qDebug() <<"paka kreye fichier soti";
return false;
}
muxingContext->pb = contextIO;
//Unless the format is of the AVFMT_NOSTREAMS type, at least
//one stream must be created with the avformat_new_stream() function.
avStream = avformat_new_stream(muxingContext,codecEncode);
//The caller should fill the stream codec context information,
//such as the codec type, id and other parameters
//(e.g. width / height, the pixel or sample format, etc.) as known
codecContext = avStream->codec;
codecContext->codec_type = AVMEDIA_TYPE_AUDIO;
codecContext->codec_id = AV_CODEC_ID_WMAV2;
codecContext->sample_fmt = codecEncode->sample_fmts[0];
codecContext->bit_rate = 128000;
codecContext->sample_rate = 44000;
codecContext->channels = 2;
//The stream timebase should be set to the timebase that the caller desires
//to use for this stream (note that the timebase actually used by the muxer
//can be different, as will be described later).
avStream->time_base = formatContext->streams[0]->time_base;
qDebug()<<formatContext->streams[0]->time_base.num <<"/"
<<formatContext- >streams[0]->time_base.den;
//When the muxing context is fully set up, the caller must call
//avformat_write_header()
//to initialize the muxer internals and write the file header
qDebug() << "does not crash yet";
if(avformat_write_header(muxingContext,NULL) <0)
{
qDebug()<<"cannot write header";
return false;
}
qDebug() << "OOps you can't see me (John Cena)";
///////////////////// Reading from an opened file //////////////////////////
while(av_read_frame(formatContext,&packet)==0)
{
//The data is then sent to the muxer by repeatedly calling
//av_write_frame() or av_interleaved_write_frame()
if(av_write_frame(muxingContext,&packet)<0)
qDebug()<<"paka write frame";
else
qDebug()<<"writing";
}
//Once all the data has been written, the caller must call
//av_write_trailer() to flush any buffered packets and finalize
//the output file, then close the IO context (if any) and finally
//free the muxing context with avformat_free_context().
if(av_write_trailer(muxingContext)!=0)
{
qDebug()<<"paka ekri trailer";
return false;
}
return true;
}
The program shows the message does not crash yet. But not OOps you can't see me (John Cena)
And there is no error. I used an MP3 file as input and I would like to ouput it in WMA.
Instead of avformat_alloc_context(), use avformat_alloc_output_context2(). This will set muxingContext->oformat.

Segmentation fault while avcodec_encode_video2

I have some problems while trying to encode a AVFrame to a packet.
Before reading the whole code, the input stuff is working, I tested it. The output stuff is from an example here. I think there is the problem. But the segmentation fault occurs in the loop near the end.
Here is my reduced code:
void nmain() {
// input stuff
AVFormatContext *formatCtxIn=0;
AVInputFormat *formatIn=0;
AVCodecContext *codecCtxIn=0;
AVCodec *codecIn;
AVPacket *pktIn;
av_register_all();
avdevice_register_all();
avcodec_register_all();
formatIn = av_find_input_format("dshow");
if(!formatIn)
return;
AVDictionary *avoption=0;
av_dict_set(&avoption, "rtbufsize", "1000000000", NULL);
if(avformat_open_input(&formatCtxIn, "video=Integrated Camera", formatIn, &avoption)!=0)
return;
if(avformat_find_stream_info(formatCtxIn, NULL)<0)
return;
codecCtxIn = formatCtxIn->streams[0]->codec;
codecIn = avcodec_find_decoder(codecCtxIn->codec_id);
if(avcodec_open2(codecCtxIn, codecIn, NULL)<0)
return;
// end input stuff
//------------------------------------------------------------------------------
// output stuff
AVOutputFormat *formatOut=0;
AVFormatContext *formatCtxOut=0;
AVStream *streamOut=0;
AVFrame *frame=0;
AVCodec *codecOut=0;
AVPacket *pktOut;
const char *filename = "test.mpeg";
formatOut = av_guess_format(NULL, filename, NULL);
if(!formatOut)
formatOut = av_guess_format("mpeg", NULL, NULL);
if(!formatOut)
return;
formatCtxOut = avformat_alloc_context();
if(!formatCtxOut)
return;
formatCtxOut->oformat = formatOut;
sprintf(formatCtxOut->filename, "%s", filename);
if(formatOut->video_codec != AV_CODEC_ID_NONE) {
AVCodecContext *ctx;
codecOut = avcodec_find_encoder(formatOut->video_codec);
if(!codecOut)
return;
streamOut = avformat_new_stream(formatCtxOut, codecOut);
if(!streamOut)
return;
ctx = streamOut->codec;
ctx->bit_rate = 400000;
ctx->width = 352;
ctx->height = 288;
ctx->time_base.den = 25;
ctx->time_base.num = 1;
ctx->gop_size = 12;
ctx->pix_fmt = AV_PIX_FMT_YUV420P;
if(ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
ctx->max_b_frames = 2;
if(ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
ctx->mb_decision = 2;
if(formatCtxOut->oformat->flags & AVFMT_GLOBALHEADER)
ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
if(streamOut) {
AVCodecContext *ctx;
ctx = streamOut->codec;
if(avcodec_open2(ctx, codecOut, NULL) < 0)
return;
}
if(!(formatCtxOut->flags & AVFMT_NOFILE))
if(avio_open(&formatCtxOut->pb, filename, AVIO_FLAG_WRITE) < 0)
return;
avformat_write_header(formatCtxOut, NULL);
// doit
pktIn = new AVPacket;
pktOut = new AVPacket;
av_init_packet(pktOut);
pktOut->data = 0;
frame = avcodec_alloc_frame();
if(!frame)
return;
for(;;) {
if(av_read_frame(formatCtxIn, pktIn) >= 0) {
av_dup_packet(pktIn);
int fff;
if(avcodec_decode_video2(codecCtxIn, frame, &fff, pktIn) < 0)
std::cout << "bad frame" << std::endl;
if(!fff)
return; // ok
static int counter=0;
SaveFrame(frame, codecCtxIn->width, codecCtxIn->height, counter++); // work fine
// here a segmentation fault is occured.
if(avcodec_encode_video2(streamOut->codec, pktOut, frame, &fff) < 0)
std::cout << "bad frame" << std::endl;
}
}
}
// only for testing
// add to ensure frame is valid
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
What am i doing wrong?
While debugging i didn't found any problems with the parameters. streamOut->codec is filled. pktOut is allocated and frame is filled with the picture encoded before.
I think the problem is while creating the output codec but watching the example and looking to the doxypages it seems to be correct.
The trace route is from QT using msvc11 and framework 5.
I also tried to run with dr. memory and get this:
Error #26: UNADDRESSABLE ACCESS: reading 0x00000000-0x00000004 4 byte(s)
# 0 replace_memcpy [d:\derek\drmemory\withwiki\trunk\drmemory\replace.c:203]
# 1 avcodec-54.dll!ff_dct_quantize_c +0xd463 (0x6a482364 <avcodec-54.dll+0x3c2364>)
# 2 avcodec-54.dll!avcodec_encode_video2+0xb7 (0x6a56a5b8 <avcodec-54.dll+0x4aa5b8>)
# 3 nmain [d:\prg\tests\recording system-qt\libav\recsys\main.cpp:610]
# 4 main [d:\prg\tests\recording system-qt\libav\recsys\main.cpp:182]
Note: #0:00:06.318 in thread 5312
Note: instruction: mov (%edx) -> %ebx
It seems like the reading process while memcpy is going wrong.
Version:
I've forgot to mention the version of libav/ffmpeg i'm using:
libavutil 51. 76.100 / 51. 76.100
libavcodec 54. 67.100 / 54. 67.100
libavformat 54. 33.100 / 54. 33.100
libavdevice 54. 3.100 / 54. 3.100
libavfilter 3. 19.103 / 3. 19.103
libswscale 2. 1.101 / 2. 1.101
libswresample 0. 16.100 / 0. 16.100
libpostproc 52. 1.100 / 52. 1.100
Addendum:
Function SafeFrame is copied from tutorial 1.
Finally i solved my problem.
The problem is (apart from the documentation of libav) avpacket is not a (real) copy of the picture in the packet. it just points to the data of the packet. You have to make a copy, or better you have to let it libav do.
So first i created a new avframe for the output and a buffer on which the output avframe is pointing to.
AVFrame *outpic = avcodec_alloc_frame();
nbytes = avpicture_get_size(codecCtxOut->pix_fmt, codecCtxOut->width, codecCtxOut->height);
uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes);
This buffer is used for the conversion from input to output. Then in the loop i have to fillup the outpic (avframe) with the buffer.
I have found in the code that this function is filling up the plane pointers with the buffer.
see here
avpicture_fill((AVPicture*)outpic, outbuffer, AV_PIX_FMT_YUV420P, codecCtxOut->width, codecCtxOut->height);
Then i converted the inpic to outpic using sws_scale. But first you have to setup the swscontext.
SwsContext* swsCtx_ = sws_getContext(codecCtxIn->width, codecCtxIn->height,
codecCtxIn->pix_fmt,
codecCtxOut->width, codecCtxOut->height,
codecCtxOut->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(swsCtx_, inpic->data, inpic->linesize, 0, codecCtxIn->height, outpic->data, outpic->linesize);
Then you can encode the outpic into pktout (avpacket for output). But first do free the output packet, otherwise you will get an error and a leak... see here
av_free_packet(pktOut);
if(avcodec_encode_video2(streamOut->codec, pktOut, outpic, &fff) < 0) {
std::cout << "shit frame" << std::endl;
continue;
}
// and write it to the file
formatOut->write_packet(formatCtxOut, pktOut);
So now it works (nearly fine) for me. Still a small memory leak, but this i can spot later.
I see at least two problems with that transcoding loop:
1) You're not checking whether the decoder produced a frame. Many decoders have a delay between input and output, so a decode call won't necessarily produce a frame even if no error occurs. You just have to keep passing packets to the decoder until it starts returning frames (and then flush it with NULL packets at the end, as described in the documentation).
The result is that you're passing an uninitialized frame to the encoder, which is probably the reason for the crash.
2) Another problem I see is that you're initing the ouput packet only once. As the documentation says
The user can supply an output buffer by setting avpkt->data and avpkt->size prior to calling the function, but if the size of the user-provided data is not large enough, encoding will fail. All other AVPacket fields will be reset by the encoder using av_init_packet(). If avpkt->data is NULL, the encoder will allocate it. The encoder will set avpkt->size to the size of the output packet. The returned data (if any) belongs to the caller, he is responsible for freeing it.
So if you only initialize it once before starting the transcode loop, on each iteration after the first it will contain the old data. The encoder will think you want to use that buffer for encoding and overwrite it. This will end in tears if you've already passed that packet to a muxer or something like that. So make sure to init packet data and size to NULL/0 before each encode call.