Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FFMPEG I/O output buffer

Tags:

c++

c

ffmpeg

I'm currently having issues trying to encapsulate raw H264 nal packets into a mp4 container. Instead of writing them to disk however, I want to have the result stored in memory. I followed this approach Raw H264 frames in mpegts container using libavcodec but haven't been successful so far.

First, is this the right way to write to memory? I have a small struct in my header

struct IOOutput {
    uint8_t* outBuffer;
    int bytesSet;
};

where I initialize the buffer and bytesset. I then initialize my AVIOContext variable

AVIOContext* pIOCtx = avio_alloc_context(pBuffer, iBufSize, 1, outptr, NULL, write_packet, NULL);

where outptr is a void pointer to IOOutput output, and write_packet looks like the following

int write_packet (void *opaque, uint8_t *buf, int buf_size) {
    IOOutput* out = reinterpret_cast<IOOutput*>(opaque);
    memcpy(out->outBuffer+out->bytesSet, buf, buf_size);
    out->bytesSet+=buf_size;
    return buf_size;
}

I then set

fc->pb = pIOCtx;
fc->flags = AVFMT_FLAG_CUSTOM_IO;

on my AVFormatContext *fc variable.

Then, whenever I encode the nal packets I have from a frame, I write them to the AVFormatContext via av_interleaved_write_frame and then get the mp4 contents via

void getBufferContent(char* buffer) {
    memcpy(buffer, output.outBuffer, output.bytesSet);
    output.bytesSet=0;
}

and thus reset the variable bytesSet, so during the next writing operation bytes will be inserted at the start of the buffer. Is there a better way to do this? Is this actually a valid way to do it? Does FFMPEG do any reading operation if I only do call av_interleaved_write_frame and avformat_write_header in order to add packets?

Thank you very much in advance!

EDIT

Here is the code regarding the muxing process - in my encode Function I have something like

int frame_size = x264_encoder_encode(obj->mEncoder, &obj->nals, &obj->i_nals, obj->pic_in, obj->pic_out);
int total_size=0;

for(int i=0; i<obj->i_nals;i++)
    {
        if ( !obj->fc ) {
            obj->create( obj->nals[i].p_payload, obj->nals[i].i_payload );
        } 

        if ( obj->fc ) {
            obj->write_frame( obj->nals[i].p_payload, obj->nals[i].i_payload);
        }
    }

// Here I get the output values
int currentBufferSize = obj->output.bytesSet;
char* mem = new char[currentBufferSize];
obj->getBufferContent(mem);

And the create and write functions look like this

int create(void *p, int len) {

   AVOutputFormat *of = av_guess_format( "mp4", 0, 0 );

   fc = avformat_alloc_context();

   // Add video stream
   AVStream *pst = av_new_stream( fc, 0 );
   vi = pst->index;

   void* outptr = (void*) &output;

// Create Buffer
   pIOCtx = avio_alloc_context(pBuffer, iBufSize, 1, outptr, NULL, write_packet, NULL);

   fc->oformat = of;
   fc->pb = pIOCtx;
   fc->flags = AVFMT_FLAG_CUSTOM_IO;

   pcc = pst->codec;

   AVCodec c= {0};
   c.type= AVMEDIA_TYPE_VIDEO;

   avcodec_get_context_defaults3( pcc, &c );
   pcc->codec_type = AVMEDIA_TYPE_VIDEO;

   pcc->codec_id = codec_id;
   pcc->bit_rate = br;
   pcc->width = w;
   pcc->height = h;
   pcc->time_base.num = 1;
   pcc->time_base.den = fps;
}

void write_frame( const void* p, int len ) {

   AVStream *pst = fc->streams[ vi ];

   // Init packet
   AVPacket pkt;
   av_init_packet( &pkt );
   pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0;   
   pkt.stream_index = pst->index;
   pkt.data = (uint8_t*)p;
   pkt.size = len;

   pkt.dts = AV_NOPTS_VALUE;
   pkt.pts = AV_NOPTS_VALUE;

   av_interleaved_write_frame( fc, &pkt );

}
like image 262
peacer212 Avatar asked Jun 05 '15 15:06

peacer212


1 Answers

See the AVFormatContext.pb documentation. You set it correctly, but you shouldn't touch AVFormatContext.flags. Also, make sure you set it before calling avformat_write_header().

When you say "it doesn't work", what exactly doesn't work? Is the callback not invoked? Is the data in it not of the expected type/format? Something else? If all you want to do is write raw nal packets, then you could just take encoded data directly from the encoder (in the AVPacket), that's the raw nal data. If you use libx264's api directly, it even gives you each nal individually so you don't need to parse it.

like image 150
Ronald S. Bultje Avatar answered Nov 09 '22 01:11

Ronald S. Bultje