7

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 );

}
Community
  • 1
  • 1
peacer212
  • 935
  • 9
  • 15
  • You might be interested in taking a look at this FFmpeg patch: https://ffmpeg.org/pipermail/ffmpeg-devel/2014-November/164996.html Though I can't find the actual "doc/examples/avio_writing.c" in the FFmpeg's git... – Gediminas Sep 15 '15 at 12:12

1 Answers1

5

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.

Ronald S. Bultje
  • 10,828
  • 26
  • 47
  • Thank you very much for your response! So I don't need to set AVFMT_FLAG_CUSTOM_IO manually, correct? So far I don't have success in encoding the nal packets into a mp4 container, and I thought that maybe the data is not written correctly into the buffer - and I didn't know whether this is the way to go, or whether I could use some FFMPEG class such as AVStream to accomplish the task. Right now, I create an AVPacket, initialise it, copy all nals into memory, assign it to the packet, and then use av_interleaved_write_frame. This doesn't seem to work however. – peacer212 Jun 05 '15 at 18:52
  • That should work, so I wonder about your other code. Can you show the code that wraps the raw nals into AVPackets and writes them out? Also, can you show some diagnostics like how often is your write_packet() callback called, and what are typical values for buf_size in these callbacks for given nal lengths? – Ronald S. Bultje Jun 06 '15 at 01:17
  • Hi, I edited the original post and added the muxing part - thank you very much for your help! After a little bit of testing I found that I get the following error: [mp4 @ 0x101076c00] muxer does not support non seekable output! Which means that I need to implement the seek function in avio_alloc_context, correct? How can I do that if I constantly overwrite my buffer? – peacer212 Jun 06 '15 at 08:33
  • I just saw that my callback function (write_packet) is not called upon av_interleaved_write_frame. – peacer212 Jun 06 '15 at 09:44
  • How to implement seeking is something for you to decide, but indeed, .mp4 muxing cannot be done without seeking. For that, you might want to use mpeg ts or so. If you don't care, you can just implement a seek callback that does nothing, but obviously the output .mp4 file will not be valid. – Ronald S. Bultje Jun 06 '15 at 10:37