2016-03-24 35 views
4

MediaCodecMediaMuxerの助けを借りてビデオをエンコードします。結果として、私はmp4ビデオファイルを持っています。このmp4ファイルにメタデータ(時間を作成する)を設定するにはどうすればよいですか? MediaMetadataRetrieverはメタデータの読み取りのみ可能ですが、変更はできません。私はffmpegを使いたくない。私はmp4parserライブラリ(this class)を試しましたが、それは私のためには機能しません。メタデータをmp4に設定

+0

何を記載してくださいあなたがしようとした。 – Reinard

+0

@Reinard私は私の質問を編集しました。 – danik

答えて

3

generally supported specificationが存在しないため、MP4ファイルのメタデータを設定することは明確ではありませんが、ほとんどのビデオプレーヤーはAppleの仕様をサポートしています。

詳細情報here,hereおよびhere

ここ

は(MetaDataInsert.javaサンプルに基づいて)MP4メタデータにタイトルや作成日を設定するコードです:

import com.coremedia.iso.IsoFile; 
import com.coremedia.iso.boxes.*; 
import com.coremedia.iso.boxes.apple.AppleItemListBox; 
import com.googlecode.mp4parser.boxes.apple.AppleNameBox; 
import com.googlecode.mp4parser.boxes.apple.AppleRecordingYear2Box; 
import com.googlecode.mp4parser.util.Path; 

import java.io.*; 
import java.nio.ByteBuffer; 
import java.nio.channels.Channels; 
import java.nio.channels.FileChannel; 
import java.util.List; 

public class Mp4MetadataWriter { 

    public FileChannel splitFileAndInsert(File f, long pos, long length) throws IOException { 
     FileChannel read = new RandomAccessFile(f, "r").getChannel(); 
     File tmp = File.createTempFile("ChangeMetaData", "splitFileAndInsert"); 
     FileChannel tmpWrite = new RandomAccessFile(tmp, "rw").getChannel(); 
     read.position(pos); 
     tmpWrite.transferFrom(read, 0, read.size() - pos); 
     read.close(); 
     FileChannel write = new RandomAccessFile(f, "rw").getChannel(); 
     write.position(pos + length); 
     tmpWrite.position(0); 
     long transferred = 0; 
     while ((transferred += tmpWrite.transferTo(0, tmpWrite.size() - transferred, write)) != tmpWrite.size()) { 
      System.out.println(transferred); 
     } 
     System.out.println(transferred); 
     tmpWrite.close(); 
     tmp.delete(); 
     return write; 
    } 


    private boolean needsOffsetCorrection(IsoFile isoFile) { 
     if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) { 
      // Fragmented files don't need a correction 
      return false; 
     } else { 
      // no correction needed if mdat is before moov as insert into moov want change the offsets of mdat 
      for (Box box : isoFile.getBoxes()) { 
       if ("moov".equals(box.getType())) { 
        return true; 
       } 
       if ("mdat".equals(box.getType())) { 
        return false; 
       } 
      } 
      throw new RuntimeException("I need moov or mdat. Otherwise all this doesn't make sense"); 
     } 
    } 

    public void writeMetadata(String videoFilePath, String theTitle, String theDate) throws IOException { 

     File videoFile = new File(videoFilePath); 
     if (!videoFile.exists()) { 
      throw new FileNotFoundException("File " + videoFilePath + " not exists"); 
     } 

     if (!videoFile.canWrite()) { 
      throw new IllegalStateException("No write permissions to file " + videoFilePath); 
     } 
     IsoFile isoFile = new IsoFile(videoFilePath); 

     MovieBox moov = isoFile.getBoxes(MovieBox.class).get(0); 
     FreeBox freeBox = findFreeBox(moov); 

     boolean correctOffset = needsOffsetCorrection(isoFile); 
     long sizeBefore = moov.getSize(); 
     long offset = 0; 
     for (Box box : isoFile.getBoxes()) { 
      if ("moov".equals(box.getType())) { 
       break; 
      } 
      offset += box.getSize(); 
     } 

     // Create structure or just navigate to Apple List Box. 
     UserDataBox userDataBox; 
     if ((userDataBox = Path.getPath(moov, "udta")) == null) { 
      userDataBox = new UserDataBox(); 
      moov.addBox(userDataBox); 
     } 
     MetaBox metaBox; 
     if ((metaBox = Path.getPath(userDataBox, "meta")) == null) { 
      metaBox = new MetaBox(); 
      HandlerBox hdlr; 
      hdlr = new HandlerBox(); 
      hdlr.setHandlerType("mdir"); 
      metaBox.addBox(hdlr); 
      userDataBox.addBox(metaBox); 
     } 
     AppleItemListBox ilst; 
     if ((ilst = Path.getPath(metaBox, "ilst")) == null) { 
      ilst = new AppleItemListBox(); 
      metaBox.addBox(ilst); 

     } 
     if (freeBox == null) { 
      freeBox = new FreeBox(128 * 1024); 
      metaBox.addBox(freeBox); 
     } 

     // Got Apple List Box 

     AppleNameBox nam; 
     if ((nam = Path.getPath(ilst, AppleNameBox.TYPE)) == null) { 
      nam = new AppleNameBox(); 
     } 
     nam.setDataCountry(0); 
     nam.setDataLanguage(0); 
     nam.setValue(theTitle); 
     ilst.addBox(nam); 

     AppleRecordingYear2Box day; 
     if ((day = Path.getPath(ilst, "©day")) == null) { 
      day = new AppleRecordingYear2Box(); 
     } 
     day.setDataCountry(0); 
     day.setDataLanguage(0); 
     day.setValue(theDate); 
     ilst.addBox(day); 

     long sizeAfter = moov.getSize(); 
     long diff = sizeAfter - sizeBefore; 
     // This is the difference of before/after 

     // can we compensate by resizing a Free Box we have found? 
     if (freeBox.getData().limit() > diff) { 
      // either shrink or grow! 
      freeBox.setData(ByteBuffer.allocate((int) (freeBox.getData().limit() - diff))); 
      sizeAfter = moov.getSize(); 
      diff = sizeAfter - sizeBefore; 
     } 
     if (correctOffset && diff != 0) { 
      correctChunkOffsets(moov, diff); 
     } 
     BetterByteArrayOutputStream baos = new BetterByteArrayOutputStream(); 
     moov.getBox(Channels.newChannel(baos)); 
     isoFile.close(); 
     FileChannel fc; 
     if (diff != 0) { 
      // this is not good: We have to insert bytes in the middle of the file 
      // and this costs time as it requires re-writing most of the file's data 
      fc = splitFileAndInsert(videoFile, offset, sizeAfter - sizeBefore); 
     } else { 
      // simple overwrite of something with the file 
      fc = new RandomAccessFile(videoFile, "rw").getChannel(); 
     } 
     fc.position(offset); 
     fc.write(ByteBuffer.wrap(baos.getBuffer(), 0, baos.size())); 
     fc.close(); 
    } 

    FreeBox findFreeBox(Container c) { 
     for (Box box : c.getBoxes()) { 
      System.err.println(box.getType()); 
      if (box instanceof FreeBox) { 
       return (FreeBox) box; 
      } 
      if (box instanceof Container) { 
       FreeBox freeBox = findFreeBox((Container) box); 
       if (freeBox != null) { 
        return freeBox; 
       } 
      } 
     } 
     return null; 
    } 

    private void correctChunkOffsets(MovieBox movieBox, long correction) { 
     List<ChunkOffsetBox> chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/stco[0]"); 
     if (chunkOffsetBoxes.isEmpty()) { 
      chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/st64[0]"); 
     } 
     for (ChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) { 
      long[] cOffsets = chunkOffsetBox.getChunkOffsets(); 
      for (int i = 0; i < cOffsets.length; i++) { 
       cOffsets[i] += correction; 
      } 
     } 
    } 

    private static class BetterByteArrayOutputStream extends ByteArrayOutputStream { 
     byte[] getBuffer() { 
      return buf; 
     } 
    } 

} 

使用法:

new Mp4MetadataWriter().writeMetadata("/home/user/downloads/1.mp4", "Yet another video title", "2020"); 

結果: VLC Media Information Dialog

+0

Nikolai Doronin、お返事ありがとうございます。あなたのソリューションはほとんどのビデオで正常に動作しますが、私の場合はそうではありません。私は 'MediaCodec'の助けを借りてビデオをエンコードし、' MediaMuxer'でパックします。結果ビデオの例は、https://dl.dropboxusercontent.com/u/15506779/persistent/edufii/issues/mp4parser-151.mp4を参照してください。このビデオのmp4parser 1.1.18はOOMをスローします。詳細はこちらhttps://github.com/sannies/mp4parser/issues/151 – danik

関連する問題