Hints for splitting an MP4 file

May 9, 2012 at 4:12 AM

I looked through the code a bit and I figure I should be able to load an MP4 video, split it and save the smaller file out. Any hints on how to split the boxes after they are loaded?

Paul

Coordinator
May 9, 2012 at 5:04 AM

Things are a bit tricky right now as I'm still working on the functionality for editing.  The library is in a great deal of flux as new boxes are being added and the reader/writer interface is being redone.  I haven't added a generic way to output box content other than what was in the source file at the moment.  That means writing out modified versions of things like xml and mdat will be the hardest part.  This is why I haven't released a download for the library itself yet.

If you don't mind editing the library, you can probably trick a new mdat box into associating with the source file with a specified offset and content length.  Then you'll be able to write it out and have it grab its subset of content from the source file.  I should make a .GetChunkedMdats() enumerable or something that does that but it'll have to wait a couple of days.  You could equally easily trick it into pulling its content from a memory stream or something instead of the source file.

A split for 3gpp, smooth streaming, hls, flash or other transport streams will require looking at the stco or co64 box to find the chunk offsets within the mdat and either use the sizes in stsz or calculate the sizes from the start of the next chunk.  Then you'd create your moof structure for each chunk, write that out then write out an mdat with just that chunk's data.  Rinse and repeat.  Depending on the particular transport you may or may not need to write out the ftyp and moov first.  Non-live smooth streaming also needs an mfra box with tfra's for each track and an mfro with the offset of the mfra so the server can quickly locate the start of all the moof boxes.  I think it uses an empty mfra for live to signal the end of the stream but I haven't looked at that in great detail yet.

The contents of the various boxes under moof and any transforms you might need to do to the ftyp and moov boxes will be very application-specific.  You'll probably need to have a look at the specs or existing files.  I've collected most of the relevant specs in a folder in the library project.

If you are trying to do a simple edit by cutting on iframe boundaries, you'd have to edit the tables in the boxes under moov\trak\mdia\minf\stbl to remove the chunks you don't want and use the offsets of the ones you do want to write the contents of a new mdat then update their offsets.  You'll  have to do the mdat entirely manually if the chunks you want to output aren't contiguous as you wouldn't be able to use the trick I outlined above.  It'd probably be easiest to write the moov out last in that case and then faststart it.

Anyway, check out the faststart implementation.  It should give you a basic idea as it outputs the boxes in a different order and updates the offsets along the way.

May 10, 2012 at 5:47 AM

Thanks for replying. I think I will have to do a simple edit on iframes because I don't want to re-encode. I can see that the STSS box (random access points) contains approximately 1 entry per second of recording. Does that mean my iframes are available each second and that's as fine as I can cut?

I would think that doing the cut operation on the boxes should be a recursive call from the MOOV at the top and calling all the way down with appropriate parameters for each child to do the cut. The cut is a trim of the start or end of the file only to keep things simple. Each box that executes the cut operation is responsible for doing the cut on it's own data and then calling it's children.

In working with the boxes in memory, how do you get data from a particular box when the only navigation is through the children collections? I see the length of a video in the duration field of the MVHD box. Is there a helper for finding a particular box or what code would I write to access that?

I haven't yet tried to compile this for Windows Phone which is my target platform so if you know of something that will block that, do let me know.

Paul

Coordinator
May 10, 2012 at 7:18 AM

The GoP (Group of Pictures a.k.a Chunk) length is configurable on the encoder side.  There are iframe-only files but those are typically only used for archiving and editing.  The longer the time between iframes, the better the compression is... up to a point.  Typical values are 1, 2 or 10 seconds.  It's basically a tradeoff between compression efficiency and seekability.  A "smart" player can make it look like you've got finer grained seeking but it actually has to start at the beginning of the GoP and work its way forward to the point you are seeking to even if it doesn't show you those frames as it decodes them.  Also, I think in some formats it's actually up to the encoder to decide whether a given chunk has just one GoP or multiple but in the case of multiple you'd actually have to parse the codec-specific bitstream contained in the mdat... not something I plan to support in this library.  I don't think I've actually come across an encoder that outputs that way, though.

All the data is in the mdat which is a root level box with no information about its own data.  It can contain video, audio, images, timed text, etc.  The moov just contains metadata and offset pointers into the mdat and not all Base Media derived formats use a moov.  Anything you do with the mdat is specific to the various format standards based on Base Media.  Having a cut function there wouldn't make sense.  It could be on the moov but then the moov would have to be able to locate the mdat and I'd rather not have circular references if possible.  Besides, the boxes are a serialization format not a business object and that kind of functionality should be hung elsewhere like the BaseMedia class or the BaseMediaFile class over in BmffViewer.

What I will be doing shortly is making it so you can create an mdat with a given stream, offset and length.  That way you can give it new data or point it at a subset of data in a source file.  Then just update the chunk lists and offsets in the moov and write out the ftyp, moov and mdat.  Once I fully support all features of Base Media, I'll look at creating separate derived file classes for different formats with format-appropriate operations.

The boxes are in a well known structure (most boxes can only be contained in one parent type) depending on the derived format specification so recursive seeking seems a little silly.  I do plan to add more typed navigation properties for known child types but they'll just be shortcuts for LINQ queries since this library needs to support arbitrary extensions and non-standard files.  The plan is to make separate classes (Mp4File, MovFile, 3gppFile, Jp2file, etc...) with specific functions for handling them.  I'll probably also be doing a LINQ to XML style .Descendants() query mechanism to simplify operations over, for instance, all tracks.  BmffReader/Writer is another mechanism I'm working on that'll be forward only, depth-first (file order of the boxes) reading and writing much like XmlReader/XmlWriter.  That'll be good for transport streams but not so great for editing.  

Take a look at BaseMediaFile in the BmffViewer project to see how it's done currently.  The FastStart method in particular shows how to find the right bits, modify things and write things out in a different order.  It's not as easy as I'd like but it works.  Don't worry about the clone I make at the beginning of that method.  I'm just doing that because I don't want to alter what the UI is databound to.  Also, the boxes that implement ISuperBox (any that can contain other boxes) are all directly enumerable now so you don't have to use the .Children collection anymore.  That'll shorten the linq queries a bit.   It'll get better but it's mostly just me at the moment trying to implement a union of 8+ different standards from scratch on my own in my spare time and I've been focused on the lower level functionality so far.

It's a Portable Class Library and WP7 is already one of the target platforms.  I haven't put it on my phone recently but it should run just fine since the portability stuff prevents you from using any BCL calls that aren't on all targetted platforms.   Anything over in BmffViewer is another story, though.  Portable Libraries don't support File, for instance, so all the classes in the library only deal with Stream.  You'll have to make your own derived class like BaseMediaFile with platform-specific methods for opening files or network streams.

Coordinator
May 15, 2012 at 5:00 PM

Ok, as of the latest change set, if you do "new MovieDataBox(sourceStream, contentOffset, contentLength);", add it to a BaseMedia/BaseMediaFile and write that file out, it should write the new 'mdat' with content from the specified range.

I haven't fully tested this scenario but it should let you output a subset of GoPs/Chunks from the source.  Remember you'll still have to edit the ITableBox types under moov\trak\mdia\minf\stbl to reflect both the removed chunks and the new locations of retained chunks.  Offsets in stco/co64 are from the beginning of the file.  The FastStart method shows one way to do the offset updates.  I'll work on a reusable method to do all that and transport stream splitting in the near future.

One thing I'm not certain about is whether or not audio track chunks will always be GoP aligned with the video tracks.  I don't believe the spec requires any particular alignment between different tracks although it may be common practice when the tracks are created together.  If that's the case, I think you need to add a moov\trak\edts and elst sub-box before the moov\trak\mdia box on the audio track to account for a portion of the audio being skipped.  I'm not familiar with how that works, unfortunately, but I'll read up on it for when I implement the cut method.  I'm also not entirely sure all players support this -- maximum compatibility would likely require re-encoding the first and last chunk which is not something this library will ever do on its own.

Sep 11, 2014 at 7:55 AM
i use a video splitter called idealshare videogo to split video into chapters which has both Windows and Mac version.

it can split video with or without re-encoding.

it can split all kinds of video files like MP4, MKV, MOV, WMV, VOB, FLV and etc

it even can split audio mp3, wav, m4a, aac, flac and etc.

but it only divide video into chapters if the original video is divided in chapters or cue based.

recources: http://www.idealshare.net/video-splitter-for-mac-windows.html
Dec 26, 2014 at 1:24 PM
jclary wrote:
Ok, as of the latest change set, if you do "new MovieDataBox(sourceStream, contentOffset, contentLength);", add it to a BaseMedia/BaseMediaFile and write that file out, it should write the new 'mdat' with content from the specified range. I haven't fully tested this scenario but it should let you output a subset of GoPs/Chunks from the source.  Remember you'll still have to edit the ITableBox types under moov\trak\mdia\minf\stbl to reflect both the removed chunks and the new locations of retained chunks.  Offsets in stco/co64 are from the beginning of the file.  The FastStart method shows one way to do the offset updates.  I'll work on a reusable method to do all that and transport stream splitting in the near future. One thing I'm not certain about is whether or not audio track chunks will always be GoP aligned with the video tracks.  I don't believe the spec requires any particular alignment between different tracks although it may be common practice when the tracks are created together.  If that's the case, I think you need to add a moov\trak\edts and elst sub-box before the moov\trak\mdia box on the audio track to account for a portion of the audio being skipped.  I'm not familiar with how that works, unfortunately, but I'll read up on it for when I implement the cut method.  I'm also not entirely sure all players support this -- maximum compatibility would likely require re-encoding the first and last chunk which is not something this library will ever do on its own.
is it done?
Mar 16, 2015 at 11:50 AM
A powerful MP4 Splitter is required if you want to split one or more big MP4 file(s) to smaller ones.

Faasoft MP4 Splitter is one of them. It can split various kinds of video like MOV, AVI, WMV, MKV, FLV, ASF, RM, DV, MPG, WebM, OGV, etc.

Hope it will help you more or less.