4

Im attempting to rotate a video that is in landscape into portrait using MP4Parser (or any other method if you know of one) currently playing with the TrackHeaderBox but unable to get the orientation to change at all, has anyone used this before that can spot the mistake I may of made? any help will go a long way thanks

IsoFile out = new DefaultMp4Builder().build(result);

        // test

        double[] m = null;
        m = new double[] { 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0 };

        TrackBox tb = out.getMovieBox().getBoxes(TrackBox.class).get(0);
        TrackHeaderBox box = tb.getTrackHeaderBox();
        box.setMatrix(m);
Edmund Rojas
  • 6,376
  • 16
  • 61
  • 92

2 Answers2

1

Embedding Information about Rotation in the MP4

Are you really changing the orientation of the video track? If you change the orientation of the audio track you will not see any change.

From my experience it's easier to change the orientation of the whole file (1.0.4.2 API Version):

    Movie result = MovieCreator.build("input.mp4");
    // do something with the file
    Container out = new DefaultMp4Builder().build(result);
    MovieHeaderBox mvhd = Path.getPath(out, "moov/mvhd");
    mvhd.setMatrix(Matrix.ROTATE_180);
    out.writeContainer(new FileOutputStream("result.mp4").getChannel());

alternatively if you want to change the orientation directly without going via a Movie object:

    IsoFile isoFile = new IsoFile("video.mp4");
    MovieHeaderBox mvhd = Path.getPath(isoFile, "/moov/mvhd");
    mvhd.setMatrix(Matrix.ROTATE_180);
    isoFile.writeContainer(new FileOutputStream("result.mp4").getChannel());

The file result.mp4 is now rotated by 180 degrees as you can verify by playing back the file in a desktop player such as QuickTime or VLC.

Typical Problems on Android

When you playback the video on Android with the help of the VideoView you might notice that the matrix is not taken into account. I'm not entirely sure if this is done on purpose or not but the workaround is to use a TextureView that applies the transformation.

In order to do so you have to

  • extract the matrices from the MovieHeaderBox at /moov/mvhd and from MediaHeaderBox at /moov/trak[0, 1, 2, 3]/tkhd (depending on which trak contains the video).
  • Combine both matrices via matrix multiplication.
  • CallsetScaleX, setScaleY, setPivotX,setPivotY and setRotation with values according to the resulting matrix from the step before.
Sebastian Annies
  • 2,438
  • 1
  • 20
  • 38
  • Thank you so much for this answer! I have one other question though, seems like when I append the files the audio also gets really out of sync, is there a known way to fix this? seen alot of people with similar issues but none can find a solution, thanks again for your help! – Edmund Rojas Jul 01 '13 at 01:00
  • In many case the audio or video recordings are not of equals length. In case the audio is shorter than the video the next track's audio will start earlier. You might want to append silence after each audio: https://code.google.com/p/mp4parser/source/browse/trunk/examples/src/main/java/com/googlecode/mp4parser/SilencePrepender.java – Sebastian Annies Jul 01 '13 at 06:57
  • @SebastianAnnies Your code works fine and Android video player plays videos in the correct orientation. But the field "Rotation" does not exist in the video information obtained in the program "mediainfo". Can you say why? – Mikalai Daronin Jul 11 '13 at 14:40
  • sorry, I have no clue. Not sure which tool is able to read the tranformation matrix. – Sebastian Annies Jul 14 '13 at 08:08
  • @Sebastian Annies isoparser-1.0 RC-24 jar file is not available – Ajay Nov 21 '14 at 11:46
  • Please use a later version. 1.0.4.2 is available at maven central. – Sebastian Annies Nov 21 '14 at 11:47
  • when i am using later version,problem with this method track.getDecodingTimeEntries() it is not available.In My app trimming the video concept is very important.when i trim the landscape video it is very pretty good.But If I trim the portrait video,the rotation of the video changes to 90degrees and the orientation also changes to landscape. – Ajay Nov 21 '14 at 11:54
  • Use long[] getSampleDurations(); instead. It returns the same values but uncompressed – Sebastian Annies Nov 21 '14 at 11:58
  • for (int i = 0; i < track.getSampleDurations().size(); i++) { TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i); for (int j = 0; j < entry.getCount(); j++) { – Ajay Nov 21 '14 at 12:08
  • i want return type of Entry in TimeToSampleBox class.so how can I do that – Ajay Nov 21 '14 at 12:09
  • You can imagine each array element as an entry with getCount () == 1 – Sebastian Annies Nov 21 '14 at 12:10
  • it is not working how to do that.How do i pass an array element to the Entry . – Ajay Nov 21 '14 at 12:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65367/discussion-between-user3069551-and-sebastian-annies). – Ajay Nov 21 '14 at 12:42
  • Container out = new DefaultMp4Builder().build(movie);but how to call setMatrix() to the Container.There is no ISOFILE return type for DefaultBulider().bulid(movie) in 27.jar.What can i do now? – Ajay Nov 21 '14 at 13:23
  • hai sebastain in latest jar file return type for DefaulitMp4Bulider().bulid(result) is container but how can i get the reteun type of ISOFILE.Please help me i am stuck here. – Ajay Nov 22 '14 at 06:20
  • hai sebastain I get the latest jar file.Rotate the video by using following code MovieHeaderBox mvhd = Path.getPath(out, "moov/mvhd"); // mvhd.setMatrix(com.googlecode.mp4parser.util.Matrix.ROTATE_90);But the content is transfer in to the path.please help me – Ajay Nov 22 '14 at 21:57
1

The proposed solution by @Sebastian is only respected by a few media players. Secondly tools like Exiftool and MediaInfo does not correctly parse this.

The post here by a mp4Parser maintainer hints that one should use '/moov/trak/tkhd' instead of '/moov/mvhd'

The example below will make files with correct rotation.

        IsoFile isoFile = new IsoFile(srcVideo.getAbsolutePath());
        FileOutputStream fileOutputStream = new FileOutputStream(destVideo.getAbsolutePath());
        FileChannel channel = fileOutputStream.getChannel()

        TrackHeaderBox thb = Path.getPath(isoFile, "/moov/trak/tkhd");
        thb.setMatrix(Matrix.ROTATE_90);
        isoFile.writeContainer(channel);
tobalr
  • 1,597
  • 15
  • 14