oggus icon indicating copy to clipboard operation
oggus copied to clipboard

Trim opus file in ogg container

Open omkar-tenkale opened this issue 4 years ago • 9 comments

Can I use this library to Trim opus file in ogg container

I'm trying to avoid the large size ffmpeg lib and aiming for a small lib to trim/cut opus files in android

By any chance this library could help me?

omkar-tenkale avatar May 25 '21 18:05 omkar-tenkale

It could help you, but not that straightway.

You should first read your audio as OggOpusStream

OggOpusStream oggOpusStream = OggOpusStream.from("audio/technology.opus");

// Get ID Header
IdHeader idHeader = oggOpusStream.getIdHeader();

// Get Comment Header
CommentHeader commentHeader = oggOpusStream.getCommentHeader();

while (true) {
    AudioDataPacket audioDataPacket = oggOpusStream.readAudioPacket();
    if (audioDataPacket == null) break;
    
    for (OpusPacket opusPacket : audioDataPacket.getOpusPackets()) {
        // Do something with opusPacket
    }
}

The create a new OutputStream, and encapsulate audioDataPacket into this OggPage and write to OutputStream.

// Create an output stream
OutputStream outputStream = ...;

// Create a new Ogg page
OggPage oggPage = OggPage.empty();

// Set header fields by calling setX() method
oggPage.setSerialNum(100);


// Add a data packet to this page
oggPage.addDataPacket(audioDataPacket.dump());

// Call dump() method to dump the OggPage object to byte array binary 
byte[] binary = oggPage.dump();

// Write the binary to stream
outputStream.write(binary);

leonfancy avatar May 26 '21 13:05 leonfancy

Thanks for detailed response I'll try to implement this

oggPage.setSerialNum(100); << Should 100 be fixed value or do i need to get it from somewhere?

omkar-tenkale avatar Jun 03 '21 18:06 omkar-tenkale

Also how will I map start and end positions in for loop

long trimStartMs;
long trimEndMs;

...
 for (OpusPacket opusPacket : audioDataPacket.getOpusPackets()) {
        if(packetLiesWithinTrimRange(opusPacket )){ writeToOutput(opusPacket); }
    }
...

private boolean packetLiesWithinTrimRange(OpusPacket packet){
   if(????????){ return true;}
  return false;
}

omkar-tenkale avatar Jun 03 '21 18:06 omkar-tenkale

@leonfancy ??

omkar-tenkale avatar Nov 18 '21 14:11 omkar-tenkale

Also looking into splitting an opus audio. The above ended up with a corrupt opus file in my scenario.

BasselAshi avatar Sep 19 '24 14:09 BasselAshi

I had partial success, the file plays but in media players the duration will be shown of original audio and the position jumping to trimmed section

omkar-tenkale avatar Sep 20 '24 12:09 omkar-tenkale

with dependency on https://github.com/leonfancy/oggus


import org.chenliang.oggus.ogg.OggPage;
import org.chenliang.oggus.ogg.OggStream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class OpusFileUtil {


    static long sampleRate = 48;
    static long streamPreskip = -1;
    static long streamGranulePosFirst;

    public static void trim(long fromMs, long toMs, InputStream source, File result) throws IOException {
        OggPage prevPage = null;

        Log.oneLine("OGGPAGETRIM START ======================");

        OutputStream outputStream = new FileOutputStream(result);

        OggStream oggStream = OggStream.from(source);


        outputStream.write(oggStream.readPage().dump()); //1st id page
        outputStream.write(oggStream.readPage().dump()); //2nd comment page
        outputStream.flush();

        while (true) {
            OggPage oggPage = oggStream.readPage();
            if (oggPage == null) break;
            long pageMsStart = prevPage == null ? 0 : getPageMsEnd(prevPage);
            long pageMsEnd = getPageMsEnd(oggPage);
            Log.oneLine("OGGPAGETRIM MS_START " + pageMsStart + "OGGPAGETRIM MS_END " + pageMsEnd);

            if(pageMsStart > toMs){
                break;
            }
            if (pageMsStart >= fromMs && pageMsEnd < toMs) {
                Log.oneLine("OGGPAGETRIM " + oggPage.getGranulePosition() + " IN RANGE --------------------");
                outputStream.write(oggPage.dump());
                outputStream.flush();
            }

            if (prevPage == null) {
                //this is first page
                streamGranulePosFirst = oggPage.getGranulePosition();
            }
            prevPage = oggPage;
        }



    }

    static long getPageMsEnd(OggPage page) {
        return (page.getGranulePosition() - streamGranulePosFirst - streamPreskip) / sampleRate;
    }
}

omkar-tenkale avatar Sep 20 '24 12:09 omkar-tenkale

That was very helpful, thank you. In terms of the position misalignment, you might need to update the granule position of each page to have it fixed. I also noticed that the pageMsEnd of the second page (excluding the id/comment) is less than the pageMsStart. I was able to get around this by making the equation much simpler: page.getGranulePosition() / sampleRate although I am not certain about the accuracy of the timing yet.

BasselAshi avatar Sep 20 '24 15:09 BasselAshi

Please post the solution for others if you made it work!

omkar-tenkale avatar Sep 22 '24 04:09 omkar-tenkale