<commit_before>/*
 * Copyright 2019 LinkedIn Corporation
 * All Rights Reserved.
 *
 * Licensed under the BSD 2-Clause License (the "License").  See License in the project root for
 * license information.
 */
package com.linkedin.android.litr;

import android.media.MediaExtractor;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.linkedin.android.litr.analytics.TransformationStatsCollector;
import com.linkedin.android.litr.exception.InsufficientDiskSpaceException;
import com.linkedin.android.litr.exception.MediaTransformationException;
import com.linkedin.android.litr.exception.TrackTranscoderException;
import com.linkedin.android.litr.io.MediaSource;
import com.linkedin.android.litr.io.MediaTarget;
import com.linkedin.android.litr.transcoder.TrackTranscoder;
import com.linkedin.android.litr.transcoder.TrackTranscoderFactory;
import com.linkedin.android.litr.utils.DiskUtil;
import com.linkedin.android.litr.utils.TranscoderUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import static com.linkedin.android.litr.MediaTransformer.GRANULARITY_NONE;

class TransformationJob implements Runnable {

    private static final String TAG = TransformationJob.class.getSimpleName();

    private static final float DEFAULT_SIZE_PADDING = 0.10f; // 10% padding

    @VisibleForTesting List<TrackTranscoder> trackTranscoders;
    @VisibleForTesting float lastProgress;
    @VisibleForTesting int granularity;

    @VisibleForTesting TrackTranscoderFactory trackTranscoderFactory;
    @VisibleForTesting DiskUtil diskUtil;

    @VisibleForTesting TransformationStatsCollector statsCollector;

    private final List<TrackTransform> trackTransforms;

    private final String jobId;
    private final MarshallingTransformationListener marshallingTransformationListener;

    TransformationJob(@NonNull String jobId,
                      List<TrackTransform> trackTransforms,
                      @IntRange(from = GRANULARITY_NONE) int granularity,
                      @NonNull MarshallingTransformationListener marshallingTransformationListener) {

        this.jobId = jobId;
        this.trackTransforms = trackTransforms;
        this.granularity = granularity;
        this.marshallingTransformationListener = marshallingTransformationListener;

        lastProgress = 0;

        trackTranscoderFactory = new TrackTranscoderFactory();
        diskUtil = new DiskUtil();
        statsCollector = new TransformationStatsCollector();
    }

    @Override
    public void run() {
        try {
            transform();
        } catch (RuntimeException e) {
            Log.e(TAG, "Transformation job error", e);
            Throwable cause = e.getCause();
            if (cause instanceof InterruptedException) {
                cancel();
            } else {
                error(e);
            }
        } catch (MediaTransformationException exception) {
            Log.e(TAG, "Transformation job error", exception);
            exception.setJobId(jobId);
            error(exception);
        }
    }

    @VisibleForTesting
    void transform() throws MediaTransformationException {
        initStatsCollector();
        verifyAvailableDiskSpace();
        createTrackTranscoders();
        startTrackTranscoders();
        seekToMediaRangeStart();

        boolean completed = false;

        marshallingTransformationListener.onStarted(jobId);
        lastProgress = 0;

        // process a frame from active track transcoder, until EoS (end of stream) is reached on each track
        do {
            completed = processNextFrame();

            if (Thread.interrupted()) {
                completed = false;
                cancel();
                break;
            }
        } while (!completed);

        release(completed);
    }

    @VisibleForTesting
    void cancel() {
        release(false);
        marshallingTransformationListener.onCancelled(jobId, statsCollector.getStats());
    }

    @VisibleForTesting
    protected void error(@Nullable Throwable cause) {
        release(false);
        marshallingTransformationListener.onError(jobId, cause, statsCollector.getStats());
    }
<commit_msg>Notify TransformationListener before releasing TransformationJob (#198)<commit_after>/*
 * Copyright 2019 LinkedIn Corporation
 * All Rights Reserved.
 *
 * Licensed under the BSD 2-Clause License (the "License").  See License in the project root for
 * license information.
 */
package com.linkedin.android.litr;

import android.media.MediaExtractor;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.linkedin.android.litr.analytics.TransformationStatsCollector;
import com.linkedin.android.litr.exception.InsufficientDiskSpaceException;
import com.linkedin.android.litr.exception.MediaTransformationException;
import com.linkedin.android.litr.exception.TrackTranscoderException;
import com.linkedin.android.litr.io.MediaSource;
import com.linkedin.android.litr.io.MediaTarget;
import com.linkedin.android.litr.transcoder.TrackTranscoder;
import com.linkedin.android.litr.transcoder.TrackTranscoderFactory;
import com.linkedin.android.litr.utils.DiskUtil;
import com.linkedin.android.litr.utils.TranscoderUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import static com.linkedin.android.litr.MediaTransformer.GRANULARITY_NONE;

class TransformationJob implements Runnable {

    private static final String TAG = TransformationJob.class.getSimpleName();

    private static final float DEFAULT_SIZE_PADDING = 0.10f; // 10% padding

    @VisibleForTesting List<TrackTranscoder> trackTranscoders;
    @VisibleForTesting float lastProgress;
    @VisibleForTesting int granularity;

    @VisibleForTesting TrackTranscoderFactory trackTranscoderFactory;
    @VisibleForTesting DiskUtil diskUtil;

    @VisibleForTesting TransformationStatsCollector statsCollector;

    private final List<TrackTransform> trackTransforms;

    private final String jobId;
    private final MarshallingTransformationListener marshallingTransformationListener;

    TransformationJob(@NonNull String jobId,
                      List<TrackTransform> trackTransforms,
                      @IntRange(from = GRANULARITY_NONE) int granularity,
                      @NonNull MarshallingTransformationListener marshallingTransformationListener) {

        this.jobId = jobId;
        this.trackTransforms = trackTransforms;
        this.granularity = granularity;
        this.marshallingTransformationListener = marshallingTransformationListener;

        lastProgress = 0;

        trackTranscoderFactory = new TrackTranscoderFactory();
        diskUtil = new DiskUtil();
        statsCollector = new TransformationStatsCollector();
    }

    @Override
    public void run() {
        try {
            transform();
        } catch (RuntimeException e) {
            Log.e(TAG, "Transformation job error", e);
            Throwable cause = e.getCause();
            if (cause instanceof InterruptedException) {
                cancel();
            } else {
                error(e);
            }
        } catch (MediaTransformationException exception) {
            Log.e(TAG, "Transformation job error", exception);
            exception.setJobId(jobId);
            error(exception);
        }
    }

    @VisibleForTesting
    void transform() throws MediaTransformationException {
        initStatsCollector();
        verifyAvailableDiskSpace();
        createTrackTranscoders();
        startTrackTranscoders();
        seekToMediaRangeStart();

        boolean completed = false;

        marshallingTransformationListener.onStarted(jobId);
        lastProgress = 0;

        // process a frame from active track transcoder, until EoS (end of stream) is reached on each track
        do {
            completed = processNextFrame();

            if (Thread.interrupted()) {
                completed = false;
                cancel();
                break;
            }
        } while (!completed);

        release(completed);
    }

    @VisibleForTesting
    void cancel() {
        marshallingTransformationListener.onCancelled(jobId, statsCollector.getStats());
        release(false);
    }

    @VisibleForTesting
    protected void error(@Nullable Throwable cause) {
        marshallingTransformationListener.onError(jobId, cause, statsCollector.getStats());
        release(false);
    }

{'fraction_non_alphanumeric': 0.0793488160291439, 'fraction_numerical': 0.0030737704918032786, 'mean_word_length': 3.175380228136882, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '829391', 'n_tokens_mistral': 2337, 'n_tokens_neox': 2230, 'n_words': 497}