• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 com.android.usbtuner.exoplayer.cache;
18 
19 import android.media.MediaFormat;
20 import android.util.Pair;
21 
22 import java.io.DataInputStream;
23 import java.io.DataOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.nio.charset.StandardCharsets;
30 import java.util.ArrayList;
31 import java.util.SortedMap;
32 
33 /**
34  * Manages DVR storage.
35  */
36 public class DvrStorageManager implements CacheManager.StorageManager {
37 
38     // TODO: make serializable classes and use protobuf after internal data structure is finalized.
39     private static final String KEY_PIXEL_WIDTH_HEIGHT_RATIO =
40             "com.google.android.videos.pixelWidthHeightRatio";
41     private static final String META_FILE_SUFFIX = ".meta";
42     private static final String IDX_FILE_SUFFIX = ".idx";
43 
44     // Size of minimum reserved storage buffer which will be used to save meta files
45     // and index files after actual recording finished.
46     private static final long MIN_BUFFER_BYTES = 256L * 1024 * 1024;
47     private static final int NO_VALUE = -1;
48     private static final long NO_VALUE_LONG = -1L;
49 
50     private final File mCacheDir;
51 
52     // {@code true} when this is for recording, {@code false} when this is for replaying.
53     private final boolean mIsRecording;
54 
DvrStorageManager(File file, boolean isRecording)55     public DvrStorageManager(File file, boolean isRecording) {
56         mCacheDir = file;
57         mCacheDir.mkdirs();
58         mIsRecording = isRecording;
59     }
60 
61     @Override
clearStorage()62     public void clearStorage() {
63         if (mIsRecording) {
64             for (File file : mCacheDir.listFiles()) {
65                 file.delete();
66             }
67         }
68     }
69 
70     @Override
getCacheDir()71     public File getCacheDir() {
72         return mCacheDir;
73     }
74 
75     @Override
isPersistent()76     public boolean isPersistent() {
77         return true;
78     }
79 
80     @Override
reachedStorageMax(long cacheSize, long pendingDelete)81     public boolean reachedStorageMax(long cacheSize, long pendingDelete) {
82         return false;
83     }
84 
85     @Override
hasEnoughBuffer(long pendingDelete)86     public boolean hasEnoughBuffer(long pendingDelete) {
87         return !mIsRecording || mCacheDir.getUsableSpace() >= MIN_BUFFER_BYTES;
88     }
89 
readFormatInt(DataInputStream in, MediaFormat format, String key)90     private void readFormatInt(DataInputStream in, MediaFormat format, String key)
91             throws IOException {
92         int val = in.readInt();
93         if (val != NO_VALUE) {
94             format.setInteger(key, val);
95         }
96     }
97 
readFormatLong(DataInputStream in, MediaFormat format, String key)98     private void readFormatLong(DataInputStream in, MediaFormat format, String key)
99             throws IOException {
100         long val = in.readLong();
101         if (val != NO_VALUE_LONG) {
102             format.setLong(key, val);
103         }
104     }
105 
readFormatFloat(DataInputStream in, MediaFormat format, String key)106     private void readFormatFloat(DataInputStream in, MediaFormat format, String key)
107             throws IOException {
108         float val = in.readFloat();
109         if (val != NO_VALUE) {
110             format.setFloat(key, val);
111         }
112     }
113 
readString(DataInputStream in)114     private String readString(DataInputStream in) throws IOException {
115         int len = in.readInt();
116         if (len <= 0) {
117             return null;
118         }
119         byte [] strBytes = new byte[len];
120         in.readFully(strBytes);
121         return new String(strBytes, StandardCharsets.UTF_8);
122     }
123 
readFormatString(DataInputStream in, MediaFormat format, String key)124     private void readFormatString(DataInputStream in, MediaFormat format, String key)
125             throws IOException {
126         String str = readString(in);
127         if (str != null) {
128             format.setString(key, str);
129         }
130     }
131 
readByteBuffer(DataInputStream in)132     private ByteBuffer readByteBuffer(DataInputStream in) throws IOException {
133         int len = in.readInt();
134         if (len <= 0) {
135             return null;
136         }
137         byte [] bytes = new byte[len];
138         in.readFully(bytes);
139         ByteBuffer buffer = ByteBuffer.allocate(len);
140         buffer.put(bytes);
141         buffer.flip();
142 
143         return buffer;
144     }
145 
readFormatByteBuffer(DataInputStream in, MediaFormat format, String key)146     private void readFormatByteBuffer(DataInputStream in, MediaFormat format, String key)
147             throws IOException {
148         ByteBuffer buffer = readByteBuffer(in);
149         if (buffer != null) {
150             format.setByteBuffer(key, buffer);
151         }
152     }
153 
154     @Override
readTrackInfoFile(boolean isAudio)155     public Pair<String, MediaFormat> readTrackInfoFile(boolean isAudio) throws IOException {
156         File file = new File(getCacheDir(), (isAudio ? "audio" : "video") + META_FILE_SUFFIX);
157         try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
158             String name = readString(in);
159             MediaFormat format = new MediaFormat();
160             readFormatString(in, format, MediaFormat.KEY_MIME);
161             readFormatInt(in, format, MediaFormat.KEY_MAX_INPUT_SIZE);
162             readFormatInt(in, format, MediaFormat.KEY_WIDTH);
163             readFormatInt(in, format, MediaFormat.KEY_HEIGHT);
164             readFormatInt(in, format, MediaFormat.KEY_CHANNEL_COUNT);
165             readFormatInt(in, format, MediaFormat.KEY_SAMPLE_RATE);
166             readFormatFloat(in, format, KEY_PIXEL_WIDTH_HEIGHT_RATIO);
167             for (int i = 0; i < 3; ++i) {
168                 readFormatByteBuffer(in, format, "csd-" + i);
169             }
170             readFormatLong(in, format, MediaFormat.KEY_DURATION);
171             return new Pair<>(name, format);
172         }
173     }
174 
175     @Override
readIndexFile(String trackId)176     public ArrayList<Long> readIndexFile(String trackId) throws IOException {
177         ArrayList<Long> indices = new ArrayList<>();
178         File file = new File(getCacheDir(), trackId + IDX_FILE_SUFFIX);
179         try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
180             long count = in.readLong();
181             for (long i = 0; i < count; ++i) {
182                 indices.add(in.readLong());
183             }
184             return indices;
185         }
186     }
187 
writeFormatInt(DataOutputStream out, MediaFormat format, String key)188     private void writeFormatInt(DataOutputStream out, MediaFormat format, String key)
189             throws IOException {
190         if (format.containsKey(key)) {
191             out.writeInt(format.getInteger(key));
192         } else {
193             out.writeInt(NO_VALUE);
194         }
195     }
196 
writeFormatLong(DataOutputStream out, MediaFormat format, String key)197     private void writeFormatLong(DataOutputStream out, MediaFormat format, String key)
198             throws IOException {
199         if (format.containsKey(key)) {
200             out.writeLong(format.getLong(key));
201         } else {
202             out.writeLong(NO_VALUE_LONG);
203         }
204     }
205 
writeFormatFloat(DataOutputStream out, MediaFormat format, String key)206     private void writeFormatFloat(DataOutputStream out, MediaFormat format, String key)
207             throws IOException {
208         if (format.containsKey(key)) {
209             out.writeFloat(format.getFloat(key));
210         } else {
211             out.writeFloat(NO_VALUE);
212         }
213     }
214 
writeString(DataOutputStream out, String str)215     private void writeString(DataOutputStream out, String str) throws IOException {
216         byte [] data = str.getBytes(StandardCharsets.UTF_8);
217         out.writeInt(data.length);
218         if (data.length > 0) {
219             out.write(data);
220         }
221     }
222 
writeFormatString(DataOutputStream out, MediaFormat format, String key)223     private void writeFormatString(DataOutputStream out, MediaFormat format, String key)
224             throws IOException {
225         if (format.containsKey(key)) {
226             writeString(out, format.getString(key));
227         } else {
228             out.writeInt(0);
229         }
230     }
231 
writeByteBuffer(DataOutputStream out, ByteBuffer buffer)232     private void writeByteBuffer(DataOutputStream out, ByteBuffer buffer) throws IOException {
233         byte [] data = new byte[buffer.limit()];
234         buffer.get(data);
235         buffer.flip();
236         out.writeInt(data.length);
237         if (data.length > 0) {
238             out.write(data);
239         } else {
240             out.writeInt(0);
241         }
242     }
243 
writeFormatByteBuffer(DataOutputStream out, MediaFormat format, String key)244     private void writeFormatByteBuffer(DataOutputStream out, MediaFormat format, String key)
245             throws IOException {
246         if (format.containsKey(key)) {
247             writeByteBuffer(out, format.getByteBuffer(key));
248         } else {
249             out.writeInt(0);
250         }
251     }
252 
253     @Override
writeTrackInfoFile(String trackId, MediaFormat format, boolean isAudio)254     public void writeTrackInfoFile(String trackId, MediaFormat format, boolean isAudio)
255             throws IOException {
256         File file = new File(getCacheDir(), (isAudio ? "audio" : "video") + META_FILE_SUFFIX);
257         try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
258             writeString(out, trackId);
259             writeFormatString(out, format, MediaFormat.KEY_MIME);
260             writeFormatInt(out, format, MediaFormat.KEY_MAX_INPUT_SIZE);
261             writeFormatInt(out, format, MediaFormat.KEY_WIDTH);
262             writeFormatInt(out, format, MediaFormat.KEY_HEIGHT);
263             writeFormatInt(out, format, MediaFormat.KEY_CHANNEL_COUNT);
264             writeFormatInt(out, format, MediaFormat.KEY_SAMPLE_RATE);
265             writeFormatFloat(out, format, KEY_PIXEL_WIDTH_HEIGHT_RATIO);
266             for (int i = 0; i < 3; ++i) {
267                 writeFormatByteBuffer(out, format, "csd-" + i);
268             }
269             writeFormatLong(out, format, MediaFormat.KEY_DURATION);
270         }
271     }
272 
273     @Override
writeIndexFile(String trackName, SortedMap<Long, SampleCache> index)274     public void writeIndexFile(String trackName, SortedMap<Long, SampleCache> index)
275             throws IOException {
276         File indexFile  = new File(getCacheDir(), trackName + IDX_FILE_SUFFIX);
277         try (DataOutputStream out = new DataOutputStream(new FileOutputStream(indexFile))) {
278             out.writeLong(index.size());
279             for (Long key : index.keySet()) {
280                 out.writeLong(key);
281             }
282         }
283     }
284 }
285