11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.exoplayer.buffer;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.MediaFormat;
206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.util.Log;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Pair;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.google.protobuf.nano.MessageNano;
256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.DataInputStream;
271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.DataOutputStream;
281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.File;
291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.FileInputStream;
301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.FileOutputStream;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.IOException;
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.ByteBuffer;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.charset.StandardCharsets;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport java.util.List;
366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport java.util.Map;
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.SortedMap;
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Manages DVR storage.
411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
4265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic class DvrStorageManager implements BufferManager.StorageManager {
436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final String TAG = "DvrStorageManager";
441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // TODO: make serializable classes and use protobuf after internal data structure is finalized.
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String KEY_PIXEL_WIDTH_HEIGHT_RATIO =
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "com.google.android.videos.pixelWidthHeightRatio";
486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final String META_FILE_TYPE_AUDIO = "audio";
496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final String META_FILE_TYPE_VIDEO = "video";
506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final String META_FILE_TYPE_CAPTION = "caption";
511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String META_FILE_SUFFIX = ".meta";
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String IDX_FILE_SUFFIX = ".idx";
536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static final String IDX_FILE_SUFFIX_V2 = IDX_FILE_SUFFIX + "2";
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // Size of minimum reserved storage buffer which will be used to save meta files
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // and index files after actual recording finished.
57ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final long MIN_BUFFER_BYTES = 256L * 1024 * 1024;
581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int NO_VALUE = -1;
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final long NO_VALUE_LONG = -1L;
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final File mBufferDir;
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // {@code true} when this is for recording, {@code false} when this is for replaying.
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final boolean mIsRecording;
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public DvrStorageManager(File file, boolean isRecording) {
6765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mBufferDir = file;
6865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mBufferDir.mkdirs();
691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mIsRecording = isRecording;
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
7365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public File getBufferDir() {
7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mBufferDir;
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public boolean isPersistent() {
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
8365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public boolean reachedStorageMax(long bufferSize, long pendingDelete) {
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return false;
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public boolean hasEnoughBuffer(long pendingDelete) {
8965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return !mIsRecording || mBufferDir.getUsableSpace() >= MIN_BUFFER_BYTES;
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void readFormatInt(DataInputStream in, MediaFormat format, String key)
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int val = in.readInt();
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (val != NO_VALUE) {
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            format.setInteger(key, val);
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void readFormatLong(DataInputStream in, MediaFormat format, String key)
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        long val = in.readLong();
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (val != NO_VALUE_LONG) {
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            format.setLong(key, val);
1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void readFormatFloat(DataInputStream in, MediaFormat format, String key)
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        float val = in.readFloat();
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (val != NO_VALUE) {
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            format.setFloat(key, val);
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private String readString(DataInputStream in) throws IOException {
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int len = in.readInt();
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (len <= 0) {
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte [] strBytes = new byte[len];
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        in.readFully(strBytes);
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new String(strBytes, StandardCharsets.UTF_8);
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void readFormatString(DataInputStream in, MediaFormat format, String key)
1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String str = readString(in);
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (str != null) {
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            format.setString(key, str);
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private void readFormatStringOptional(DataInputStream in, MediaFormat format, String key) {
1356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        try {
1366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            String str = readString(in);
1376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (str != null) {
1386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                format.setString(key, str);
1396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } catch (IOException e) {
1416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // Since we are reading optional field, ignore the exception.
1426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
1436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
1446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private ByteBuffer readByteBuffer(DataInputStream in) throws IOException {
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int len = in.readInt();
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (len <= 0) {
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte [] bytes = new byte[len];
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        in.readFully(bytes);
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ByteBuffer buffer = ByteBuffer.allocate(len);
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        buffer.put(bytes);
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        buffer.flip();
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return buffer;
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void readFormatByteBuffer(DataInputStream in, MediaFormat format, String key)
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ByteBuffer buffer = readByteBuffer(in);
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (buffer != null) {
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            format.setByteBuffer(key, buffer);
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public List<BufferManager.TrackFormat> readTrackInfoFiles(boolean isAudio) {
1696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        List<BufferManager.TrackFormat> trackFormatList = new ArrayList<>();
1706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int index = 0;
1716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        boolean trackNotFound = false;
1726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        do {
1736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            String fileName = (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
1746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    + ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
1756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            File file = new File(getBufferDir(), fileName);
1766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
1776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                String name = readString(in);
1786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                MediaFormat format = new MediaFormat();
1796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatString(in, format, MediaFormat.KEY_MIME);
1806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatInt(in, format, MediaFormat.KEY_MAX_INPUT_SIZE);
1816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatInt(in, format, MediaFormat.KEY_WIDTH);
1826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatInt(in, format, MediaFormat.KEY_HEIGHT);
1836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatInt(in, format, MediaFormat.KEY_CHANNEL_COUNT);
1846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatInt(in, format, MediaFormat.KEY_SAMPLE_RATE);
1856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatFloat(in, format, KEY_PIXEL_WIDTH_HEIGHT_RATIO);
1866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (int i = 0; i < 3; ++i) {
1876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    readFormatByteBuffer(in, format, "csd-" + i);
1886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatLong(in, format, MediaFormat.KEY_DURATION);
1906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                // This is optional since language field is added later.
1926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                readFormatStringOptional(in, format, MediaFormat.KEY_LANGUAGE);
1936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                trackFormatList.add(new BufferManager.TrackFormat(name, format));
1946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } catch (IOException e) {
1956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                trackNotFound = true;
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            index++;
1986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } while(!trackNotFound);
1996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return trackFormatList;
2006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
2016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
2036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Reads caption information from files.
2046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     *
2056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * @return a list of {@link AtscCaptionTrack} objects which store caption information.
2066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
2076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public List<AtscCaptionTrack> readCaptionInfoFiles() {
2086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        List<AtscCaptionTrack> tracks = new ArrayList<>();
2096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int index = 0;
2106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        boolean trackNotFound = false;
2116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        do {
2126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            String fileName = META_FILE_TYPE_CAPTION +
2136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    ((index == 0) ? META_FILE_SUFFIX : (index + META_FILE_SUFFIX));
2146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            File file = new File(getBufferDir(), fileName);
2156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
2166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                byte[] data = new byte[(int) file.length()];
2176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                in.read(data);
2186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                tracks.add(AtscCaptionTrack.parseFrom(data));
2196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } catch (IOException e) {
2206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                trackNotFound = true;
2216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
2226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            index++;
2236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } while(!trackNotFound);
2246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return tracks;
2256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
2266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private ArrayList<BufferManager.PositionHolder> readOldIndexFile(File indexFile)
2286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            throws IOException {
2296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        ArrayList<BufferManager.PositionHolder> indices = new ArrayList<>();
2306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        try (DataInputStream in = new DataInputStream(new FileInputStream(indexFile))) {
2316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            long count = in.readLong();
2326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            for (long i = 0; i < count; ++i) {
2336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                long positionUs = in.readLong();
2346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                indices.add(new BufferManager.PositionHolder(positionUs, positionUs, 0));
2356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
2366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return indices;
2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private ArrayList<BufferManager.PositionHolder> readNewIndexFile(File indexFile)
2416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            throws IOException {
2426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        ArrayList<BufferManager.PositionHolder> indices = new ArrayList<>();
2436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        try (DataInputStream in = new DataInputStream(new FileInputStream(indexFile))) {
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            long count = in.readLong();
2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (long i = 0; i < count; ++i) {
2466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                long positionUs = in.readLong();
2476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                long basePositionUs = in.readLong();
2486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                int offset = in.readInt();
2496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                indices.add(new BufferManager.PositionHolder(positionUs, basePositionUs, offset));
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return indices;
2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    @Override
2566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public ArrayList<BufferManager.PositionHolder> readIndexFile(String trackId)
2576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            throws IOException {
2586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        File file = new File(getBufferDir(), trackId + IDX_FILE_SUFFIX_V2);
2596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (file.exists()) {
2606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return readNewIndexFile(file);
2616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        } else {
2626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return readOldIndexFile(new File(getBufferDir(),trackId + IDX_FILE_SUFFIX));
2636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
2646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
2656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeFormatInt(DataOutputStream out, MediaFormat format, String key)
2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (format.containsKey(key)) {
2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeInt(format.getInteger(key));
2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeInt(NO_VALUE);
2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeFormatLong(DataOutputStream out, MediaFormat format, String key)
2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (format.containsKey(key)) {
2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeLong(format.getLong(key));
2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeLong(NO_VALUE_LONG);
2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeFormatFloat(DataOutputStream out, MediaFormat format, String key)
2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (format.containsKey(key)) {
2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeFloat(format.getFloat(key));
2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeFloat(NO_VALUE);
2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeString(DataOutputStream out, String str) throws IOException {
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte [] data = str.getBytes(StandardCharsets.UTF_8);
2951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        out.writeInt(data.length);
2961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length > 0) {
2971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.write(data);
2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeFormatString(DataOutputStream out, MediaFormat format, String key)
3021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
3031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (format.containsKey(key)) {
3041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            writeString(out, format.getString(key));
3051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
3061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeInt(0);
3071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeByteBuffer(DataOutputStream out, ByteBuffer buffer) throws IOException {
3111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte [] data = new byte[buffer.limit()];
3121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        buffer.get(data);
3131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        buffer.flip();
3141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        out.writeInt(data.length);
3151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length > 0) {
3161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.write(data);
3171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
3181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeInt(0);
3191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void writeFormatByteBuffer(DataOutputStream out, MediaFormat format, String key)
3231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
3241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (format.containsKey(key)) {
3251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            writeByteBuffer(out, format.getByteBuffer(key));
3261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeInt(0);
3281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
3326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio)
3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
3346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        for (int i = 0; i < formatList.size() ; ++i) {
3356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            BufferManager.TrackFormat trackFormat = formatList.get(i);
3366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            String fileName = (isAudio ? META_FILE_TYPE_AUDIO : META_FILE_TYPE_VIDEO)
3376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    + ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
3386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            File file = new File(getBufferDir(), fileName);
3396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
3406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeString(out, trackFormat.trackId);
3416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatString(out, trackFormat.format, MediaFormat.KEY_MIME);
3426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatInt(out, trackFormat.format, MediaFormat.KEY_MAX_INPUT_SIZE);
3436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatInt(out, trackFormat.format, MediaFormat.KEY_WIDTH);
3446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatInt(out, trackFormat.format, MediaFormat.KEY_HEIGHT);
3456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatInt(out, trackFormat.format, MediaFormat.KEY_CHANNEL_COUNT);
3466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatInt(out, trackFormat.format, MediaFormat.KEY_SAMPLE_RATE);
3476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatFloat(out, trackFormat.format, KEY_PIXEL_WIDTH_HEIGHT_RATIO);
3486ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                for (int j = 0; j < 3; ++j) {
3496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    writeFormatByteBuffer(out, trackFormat.format, "csd-" + j);
3506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
3516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatLong(out, trackFormat.format, MediaFormat.KEY_DURATION);
3526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                writeFormatString(out, trackFormat.format, MediaFormat.KEY_LANGUAGE);
3536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
3546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
3556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
3566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
3576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
3586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Writes caption information to files.
3596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     *
3606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * @param tracks a list of {@link AtscCaptionTrack} objects which store caption information.
3616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
3626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void writeCaptionInfoFiles(List<AtscCaptionTrack> tracks) {
3636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (tracks == null || tracks.isEmpty()) {
3646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return;
3656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
3666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        for (int i = 0; i < tracks.size(); i++) {
3676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            AtscCaptionTrack track = tracks.get(i);
3686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            String fileName = META_FILE_TYPE_CAPTION +
3696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX));
3706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            File file = new File(getBufferDir(), fileName);
3716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
3726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                out.write(MessageNano.toByteArray(track));
3736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } catch (Exception e) {
3746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.e(TAG, "Fail to write caption info to files", e);
3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
3806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void writeIndexFile(String trackName, SortedMap<Long, Pair<SampleChunk, Integer>> index)
3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throws IOException {
3826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        File indexFile  = new File(getBufferDir(), trackName + IDX_FILE_SUFFIX_V2);
3831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        try (DataOutputStream out = new DataOutputStream(new FileOutputStream(indexFile))) {
3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            out.writeLong(index.size());
3856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            for (Map.Entry<Long, Pair<SampleChunk, Integer>> entry : index.entrySet()) {
3866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                out.writeLong(entry.getKey());
3876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                out.writeLong(entry.getValue().first.getStartPositionUs());
3886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                out.writeInt(entry.getValue().second);
3891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
393