content: /*
   * Copyright 2022 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.google.android.exoplayer2.transformer;
  
  import static com.google.android.exoplayer2.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING;
  import static com.google.android.exoplayer2.transformer.AndroidTestUtil.MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_URI_STRING;
  import static com.google.common.truth.Truth.assertThat;
  import static org.junit.Assert.assertThrows;
  
  import android.content.Context;
  import android.net.Uri;
  import androidx.test.core.app.ApplicationProvider;
  import androidx.test.ext.junit.runners.AndroidJUnit4;
  import com.google.android.exoplayer2.Format;
  import com.google.android.exoplayer2.MediaItem;
  import java.util.List;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  
  /**
   * End-to-end instrumentation test for {@link Transformer} for cases that cannot be tested using
   * robolectric.
   */
  @RunWith(AndroidJUnit4.class)
  public class TransformerEndToEndTest {
  
    private final Context context = ApplicationProvider.getApplicationContext();
  
    @Test
    public void videoEditing_completesWithConsistentFrameCount() throws Exception {
      Transformer transformer =
          new Transformer.Builder(context)
              .setTransformationRequest(
                  new TransformationRequest.Builder().setResolution(480).build())
              .setEncoderFactory(
                  new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build())
              .build();
      // Result of the following command:
      // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4
      int expectedFrameCount = 30;
  
      TransformationTestResult result =
          new TransformerAndroidTestRunner.Builder(context, transformer)
              .build()
              .run(
                  /* testId= */ "videoEditing_completesWithConsistentFrameCount",
                  MediaItem.fromUri(Uri.parse(MP4_ASSET_URI_STRING)));
  
      assertThat(result.transformationResult.videoFrameCount).isEqualTo(expectedFrameCount);
    }
  
    @Test
    public void videoOnly_completesWithConsistentDuration() throws Exception {
      Transformer transformer =
          new Transformer.Builder(context)
              .setRemoveAudio(true)
              .setTransformationRequest(
                  new TransformationRequest.Builder().setResolution(480).build())
              .setEncoderFactory(
                  new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build())
              .build();
      long expectedDurationMs = 967;
  
      TransformationTestResult result =
          new TransformerAndroidTestRunner.Builder(context, transformer)
              .build()
              .run(
                  /* testId= */ "videoOnly_completesWithConsistentDuration",
                  MediaItem.fromUri(Uri.parse(MP4_ASSET_URI_STRING)));
  
      assertThat(result.transformationResult.durationMs).isEqualTo(expectedDurationMs);
    }
  
    @Test
    public void clippedMedia_completesWithClippedDuration() throws Exception {
      Transformer transformer = new Transformer.Builder(context).build();
      long clippingStartMs = 10_000;
      long clippingEndMs = 11_000;
      MediaItem mediaItem =
          new MediaItem.Builder()
              .setUri(Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S_URI_STRING))
              .setClippingConfiguration(
                  new MediaItem.ClippingConfiguration.Builder()
                      .setStartPositionMs(clippingStartMs)
                      .setEndPositionMs(clippingEndMs)
                      .build())
              .build();
  
      TransformationTestResult result =
          new TransformerAndroidTestRunner.Builder(context, transformer)
              .build()
              .run(/* testId= */ "clippedMedia_completesWithClippedDuration", mediaItem);
  
      assertThat(result.transformationResult.durationMs).isAtMost(clippingEndMs - clippingStartMs);
    }
  
    @Test
    public void videoEncoderFormatUnsupported_completesWithError() {
      Transformer transformer =
          new Transformer.Builder(context)
              .setEncoderFactory(new VideoUnsupportedEncoderFactory(context))
              .setRemoveAudio(true)
              .build();
  
      TransformationException exception =
          assertThrows(
              TransformationException.class,
              () ->
                  new TransformerAndroidTestRunner.Builder(context, transformer)
                      .build()
                      .run(
                          /* testId= */ "videoEncoderFormatUnsupported_completesWithError",
                          MediaItem.fromUri(Uri.parse(MP4_ASSET_URI_STRING))));
  
      assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
      assertThat(exception.errorCode)
          .isEqualTo(TransformationException.ERROR_CODE_ENCODER_INIT_FAILED);
      assertThat(exception).hasMessageThat().contains("video");
    }
  
    private static final class VideoUnsupportedEncoderFactory implements Codec.EncoderFactory {
  
      private final Codec.EncoderFactory encoderFactory;
  
      public VideoUnsupportedEncoderFactory(Context context) {
        encoderFactory = new DefaultEncoderFactory.Builder(context).build();
      }
  
      @Override
      public Codec createForAudioEncoding(Format format, List<String> allowedMimeTypes)
          throws TransformationException {
        return encoderFactory.createForAudioEncoding(format, allowedMimeTypes);
      }
  
      @Override
      public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
          throws TransformationException {
        throw TransformationException.createForCodec(
            new IllegalArgumentException(),
            /* isVideo= */ true,
            /* isDecoder= */ false,
            format,
            /* mediaCodecName= */ null,
            TransformationException.ERROR_CODE_ENCODER_INIT_FAILED);
      }
  
      @Override
      public boolean audioNeedsEncoding() {
        return false;
      }
  
      @Override
      public boolean videoNeedsEncoding() {
        return true;
      }
    }
  }
  