1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.video;
18 
19 import android.location.Location;
20 
21 import androidx.annotation.IntRange;
22 import androidx.core.util.Preconditions;
23 
24 import org.jspecify.annotations.NonNull;
25 import org.jspecify.annotations.Nullable;
26 
27 /**
28  * Options for configuring output destination for generating a recording.
29  *
30  * <p>A {@link PendingRecording} can be generated with {@link Recorder#prepareRecording} for
31  * different types of output destination, such as {@link FileOutputOptions},
32  * {@link FileDescriptorOutputOptions} and {@link MediaStoreOutputOptions}.
33  *
34  * @see FileOutputOptions
35  * @see FileDescriptorOutputOptions
36  * @see MediaStoreOutputOptions
37  */
38 public abstract class OutputOptions {
39 
40     /** Represents an unbound file size. */
41     public static final int FILE_SIZE_UNLIMITED = 0;
42 
43     /** Represents an unlimited duration. */
44     public static final int DURATION_UNLIMITED = 0;
45 
46     private final OutputOptionsInternal mOutputOptionsInternal;
47 
OutputOptions(@onNull OutputOptionsInternal outputOptionsInternal)48     OutputOptions(@NonNull OutputOptionsInternal outputOptionsInternal) {
49         mOutputOptionsInternal = outputOptionsInternal;
50     }
51 
52     /**
53      * Gets the limit for the file size in bytes.
54      *
55      * @return the file size limit in bytes or {@link #FILE_SIZE_UNLIMITED} if it's unlimited.
56      */
57     @IntRange(from = 0)
getFileSizeLimit()58     public long getFileSizeLimit() {
59         return mOutputOptionsInternal.getFileSizeLimit();
60     }
61 
62     /**
63      * Returns a {@link Location} object representing the geographic location where the video was
64      * recorded.
65      *
66      * @return the location object or {@code null} if no location was set.
67      */
getLocation()68     public @Nullable Location getLocation() {
69         return mOutputOptionsInternal.getLocation();
70     }
71 
72     /**
73      * Gets the limit for the video duration in milliseconds.
74      *
75      * @return the video duration limit in milliseconds or {@link #DURATION_UNLIMITED} if it's
76      * unlimited.
77      */
78     @IntRange(from = 0)
getDurationLimitMillis()79     public long getDurationLimitMillis() {
80         return mOutputOptionsInternal.getDurationLimitMillis();
81     }
82 
83     /**
84      * The builder of the {@link OutputOptions}.
85      */
86     @SuppressWarnings("unchecked") // Cast to type B
87     abstract static class Builder<T extends OutputOptions, B> {
88 
89         final OutputOptionsInternal.Builder<?> mRootInternalBuilder;
90 
Builder(OutputOptionsInternal.@onNull Builder<?> builder)91         Builder(OutputOptionsInternal.@NonNull Builder<?> builder) {
92             mRootInternalBuilder = builder;
93             // Apply default value
94             mRootInternalBuilder.setFileSizeLimit(FILE_SIZE_UNLIMITED);
95             mRootInternalBuilder.setDurationLimitMillis(DURATION_UNLIMITED);
96         }
97 
98         /**
99          * Sets the limit for the file length in bytes.
100          *
101          * <p>When used with {@link Recorder} to generate recording, if the specified file size
102          * limit is reached while the recording is being recorded, the recording will be
103          * finalized with {@link VideoRecordEvent.Finalize#ERROR_FILE_SIZE_LIMIT_REACHED}.
104          *
105          * <p>If not set or set with zero, the file size will be {@linkplain #FILE_SIZE_UNLIMITED
106          * unlimited}. If set with a negative value, an {@link IllegalArgumentException} will be
107          * thrown.
108          *
109          * @param fileSizeLimitBytes the file size limit in bytes.
110          * @return this Builder.
111          * @throws IllegalArgumentException if the specified file size limit is negative.
112          */
setFileSizeLimit(@ntRangefrom = 0) long fileSizeLimitBytes)113         public @NonNull B setFileSizeLimit(@IntRange(from = 0) long fileSizeLimitBytes) {
114             Preconditions.checkArgument(fileSizeLimitBytes >= 0, "The specified file size limit "
115                     + "can't be negative.");
116             mRootInternalBuilder.setFileSizeLimit(fileSizeLimitBytes);
117             return (B) this;
118         }
119 
120         /**
121          * Sets the limit for the video duration in milliseconds.
122          *
123          * <p>When used to generate recording with {@link Recorder}, if the specified duration
124          * limit is reached while the recording is being recorded, the recording will be
125          * finalized with {@link VideoRecordEvent.Finalize#ERROR_DURATION_LIMIT_REACHED}.
126          *
127          * <p>If not set or set with zero, the duration will be {@linkplain #DURATION_UNLIMITED
128          * unlimited}. If set with a negative value, an {@link IllegalArgumentException} will be
129          * thrown.
130          *
131          * @param durationLimitMillis the video duration limit in milliseconds.
132          * @return this Builder.
133          * @throws IllegalArgumentException if the specified duration limit is negative.
134          */
setDurationLimitMillis(@ntRangefrom = 0) long durationLimitMillis)135         public @NonNull B setDurationLimitMillis(@IntRange(from = 0) long durationLimitMillis) {
136             Preconditions.checkArgument(durationLimitMillis >= 0, "The specified duration limit "
137                     + "can't be negative.");
138             mRootInternalBuilder.setDurationLimitMillis(durationLimitMillis);
139             return (B) this;
140         }
141 
142         /**
143          * Sets a {@link Location} object representing a geographic location where the video was
144          * recorded.
145          *
146          * <p>When use with {@link Recorder}, the geographic location is stored in udta box if the
147          * output format is MP4, and is ignored for other formats. The geographic location is
148          * stored according to ISO-6709 standard.
149          *
150          * <p>If {@code null}, no location information will be saved with the video. Default
151          * value is {@code null}.
152          *
153          * @throws IllegalArgumentException if the latitude of the location is not in the range
154          * {@code [-90, 90]} or the longitude of the location is not in the range {@code [-180,
155          * 180]}.
156          */
setLocation(@ullable Location location)157         public @NonNull B setLocation(@Nullable Location location) {
158             if (location != null) {
159                 Preconditions.checkArgument(
160                         location.getLatitude() >= -90 && location.getLatitude() <= 90,
161                         "Latitude must be in the range [-90, 90]");
162                 Preconditions.checkArgument(
163                         location.getLongitude() >= -180 && location.getLongitude() <= 180,
164                         "Longitude must be in the range [-180, 180]");
165             }
166             mRootInternalBuilder.setLocation(location);
167             return (B) this;
168         }
169 
170         /**
171          * Builds the {@link OutputOptions} instance.
172          */
build()173         abstract @NonNull T build();
174     }
175 
176     // A base class of a @AutoValue class
177     abstract static class OutputOptionsInternal {
178 
179         @IntRange(from = 0)
getFileSizeLimit()180         abstract long getFileSizeLimit();
181 
182         @IntRange(from = 0)
getDurationLimitMillis()183         abstract long getDurationLimitMillis();
184 
getLocation()185         abstract @Nullable Location getLocation();
186 
187         // A base class of a @AutoValue.Builder class
188         @SuppressWarnings("NullableProblems") // Nullable problem in AutoValue generated class
189         abstract static class Builder<B> {
190 
setFileSizeLimit(@ntRangefrom = 0) long fileSizeLimitBytes)191             abstract @NonNull B setFileSizeLimit(@IntRange(from = 0) long fileSizeLimitBytes);
192 
setDurationLimitMillis( @ntRangefrom = 0) long durationLimitMillis)193             abstract @NonNull B setDurationLimitMillis(
194                     @IntRange(from = 0) long durationLimitMillis);
195 
setLocation(@ullable Location location)196             abstract @NonNull B setLocation(@Nullable Location location);
197 
build()198             abstract @NonNull OutputOptionsInternal build();
199         }
200     }
201 }
202