Skip to content

Commit

Permalink
[common][fmp4] Added simple demux of mp4
Browse files Browse the repository at this point in the history
  • Loading branch information
aldenml committed Mar 26, 2016
1 parent 70ca41c commit a46f98a
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 4 deletions.
2 changes: 2 additions & 0 deletions common/src/main/java/com/frostwire/fmp4/IsoMedia.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public boolean onBox(Box b) {
}

public static boolean write(OutputChannel ch, LinkedList<Box> boxes, OnBoxListener l, ByteBuffer buf) throws IOException {
buf.clear();

for (Box b : boxes) {
buf.putInt(b.size);
buf.putInt(b.type);
Expand Down
132 changes: 128 additions & 4 deletions common/src/main/java/com/frostwire/fmp4/Mp4Demuxer.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.ListIterator;

/**
* @author gubatron
Expand Down Expand Up @@ -62,7 +63,7 @@ private static void track(int id, boolean fragments, LinkedList<Box> head, Mp4Ta
if (fragments) {
trackFragments(id, head, tags, in, out);
} else {
trackSimple(id);
trackSimple(id, tags, in, out);
}
}

Expand Down Expand Up @@ -216,6 +217,22 @@ public boolean onBox(Box b) {
}
}, buf);

// remove other tracks
MovieBox moov = Box.findFirst(boxes, Box.moov);
ListIterator<Box> it = moov.boxes.listIterator();
while (it.hasNext()) {
Box b = it.next();
if (b.type != Box.trak) {
continue;
}

TrackHeaderBox tkhd = b.findFirst(Box.tkhd);
if (tkhd.trackId() != trackId) {
it.remove();
}
}

// recreate sample tables
SampleTableBox stbl = Box.findFirst(boxes, Box.stbl);
TimeToSampleBox stts = stbl.findFirst(Box.stts);
if (stts != null) {
Expand Down Expand Up @@ -268,8 +285,11 @@ public boolean onBox(Box b) {
FileTypeBox ftyp = Box.findFirst(boxes, Box.ftyp);
ftyp.compatible_brands = tags.compatibleBrands;
}
// remove old udta boxes
for (UserDataBox b : moov.<UserDataBox>find(Box.udta)) {
moov.boxes.remove(b);
}
UserDataBox udta = createUdta(tags);
MovieBox moov = Box.findFirst(boxes, Box.moov);
moov.boxes.add(udta);
}

Expand All @@ -291,8 +311,112 @@ public boolean onBox(Box b) {
}, buf);
}

private static void trackSimple(int id) {
throw new UnsupportedOperationException();
private static void trackSimple(int id, Mp4Tags tags, RandomAccessFile fIn, RandomAccessFile fOut) throws IOException {
int trackId = id;
ByteBuffer buf = ByteBuffer.allocate(10 * 1024);
InputChannel in = new InputChannel(fIn.getChannel());
OutputChannel out = new OutputChannel(fOut.getChannel());

final LinkedList<Box> boxes = new LinkedList<>();

IsoMedia.read(in, fIn.length(), null, new IsoMedia.OnBoxListener() {
@Override
public boolean onBox(Box b) {
if (b.parent == null) {
boxes.add(b);
}
return b.type != Box.mdat;
}
}, buf);

MovieBox moov = Box.findFirst(boxes, Box.moov);
TrackBox trak = null;

// remove other tracks
ListIterator<Box> it = moov.boxes.listIterator();
while (it.hasNext()) {
Box b = it.next();
if (b.type != Box.trak) {
continue;
}

TrackHeaderBox tkhd = b.findFirst(Box.tkhd);
if (tkhd.trackId() != trackId) {
it.remove();
} else {
trak = (TrackBox) b;
}
}

// some fixes
TrackHeaderBox tkhd = Box.findFirst(boxes, Box.tkhd);
tkhd.enabled(true);
tkhd.inMovie(true);
tkhd.inPreview(true);
tkhd.inPoster(true);
MediaHeaderBox mdhd = Box.findFirst(boxes, Box.mdhd);
mdhd.language("eng");

// add tags
if (tags != null) {
if (tags.compatibleBrands != null) {
FileTypeBox ftyp = Box.findFirst(boxes, Box.ftyp);
ftyp.compatible_brands = tags.compatibleBrands;
}
// remove old udta boxes
for (UserDataBox b : moov.<UserDataBox>find(Box.udta)) {
moov.boxes.remove(b);
}
UserDataBox udta = createUdta(tags);
moov.boxes.add(udta);
}

MediaDataBox mdat = Box.findFirst(boxes, Box.mdat);
SampleToChunkBox stsc = trak.findFirst(Box.stsc);
SampleSizeBox stsz = trak.findFirst(Box.stsz);
ChunkOffsetBox stco = trak.findFirst(Box.stco);

int[] chunkSize = new int[stco.entry_count];

int chunkIdx = 0;
int sampleIdx = 0;
for (int i = 0; i < stsc.entry_count; i++) {
int a = stsc.entries[i].first_chunk;
int b = i < stsc.entry_count - 1 ? stsc.entries[i + 1].first_chunk : a + 1;
for (int j = a; j < b; j++) {
int sampleSize = 0;
for (int k = 0; k < stsc.entries[i].samples_per_chunk; k++) {
sampleSize += stsz.sample_size != 0 ? stsz.sample_size : stsz.entries[sampleIdx].entry_size;
sampleIdx++;
}
chunkSize[chunkIdx] += sampleSize;
chunkIdx++;
}
}

int[] chunkOffsetOrg = new int[stco.entry_count];
int offset = (int) (ContainerBox.length(boxes) - mdat.length());
for (int i = 0; i < stco.entry_count; i++) {
chunkOffsetOrg[i] = stco.entries[i].chunk_offset;
stco.entries[i].chunk_offset = offset;
offset += chunkSize[i];
}

IsoMedia.write(out, boxes, new IsoMedia.OnBoxListener() {
@Override
public boolean onBox(Box b) {
return true;
}
}, buf);

for (int i = 0; i < stco.entry_count; i++) {
int pos = (int) in.count();

int skp = chunkOffsetOrg[i] - pos;
IO.skip(in, skp, buf);

IO.copy(in, out, chunkSize[i], buf);
}
}

private static int calcMdatOffset(LinkedList<Box> head, long length, Mp4Tags tags) throws IOException {
Expand Down
48 changes: 48 additions & 0 deletions common/src/test/java/com/frostwire/fmp4/SimpleDemuxTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Created by Angel Leon (@gubatron), Alden Torres (aldenml)
* Copyright (c) 2011-2016, FrostWire(R). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.frostwire.fmp4;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.LinkedList;

/**
* @author gubatron
* @author aldenml
*/
public class SimpleDemuxTest {

@Test
public void testSimpleAudio() throws IOException {
File fIn = new File("/Users/aldenml/Downloads/test.mp4");
File fOut = new File("/Users/aldenml/Downloads/test_out.mp4");

Mp4Tags tags = new Mp4Tags();
tags.compatibleBrands = new int[]{Bits.make4cc("M4A "), Bits.make4cc("mp42"), Bits.make4cc("isom"), Bits.make4cc("\0\0\0\0")};
tags.title = "ti";
tags.author = "au";
tags.source = "sr";
tags.jpg = null;

Mp4Demuxer.audio(fIn, fOut, tags);
}
}

0 comments on commit a46f98a

Please sign in to comment.