/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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.android.media.benchmark.library;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;

public class Extractor {
    private static final String TAG = "Extractor";
    private static final int kMaxBufSize = 1024 * 1024 * 16;
    private MediaExtractor mExtractor;
    private ByteBuffer mFrameBuffer;
    private MediaCodec.BufferInfo mBufferInfo;
    private Stats mStats;
    private long mDurationUs;

    public Extractor() {
        mFrameBuffer = ByteBuffer.allocate(kMaxBufSize);
        mBufferInfo = new MediaCodec.BufferInfo();
        mStats = new Stats();
    }

    /**
     * Creates a Media Extractor and sets data source(FileDescriptor)to use
     *
     * @param fileDescriptor FileDescriptor for the file which is to be extracted
     * @return TrackCount of the sample
     * @throws IOException If FileDescriptor is null
     */
    public int setUpExtractor(FileDescriptor fileDescriptor) throws IOException {
        long sTime = mStats.getCurTime();
        mExtractor = new MediaExtractor();
        mExtractor.setDataSource(fileDescriptor);
        long eTime = mStats.getCurTime();
        long timeTaken = mStats.getTimeDiff(sTime, eTime);
        mStats.setInitTime(timeTaken);
        return mExtractor.getTrackCount();
    }

    /**
     * Returns the track format of the specified index
     *
     * @param trackID Index of the track
     * @return Format of the track
     */
    public MediaFormat getFormat(int trackID) { return mExtractor.getTrackFormat(trackID); }

    /**
     * Returns the extracted buffer for the input clip
     */
    public ByteBuffer getFrameBuffer() { return this.mFrameBuffer; }

    /**
     * Returns the information of buffer related to sample
     */
    public MediaCodec.BufferInfo getBufferInfo() { return this.mBufferInfo; }

    /**
     * Returns the maximum sample size for the selected track
     * @return max sample size in the given track
     */
    public int getMaxSampleSize() {
        int size = 0;
        int maxSampleSize = 0;
        while ((size = (int) mExtractor.getSampleSize()) != -1) {
            maxSampleSize = Math.max(maxSampleSize, size);
            mExtractor.advance();
        }
        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
        return maxSampleSize;
    }

    /**
     * Returns the duration of the sample
     */
    public long getClipDuration() { return this.mDurationUs; }

    /**
     * Retrieve the current sample and store it in the byte buffer
     * Also, sets the information related to extracted sample and store it in buffer info
     *
     * @return Sample size of the extracted sample
     */
    public int getFrameSample() {
        int sampleSize = mExtractor.readSampleData(mFrameBuffer, 0);
        if (sampleSize < 0) {
            mBufferInfo.flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
            mBufferInfo.size = 0;
        } else {
            mBufferInfo.size = sampleSize;
            mBufferInfo.offset = 0;
            mBufferInfo.flags = mExtractor.getSampleFlags();
            mBufferInfo.presentationTimeUs = mExtractor.getSampleTime();
            mExtractor.advance();
        }
        return sampleSize;
    }

    /**
     * Setup the track format and get the duration of the sample
     * Track is selected here for extraction
     *
     * @param trackId Track index to be selected
     * @return 0 for valid track, otherwise -1
     */
    public int selectExtractorTrack(int trackId) {
        MediaFormat trackFormat = mExtractor.getTrackFormat(trackId);
        mDurationUs = trackFormat.getLong(MediaFormat.KEY_DURATION);
        if (mDurationUs < 0) {
            Log.e(TAG, "Invalid Clip");
            return -1;
        }
        mExtractor.selectTrack(trackId);
        return 0;
    }

    /**
     * Unselect the track
     *
     * @param trackId Track Index to be unselected
     */
    public void unselectExtractorTrack(int trackId) { mExtractor.unselectTrack(trackId); }

    /**
     * Free up the resources
     */
    public void deinitExtractor() {
        long sTime = mStats.getCurTime();
        mExtractor.release();
        long eTime = mStats.getCurTime();
        long timeTaken = mStats.getTimeDiff(sTime, eTime);
        mStats.setDeInitTime(timeTaken);
    }

    /**
     * Performs extract operation
     *
     * @param currentTrack Track index to be extracted
     * @return Status as 0 if extraction is successful, -1 otherwise
     */
    public int extractSample(int currentTrack) {
        int status;
        status = selectExtractorTrack(currentTrack);
        if (status == -1) {
            Log.e(TAG, "Failed to select track");
            return -1;
        }
        mStats.setStartTime();
        while (true) {
            int readSampleSize = getFrameSample();
            if (readSampleSize <= 0) {
                break;
            }
            mStats.addOutputTime();
            mStats.addFrameSize(readSampleSize);
        }
        unselectExtractorTrack(currentTrack);
        return 0;
    }

    /**
     * Write the benchmark logs for the given input file
     *
     * @param inputReference Name of the input file
     * @param mimeType       Mime type of the muxed file
     * @param statsFile      The output file where the stats data is written
     */
    public void dumpStatistics(String inputReference, String mimeType, String statsFile)
            throws IOException {
        String operation = "extract";
        mStats.dumpStatistics(inputReference, operation, mimeType, "", mDurationUs, statsFile);
    }
}
