1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/* 2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Copyright (C) 2013 The Android Open Source Project 3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Licensed under the Apache License, Version 2.0 (the "License"); 5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * you may not use this file except in compliance with the License. 6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * You may obtain a copy of the License at 7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * http://www.apache.org/licenses/LICENSE-2.0 9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Unless required by applicable law or agreed to in writing, software 11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * distributed under the License is distributed on an "AS IS" BASIS, 12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * See the License for the specific language governing permissions and 14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * limitations under the License. 15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */ 16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspackage androidx.media.filterfw; 17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.GraphRunner.Listener; 19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.Signature.PortInfo; 20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport com.google.common.util.concurrent.SettableFuture; 22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport junit.framework.TestCase; 24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.HashMap; 26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.HashSet; 27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.Map; 28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.Map.Entry; 29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.Set; 30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.concurrent.ExecutionException; 31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.concurrent.TimeUnit; 32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport java.util.concurrent.TimeoutException; 33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/** 35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * A {@link TestCase} for testing single MFF filter runs. Implementers should extend this class and 36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * implement the {@link #createFilter(MffContext)} method to create the filter under test. Inside 37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * each test method, the implementer should supply one or more frames for all the filter inputs 38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * (calling {@link #injectInputFrame(String, Frame)}) and then invoke {@link #process()}. Once the 39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * processing finishes, one should call {@link #getOutputFrame(String)} to get and inspect the 40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * output frames. 41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * TODO: extend this to deal with filters that push multiple output frames. 43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * TODO: relax the requirement that all output ports should be pushed (the implementer should be 44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * able to tell which ports to wait for before process() returns). 45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * TODO: handle undeclared inputs and outputs. 46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */ 47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspublic abstract class MffFilterTestCase extends MffTestCase { 48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private static final long DEFAULT_TIMEOUT_MS = 1000; 50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private FilterGraph mGraph; 52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private GraphRunner mRunner; 53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private Map<String, Frame> mOutputFrames; 54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private Set<String> mEmptyOutputPorts; 55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private SettableFuture<Void> mProcessResult; 57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected abstract Filter createFilter(MffContext mffContext); 59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void setUp() throws Exception { 62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks super.setUp(); 63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks MffContext mffContext = getMffContext(); 64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FilterGraph.Builder graphBuilder = new FilterGraph.Builder(mffContext); 65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Filter filterUnderTest = createFilter(mffContext); 66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks graphBuilder.addFilter(filterUnderTest); 67227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 68227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks connectInputPorts(mffContext, graphBuilder, filterUnderTest); 69227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks connectOutputPorts(mffContext, graphBuilder, filterUnderTest); 70227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 71227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mGraph = graphBuilder.build(); 72227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner = mGraph.getRunner(); 73227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner.setListener(new Listener() { 74227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 75227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public void onGraphRunnerStopped(GraphRunner runner) { 76227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mProcessResult.set(null); 77227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 78227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 79227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 80227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public void onGraphRunnerError(Exception exception, boolean closedSuccessfully) { 81227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mProcessResult.setException(exception); 82227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 83227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks }); 84227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 85227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mOutputFrames = new HashMap<String, Frame>(); 86227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mProcessResult = SettableFuture.create(); 87227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 88227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 89227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 90227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void tearDown() throws Exception { 91227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks for (Frame frame : mOutputFrames.values()) { 92227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks frame.release(); 93227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 94227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mOutputFrames = null; 95227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 96227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner.stop(); 97227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner = null; 98227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mGraph = null; 99227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 100227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mProcessResult = null; 101227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks super.tearDown(); 102227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 103227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 104227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void injectInputFrame(String portName, Frame frame) { 105227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameSourceFilter filter = (FrameSourceFilter) mGraph.getFilter("in_" + portName); 106227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks filter.injectFrame(frame); 107227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 108227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 109227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks /** 110227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Returns the frame pushed out by the filter under test. Should only be called after 111227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * {@link #process(long)} has returned. 112227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */ 113227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected Frame getOutputFrame(String outputPortName) { 114227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return mOutputFrames.get("out_" + outputPortName); 115227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 116227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 117227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void process(long timeoutMs) 118227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks throws ExecutionException, TimeoutException, InterruptedException { 119227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner.start(mGraph); 120227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mProcessResult.get(timeoutMs, TimeUnit.MILLISECONDS); 121227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 122227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 123227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void process() throws ExecutionException, TimeoutException, InterruptedException { 124227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks process(DEFAULT_TIMEOUT_MS); 125227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 126227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 127227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks /** 128227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * This method should be called to create the input frames inside the test cases (instead of 129227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * {@link Frame#create(FrameType, int[])}). This is required to work around a requirement for 130227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * the latter method to be called on the MFF thread. 131227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */ 132227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected Frame createFrame(FrameType type, int[] dimensions) { 133227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return new Frame(type, dimensions, mRunner.getFrameManager()); 134227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 135227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 136227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private void connectInputPorts( 137227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks MffContext mffContext, FilterGraph.Builder graphBuilder, Filter filter) { 138227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Signature signature = filter.getSignature(); 139227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks for (Entry<String, PortInfo> inputPortEntry : signature.getInputPorts().entrySet()) { 140227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Filter inputFilter = new FrameSourceFilter(mffContext, "in_" + inputPortEntry.getKey()); 141227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks graphBuilder.addFilter(inputFilter); 142227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks graphBuilder.connect(inputFilter, "output", filter, inputPortEntry.getKey()); 143227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 144227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 145227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 146227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private void connectOutputPorts( 147227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks MffContext mffContext, FilterGraph.Builder graphBuilder, Filter filter) { 148227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Signature signature = filter.getSignature(); 149227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mEmptyOutputPorts = new HashSet<String>(); 150227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks OutputFrameListener outputFrameListener = new OutputFrameListener(); 151227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks for (Entry<String, PortInfo> outputPortEntry : signature.getOutputPorts().entrySet()) { 152227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameTargetFilter outputFilter = new FrameTargetFilter( 153227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mffContext, "out_" + outputPortEntry.getKey()); 154227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks graphBuilder.addFilter(outputFilter); 155227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks graphBuilder.connect(filter, outputPortEntry.getKey(), outputFilter, "input"); 156227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks outputFilter.setListener(outputFrameListener); 157227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mEmptyOutputPorts.add("out_" + outputPortEntry.getKey()); 158227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 159227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 160227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 161227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private class OutputFrameListener implements FrameTargetFilter.Listener { 162227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 163227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 164227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public void onFramePushed(String filterName, Frame frame) { 165227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mOutputFrames.put(filterName, frame); 166227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks boolean alreadyPushed = !mEmptyOutputPorts.remove(filterName); 167227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (alreadyPushed) { 168227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks throw new IllegalStateException( 169227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks "A frame has been pushed twice to the same output port."); 170227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 171227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (mEmptyOutputPorts.isEmpty()) { 172227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // All outputs have been pushed, stop the graph. 173227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mRunner.stop(); 174227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 175227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 176227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 177227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 178227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 179227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks} 180