My goal is to stream out mpegts. As input, I take an mp4 file stream. That is, video producer writes mp4 file into a stream that I try to work with. The video may be anywhere from one minute to ten minutes. Because producer writes bytes into stream, the originally written mp4 header is not complete (first 32 bytes prior to ftyp are 0x00 because it doesn't know yet various offsets... which are written post-recording, I think):
This is how the header of typical mp4 looks like:
00 00 00 18 66 74 79 70 69 73 6f 6d 00 00 00 00 ....ftypisom....
69 73 6f 6d 33 67 70 34 00 01 bb 8c 6d 64 61 74 isom3gp4..»Œmdat
This is how the header of "in progress" mp4 looks like:
00 00 00 00 66 74 79 70 69 73 6f 6d 00 00 00 00 ....ftypisom....
69 73 6f 6d 33 67 70 34 00 00 00 18 3f 3f 3f 3f isom3gp4....????
6d 64 61 74 mdat
It is my guess but I assume that once the producer completes recording, it updates the header by writing all the necessary offsets.
I have run into two issues while trying to make this work:
- I created a custom AVIO with read function that does not support seeking. In my driver program, I decided to stream in a properly formatted mp4 file. I am able to detect its input format. When I try to open it, I see that my custom read function gets executed within avformat_open_input until the entire file is read in.
My code sample:
av_register_all();
AVFormatContext* pCtx = avformat_alloc_context();
pCtx->pb = avio_alloc_context(
pBuffer, // internal buffer
iBufSize, // internal buffer size
0, // bWriteable (1=true,0=false)
stream, // user data ; will be passed to our callback functions
read_stream, // read callback function
NULL, // write callback function (not used in this example)
NULL // seek callback function
);
pCtx->pb->seekable = 0;
pCtx->pb->write_flag = 0;
pCtx->iformat = av_find_input_format( "mp4" );
pCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
avformat_open_input( &pCtx, "", pCtx->iformat, NULL );
Obviously, this does not work as I need (I expectations were wrong). Once I substitute the file of finite size by a stream of varies length, I cannot have avformat_open_input wait around for the stream to finish before attempting to do further processing.
As such, I need to find a way to open input without attempt to read it and only read when I execute av_read_frame. Is this at all possible to do by using custom AVIO. That is, prepare/open input -> read initial input data into input buffer -> read frame/packet from input buffer -> write packet to output -> repeat read input data until the end of stream.
I did scavenge google and only saw two alternatives: providing custom URLProtocol and using AVFMT_NOFILE.
Custom URLProtocol
This sounds like a little backwards way for what I'm trying to accomplish. I understand that it is best used when there is a file source available. Whereas I am trying to read from a byte stream. Also, another reason I think it doesn't fit my needs is that custom URLProtocol needs to be compiled into ffmpeg lib, correct? Or is there a way to manually register it during runtime?
AVFMT NOFILE
This seems like something that should actually work best for me. The flag itself says that there is no underlying source file and assumes that I will handle all the reading and provisioning of input data. The trouble is that I haven't seen any online code snippets so far but my assumption is as follows:
I am really hoping to get some suggestions of food for brain from anyone because I am a newbie to ffmpeg and digital media and my second issue expects that I can stream output while ingesting input.
As I mentioned above, I have a handle on the mp4 file bytestream as it would be written to the hard disk. The format is mp4 (h.264 and aac). I need to remux it to mpegts prior to streaming it out. This shouldn't be difficult because mp4 and mpegts are simply containers. From what I learned so far, mp4 file looks the following:
[header info containing format versions]
mdat
[stream data, in my case h.264 and aac streams]
[some trailer separator]
[trailer data]
If that is correct, I should be able to get the handle on h.264 and aac interleaved data by simply starting to read the stream after "mdat" identifier, correct?
If that is true and I decide to go with AVFMT_NOFILE approach of managing input data, I can just ingest stream data (into AVFormatContext buffer) -> av_read_frame -> process it -> populate AVFormatContext with more data -> av_read_frame -> and so on until the end of stream.
I know, this is a mouthful and a dump of my thoughts but I would appreciate any discussion, pointers, thoughts!