17cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang/*
27cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * Copyright (C) 2018 The Android Open Source Project
37cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang *
47cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * Licensed under the Apache License, Version 2.0 (the "License");
57cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * you may not use this file except in compliance with the License.
67cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * You may obtain a copy of the License at
77cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang *
87cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang *      http://www.apache.org/licenses/LICENSE-2.0
97cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang *
107cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * Unless required by applicable law or agreed to in writing, software
117cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * distributed under the License is distributed on an "AS IS" BASIS,
127cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * See the License for the specific language governing permissions and
147cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * limitations under the License.
157cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang */
167cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
17db1945bea4e3816a04afe0ad41681cec1d0c074dAlan Viverettepackage androidx.heifwriter;
187cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
197cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport static android.support.test.InstrumentationRegistry.getContext;
207cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
217cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.graphics.Bitmap;
227cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.graphics.ImageFormat;
237cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.media.MediaExtractor;
247cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.media.MediaFormat;
257cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.media.MediaMetadataRetriever;
267cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.opengl.GLES20;
277cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.os.Environment;
287cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.os.Handler;
297cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.os.HandlerThread;
307cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.os.Process;
317cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.support.test.filters.LargeTest;
327cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.support.test.runner.AndroidJUnit4;
337cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport android.util.Log;
34b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang
35b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport static androidx.heifwriter.HeifWriter.INPUT_MODE_BITMAP;
36b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport static androidx.heifwriter.HeifWriter.INPUT_MODE_BUFFER;
37b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport static androidx.heifwriter.HeifWriter.INPUT_MODE_SURFACE;
38b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang
39b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport androidx.annotation.NonNull;
40b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport androidx.annotation.Nullable;
41db1945bea4e3816a04afe0ad41681cec1d0c074dAlan Viveretteimport androidx.heifwriter.test.R;
427cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
43b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport static org.junit.Assert.assertEquals;
44b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhangimport static org.junit.Assert.assertTrue;
45b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang
467cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport org.junit.After;
477cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport org.junit.Before;
487cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport org.junit.Test;
497cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport org.junit.runner.RunWith;
507cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
517cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.Closeable;
527cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.File;
537cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.FileInputStream;
547cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.FileOutputStream;
557cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.IOException;
567cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.InputStream;
577cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.io.OutputStream;
587cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangimport java.util.Arrays;
597cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
607cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang/**
617cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang * Test {@link HeifWriter}.
627cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang */
637cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang@RunWith(AndroidJUnit4.class)
647cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhangpublic class HeifWriterTest {
657cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final String TAG = HeifWriterTest.class.getSimpleName();
667cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final boolean DEBUG = false;
677cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final boolean DUMP_YUV_INPUT = false;
687cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
697cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static byte[][] TEST_COLORS = {
707cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            {(byte) 255, (byte) 0, (byte) 0},
717cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            {(byte) 255, (byte) 0, (byte) 255},
727cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            {(byte) 255, (byte) 255, (byte) 255},
737cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            {(byte) 255, (byte) 255, (byte) 0},
747cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    };
757cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
767cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final String TEST_HEIC = "test.heic";
777cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final int[] IMAGE_RESOURCES = new int[] {
787cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            R.raw.test
797cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    };
807cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final String[] IMAGE_FILENAMES = new String[] {
817cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            TEST_HEIC
827cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    };
837cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static final String OUTPUT_FILENAME = "output.heic";
847cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
857cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private EglWindowSurface mInputEglSurface;
867cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private Handler mHandler;
877cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private int mInputIndex;
887cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
897cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Before
907cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void setUp() throws Exception {
917cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
927cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String outputPath = new File(Environment.getExternalStorageDirectory(),
937cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    IMAGE_FILENAMES[i]).getAbsolutePath();
947cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
957cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            InputStream inputStream = null;
967cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            FileOutputStream outputStream = null;
977cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            try {
987cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                inputStream = getContext().getResources().openRawResource(IMAGE_RESOURCES[i]);
997cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                outputStream = new FileOutputStream(outputPath);
1007cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                copy(inputStream, outputStream);
1017cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            } finally {
1027cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                closeQuietly(inputStream);
1037cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                closeQuietly(outputStream);
1047cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
1057cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
1067cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1077cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        HandlerThread handlerThread = new HandlerThread(
1087cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                "HeifEncoderThread", Process.THREAD_PRIORITY_FOREGROUND);
1097cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        handlerThread.start();
1107cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mHandler = new Handler(handlerThread.getLooper());
1117cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1127cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1137cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @After
1147cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void tearDown() throws Exception {
1157cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
1167cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String imageFilePath =
1177cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    new File(Environment.getExternalStorageDirectory(), IMAGE_FILENAMES[i])
1187cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                            .getAbsolutePath();
1197cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            File imageFile = new File(imageFilePath);
1207cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            if (imageFile.exists()) {
1217cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                imageFile.delete();
1227cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
1237cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
1247cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1257cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1267cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1277cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1287cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBuffer_NoGrid_NoHandler() throws Throwable {
1295c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, false, false);
1305c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1317cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1327cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1337cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1347cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1357cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBuffer_Grid_NoHandler() throws Throwable {
1365c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, true, false);
1375c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1387cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1397cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1407cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1417cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1427cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBuffer_NoGrid_Handler() throws Throwable {
1435c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, false, true);
1445c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1457cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1467cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1477cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1487cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1497cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBuffer_Grid_Handler() throws Throwable {
1505c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BUFFER, true, true);
1515c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1527cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1537cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1547cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1557cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1567cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputSurface_NoGrid_NoHandler() throws Throwable {
1575c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, false);
1585c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1597cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1607cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1617cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1627cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1637cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputSurface_Grid_NoHandler() throws Throwable {
1645c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, false);
1655c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1667cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1677cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1687cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1697cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1707cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputSurface_NoGrid_Handler() throws Throwable {
1715c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, true);
1725c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1737cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1747cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1757cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1767cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1777cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputSurface_Grid_Handler() throws Throwable {
1785c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, true);
1795c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTestForVariousNumberImages(builder);
1807cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1817cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1827cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1837cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1847cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBitmap_NoGrid_NoHandler() throws Throwable {
1855c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, false, false);
1867cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
1877cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String inputPath = new File(Environment.getExternalStorageDirectory(),
1887cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    IMAGE_FILENAMES[i]).getAbsolutePath();
1895c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            doTestForVariousNumberImages(builder.setInputPath(inputPath));
1907cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
1917cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
1927cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
1937cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
1947cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
1957cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBitmap_Grid_NoHandler() throws Throwable {
1965c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, true, false);
1977cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
1987cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String inputPath = new File(Environment.getExternalStorageDirectory(),
1997cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    IMAGE_FILENAMES[i]).getAbsolutePath();
2005c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            doTestForVariousNumberImages(builder.setInputPath(inputPath));
2017cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2027cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
2037cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2047cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
2057cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
2067cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBitmap_NoGrid_Handler() throws Throwable {
2075c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, false, true);
2087cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
2097cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String inputPath = new File(Environment.getExternalStorageDirectory(),
2107cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    IMAGE_FILENAMES[i]).getAbsolutePath();
2115c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            doTestForVariousNumberImages(builder.setInputPath(inputPath));
2127cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2137cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
2147cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2157cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @Test
2167cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    @LargeTest
2177cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    public void testInputBitmap_Grid_Handler() throws Throwable {
2185c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_BITMAP, true, true);
2197cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
2207cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            String inputPath = new File(Environment.getExternalStorageDirectory(),
2217cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    IMAGE_FILENAMES[i]).getAbsolutePath();
2225c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            doTestForVariousNumberImages(builder.setInputPath(inputPath));
2237cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2247cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
2257cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2265c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang    private void doTestForVariousNumberImages(TestConfig.Builder builder) throws Exception {
227c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        builder.setNumImages(4);
228c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        doTest(builder.setRotation(270).build());
229c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        doTest(builder.setRotation(180).build());
230c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        doTest(builder.setRotation(90).build());
231c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        doTest(builder.setRotation(0).build());
2325c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTest(builder.setNumImages(1).build());
2335c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        doTest(builder.setNumImages(8).build());
2345c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang    }
2355c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
2367cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private void closeQuietly(Closeable closeable) {
2377cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        if (closeable != null) {
2387cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            try {
2397cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                closeable.close();
2407cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            } catch (RuntimeException rethrown) {
2417cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                throw rethrown;
2427cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            } catch (Exception ignored) {
2437cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
2447cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2457cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
2467cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2477cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private int copy(InputStream in, OutputStream out) throws IOException {
2487cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        int total = 0;
2497cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        byte[] buffer = new byte[8192];
2507cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        int c;
2517cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        while ((c = in.read(buffer)) != -1) {
2527cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            total += c;
2537cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            out.write(buffer, 0, c);
2547cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2557cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        return total;
2567cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
2577cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2587cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private static class TestConfig {
259c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mInputMode;
260c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final boolean mUseGrid;
261c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final boolean mUseHandler;
262c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mMaxNumImages;
263c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mNumImages;
264c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mWidth;
265c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mHeight;
266c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mRotation;
267c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final int mQuality;
268c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final String mInputPath;
269c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final String mOutputPath;
270c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        final Bitmap[] mBitmaps;
271c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
272c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        TestConfig(int inputMode, boolean useGrid, boolean useHandler,
273c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                   int maxNumImages, int numImages, int width, int height,
274c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                   int rotation, int quality,
275c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                   String inputPath, String outputPath, Bitmap[] bitmaps) {
276c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mInputMode = inputMode;
277c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mUseGrid = useGrid;
278c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mUseHandler = useHandler;
279c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mMaxNumImages = maxNumImages;
280c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mNumImages = numImages;
281c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mWidth = width;
282c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mHeight = height;
283c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mRotation = rotation;
284c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mQuality = quality;
285c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mInputPath = inputPath;
286c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mOutputPath = outputPath;
287c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            mBitmaps = bitmaps;
2887cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
2897cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
2905c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang        static class Builder {
291c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            final int mInputMode;
292c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            final boolean mUseGrid;
293c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            final boolean mUseHandler;
294c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            int mMaxNumImages;
295c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            int mNumImages;
296c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            int mWidth;
297c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            int mHeight;
298c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            int mRotation;
299c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            final int mQuality;
300c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            String mInputPath;
301c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            final String mOutputPath;
302c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            Bitmap[] mBitmaps;
303c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            boolean mNumImagesSetExplicitly;
304c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
305c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
306c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            Builder(int inputMode, boolean useGrids, boolean useHandler) {
307c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mInputMode = inputMode;
308c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mUseGrid = useGrids;
309c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mUseHandler = useHandler;
310c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mMaxNumImages = mNumImages = 4;
311c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mWidth = 1920;
312c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mHeight = 1080;
313c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mRotation = 0;
314c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mQuality = 100;
315c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mOutputPath = new File(Environment.getExternalStorageDirectory(),
3165c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                        OUTPUT_FILENAME).getAbsolutePath();
3177cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
3185c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
319c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            Builder setInputPath(String inputPath) {
320c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mInputPath = (mInputMode == INPUT_MODE_BITMAP) ? inputPath : null;
3215c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                return this;
3227cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
3235c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
324c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            Builder setNumImages(int numImages) {
325c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mNumImagesSetExplicitly = true;
326c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mNumImages = numImages;
327c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                return this;
328c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            }
329c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
330c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            Builder setRotation(int rotation) {
331c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mRotation = rotation;
3325c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                return this;
3337cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
3347cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
3355c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            private void loadBitmapInputs() {
336c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                if (mInputMode != INPUT_MODE_BITMAP) {
3375c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                    return;
3385c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                }
3395c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                MediaMetadataRetriever retriever = new MediaMetadataRetriever();
340c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                retriever.setDataSource(mInputPath);
3415c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                String hasImage = retriever.extractMetadata(
3425c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                        MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
3435c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                if (!"yes".equals(hasImage)) {
3445c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                    throw new IllegalArgumentException("no bitmap found!");
3455c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                }
346c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mMaxNumImages = Math.min(mMaxNumImages, Integer.parseInt(retriever.extractMetadata(
3475c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                        MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
348c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                if (!mNumImagesSetExplicitly) {
349c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    mNumImages = mMaxNumImages;
3505c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                }
351c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mBitmaps = new Bitmap[mMaxNumImages];
352c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                for (int i = 0; i < mBitmaps.length; i++) {
353c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    mBitmaps[i] = retriever.getImageAtIndex(i);
3545c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                }
355c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mWidth = mBitmaps[0].getWidth();
356c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                mHeight = mBitmaps[0].getHeight();
3575c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                retriever.release();
3585c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            }
3595c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
3605c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            private void cleanupStaleOutputs() {
361c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                File outputFile = new File(mOutputPath);
3625c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                if (outputFile.exists()) {
3635c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                    outputFile.delete();
3645c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                }
3655c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            }
3665c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
3675c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang            TestConfig build() {
3685c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                cleanupStaleOutputs();
3695c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang                loadBitmapInputs();
3705c0dcef896f803442eeca6aad3af8183f6e27071Chong Zhang
371c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                return new TestConfig(mInputMode, mUseGrid, mUseHandler, mMaxNumImages, mNumImages,
372c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                        mWidth, mHeight, mRotation, mQuality, mInputPath, mOutputPath, mBitmaps);
3737cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
3747cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
3757cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
3767cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        @Override
3777cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        public String toString() {
378c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            return "TestConfig"
379c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ": mInputMode " + mInputMode
380c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mUseGrid " + mUseGrid
381c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mUseHandler " + mUseHandler
382c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mMaxNumImages " + mMaxNumImages
383c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mNumImages " + mNumImages
384c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mWidth " + mWidth
385c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mHeight " + mHeight
386c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mRotation " + mRotation
387c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mQuality " + mQuality
388c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mInputPath " + mInputPath
389c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    + ", mOutputPath " + mOutputPath;
3907cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
3917cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
3927cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
393c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang    private void doTest(TestConfig config) throws Exception {
394c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        int width = config.mWidth;
395c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        int height = config.mHeight;
396c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        int numImages = config.mNumImages;
3977cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
3987cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mInputIndex = 0;
3997cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        HeifWriter heifWriter = null;
4007cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        FileInputStream inputStream = null;
4017cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        FileOutputStream outputStream = null;
4027cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        try {
403c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            if (DEBUG) Log.d(TAG, "started: " + config);
404c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
405c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            heifWriter = new HeifWriter.Builder(
406c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    config.mOutputPath, width, height, config.mInputMode)
407c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setRotation(config.mRotation)
408c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setGridEnabled(config.mUseGrid)
409c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setMaxImages(config.mMaxNumImages)
410c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setQuality(config.mQuality)
411c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setPrimaryIndex(config.mMaxNumImages - 1)
412c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .setHandler(config.mUseHandler ? mHandler : null)
413c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    .build();
414c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang
415c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            if (config.mInputMode == INPUT_MODE_SURFACE) {
4167cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                mInputEglSurface = new EglWindowSurface(heifWriter.getInputSurface());
4177cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
4187cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4197cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            heifWriter.start();
4207cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
421c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            if (config.mInputMode == INPUT_MODE_BUFFER) {
4227cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                byte[] data = new byte[width * height * 3 / 2];
4237cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
424c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                if (config.mInputPath != null) {
425c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    inputStream = new FileInputStream(config.mInputPath);
4267cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4277cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4287cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                if (DUMP_YUV_INPUT) {
4297cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    File outputFile = new File("/sdcard/input.yuv");
4307cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    outputFile.createNewFile();
4317cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    outputStream = new FileOutputStream(outputFile);
4327cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4337cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4347cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                for (int i = 0; i < numImages; i++) {
4357cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    if (DEBUG) Log.d(TAG, "fillYuvBuffer: " + i);
4367cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    fillYuvBuffer(i, data, width, height, inputStream);
4377cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    if (DUMP_YUV_INPUT) {
4387cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                        Log.d(TAG, "@@@ dumping input YUV");
4397cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                        outputStream.write(data);
4407cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    }
4417cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    heifWriter.addYuvBuffer(ImageFormat.YUV_420_888, data);
4427cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
443c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            } else if (config.mInputMode == INPUT_MODE_SURFACE) {
4447cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // The input surface is a surface texture using single buffer mode, draws will be
4457cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // blocked until onFrameAvailable is done with the buffer, which is dependant on
4467cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // how fast MediaCodec processes them, which is further dependent on how fast the
4477cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // MediaCodec callbacks are handled. We can't put draws on the same looper that
4487cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // handles MediaCodec callback, it will cause deadlock.
4497cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                for (int i = 0; i < numImages; i++) {
4507cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    if (DEBUG) Log.d(TAG, "drawFrame: " + i);
4517cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    drawFrame(width, height);
4527cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4537cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                heifWriter.setInputEndOfStreamTimestamp(
4547cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                        1000 * computePresentationTime(numImages - 1));
455c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            } else if (config.mInputMode == INPUT_MODE_BITMAP) {
456c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                Bitmap[] bitmaps = config.mBitmaps;
4577cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                for (int i = 0; i < Math.min(bitmaps.length, numImages); i++) {
4587cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    if (DEBUG) Log.d(TAG, "addBitmap: " + i);
4597cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    heifWriter.addBitmap(bitmaps[i]);
4607cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    bitmaps[i].recycle();
4617cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4627cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
4637cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4647cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            heifWriter.stop(3000);
465c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            verifyResult(config.mOutputPath, width, height, config.mRotation, config.mUseGrid,
466c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    Math.min(numImages, config.mMaxNumImages));
4677cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            if (DEBUG) Log.d(TAG, "finished: PASS");
4687cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        } finally {
4697cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            try {
4707cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                if (outputStream != null) {
4717cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    outputStream.close();
4727cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4737cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                if (inputStream != null) {
4747cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    inputStream.close();
4757cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                }
4767cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            } catch (IOException e) {}
4777cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4787cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            if (heifWriter != null) {
4797cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                heifWriter.close();
4807cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                heifWriter = null;
4817cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
4827cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            if (mInputEglSurface != null) {
4837cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                // This also releases the surface from encoder.
4847cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                mInputEglSurface.release();
4857cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                mInputEglSurface = null;
4867cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            }
4877cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
4887cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
4897cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4907cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private long computePresentationTime(int frameIndex) {
4917cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        return 132 + (long)frameIndex * 1000000;
4927cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
4937cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
4947cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private void fillYuvBuffer(int frameIndex, @NonNull byte[] data, int width, int height,
4957cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                               @Nullable FileInputStream inputStream) throws IOException {
4967cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        if (inputStream != null) {
4977cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            inputStream.read(data);
4987cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        } else {
4997cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            byte[] color = TEST_COLORS[frameIndex % TEST_COLORS.length];
5007cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            int sizeY = width * height;
5017cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            Arrays.fill(data, 0, sizeY, color[0]);
5027cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            Arrays.fill(data, sizeY, sizeY * 5 / 4, color[1]);
5037cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            Arrays.fill(data, sizeY * 5 / 4, sizeY * 3 / 2, color[2]);
5047cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
5057cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
5067cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5077cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private void drawFrame(int width, int height) {
5087cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mInputEglSurface.makeCurrent();
5097cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        generateSurfaceFrame(mInputIndex, width, height);
5107cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mInputEglSurface.setPresentationTime(1000 * computePresentationTime(mInputIndex));
5117cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mInputEglSurface.swapBuffers();
5127cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        mInputIndex++;
5137cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
5147cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5157cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private void generateSurfaceFrame(int frameIndex, int width, int height) {
5167cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        frameIndex %= 4;
5177cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5187cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glViewport(0, 0, width, height);
5197cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
5207cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
5217cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
5227cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
5237cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5247cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        int startX, startY;
5257cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        int borderWidth = 16;
5267cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        for (int i = 0; i < 7; i++) {
5277cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            startX = (width - borderWidth * 2) * i / 7 + borderWidth;
5287cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            GLES20.glScissor(startX, borderWidth,
5297cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    (width - borderWidth * 2) / 7, height - borderWidth * 2);
5307cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            GLES20.glClearColor(((7 - i) & 0x4) * 0.16f,
5317cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    ((7 - i) & 0x2) * 0.32f,
5327cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    ((7 - i) & 0x1) * 0.64f,
5337cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                    1.0f);
5347cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
5357cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
5367cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5377cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        startX = (width / 6) + (width / 6) * frameIndex;
5387cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        startY = height / 4;
5397cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glScissor(startX, startY, width / 6, height / 3);
5407cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
5417cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
5427cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glScissor(startX + borderWidth, startY + borderWidth,
5437cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                width / 6 - borderWidth * 2, height / 3 - borderWidth * 2);
5447cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
5457cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
5467cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
5477cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5487cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    private void verifyResult(
549c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang            String filename, int width, int height, int rotation, boolean useGrid, int numImages)
5507cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            throws Exception {
5517cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
5527cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        retriever.setDataSource(filename);
5537cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        String hasImage = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
5547cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        if (!"yes".equals(hasImage)) {
5557cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            throw new Exception("No images found in file " + filename);
5567cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
5577cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        assertEquals("Wrong image count", numImages,
5587cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                Integer.parseInt(retriever.extractMetadata(
559c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
5607cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        assertEquals("Wrong width", width,
5617cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                Integer.parseInt(retriever.extractMetadata(
562c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
5637cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        assertEquals("Wrong height", height,
5647cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang                Integer.parseInt(retriever.extractMetadata(
565c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
566c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang        assertEquals("Wrong rotation", rotation,
567c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                Integer.parseInt(retriever.extractMetadata(
568c6cd9ccf97400c11e449072798866f0f86b64701Chong Zhang                    MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
5697cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        retriever.release();
5707cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang
5717cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        if (useGrid) {
5727cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            MediaExtractor extractor = new MediaExtractor();
5737cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            extractor.setDataSource(filename);
5747cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            MediaFormat format = extractor.getTrackFormat(0);
575b8aa7b3a65331060158806a9a1e0e327556c6df7Jason Monk            int gridWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
576b8aa7b3a65331060158806a9a1e0e327556c6df7Jason Monk            int gridHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
577b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang            int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
578b8aa7b3a65331060158806a9a1e0e327556c6df7Jason Monk            int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
579b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang            assertTrue("Wrong grid width or cols",
580b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang                    ((width + gridWidth - 1) / gridWidth) == gridCols);
581b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang            assertTrue("Wrong grid height or rows",
582b12f17f77f3b42d149abed231bc7875e658d08c2Chong Zhang                    ((height + gridHeight - 1) / gridHeight) == gridRows);
5837cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang            extractor.release();
5847cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang        }
5857cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang    }
5867cabcaccabb00c7f7068a8ebbd55eb43795eefb4Chong Zhang}
587