1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.screenrecord;
18 
19 import android.media.MediaCodec;
20 import android.media.MediaExtractor;
21 import android.media.MediaMuxer;
22 import android.util.ArrayMap;
23 import android.util.Log;
24 import android.util.Pair;
25 
26 import java.io.IOException;
27 import java.nio.ByteBuffer;
28 import java.util.ArrayList;
29 
30 /**
31  * Mixing audio and video tracks
32  */
33 public class ScreenRecordingMuxer {
34     // size of a memory page for cache coherency
35     private static final int BUFFER_SIZE = 1024 * 4096;
36     private String[] mFiles;
37     private String mOutFile;
38     private int mFormat;
39     private ArrayMap<Pair<MediaExtractor, Integer>, Integer> mExtractorIndexToMuxerIndex
40             = new ArrayMap<>();
41     private ArrayList<MediaExtractor> mExtractors = new ArrayList<>();
42 
43     private static String TAG = "ScreenRecordingMuxer";
ScreenRecordingMuxer(@ediaMuxer.Format int format, String outfileName, String... inputFileNames)44     public ScreenRecordingMuxer(@MediaMuxer.Format int format, String outfileName,
45             String... inputFileNames) {
46         mFiles = inputFileNames;
47         mOutFile = outfileName;
48         mFormat = format;
49         Log.d(TAG, "out: " + mOutFile + " , in: " + mFiles[0]);
50     }
51 
52     /**
53      * RUN IN THE BACKGROUND THREAD!
54      */
mux()55     public void mux() throws IOException, IllegalStateException {
56         MediaMuxer muxer = new MediaMuxer(mOutFile, mFormat);
57         // Add extractors
58         for (String file: mFiles) {
59             MediaExtractor extractor = new MediaExtractor();
60             try {
61                 extractor.setDataSource(file);
62             } catch (IOException e) {
63                 Log.e(TAG, "error creating extractor: " + file);
64                 e.printStackTrace();
65                 continue;
66             }
67             Log.d(TAG, file + " track count: " + extractor.getTrackCount());
68             mExtractors.add(extractor);
69             for (int i = 0; i < extractor.getTrackCount(); i++) {
70                 int muxId = muxer.addTrack(extractor.getTrackFormat(i));
71                 Log.d(TAG, "created extractor format" + extractor.getTrackFormat(i).toString());
72                 mExtractorIndexToMuxerIndex.put(Pair.create(extractor, i), muxId);
73             }
74         }
75 
76         // This may throw IllegalStateException if no tracks were added above
77         // Let the error propagate up so we can notify the user.
78         muxer.start();
79 
80         for (Pair<MediaExtractor, Integer> pair: mExtractorIndexToMuxerIndex.keySet()) {
81             MediaExtractor extractor = pair.first;
82             extractor.selectTrack(pair.second);
83             int muxId = mExtractorIndexToMuxerIndex.get(pair);
84             Log.d(TAG, "track format: " + extractor.getTrackFormat(pair.second));
85             extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
86             ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
87             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
88             int offset;
89             while (true) {
90                 offset = buffer.arrayOffset();
91                 info.size = extractor.readSampleData(buffer, offset);
92                 if (info.size < 0) break;
93                 info.presentationTimeUs = extractor.getSampleTime();
94                 info.flags = extractor.getSampleFlags();
95                 muxer.writeSampleData(muxId, buffer, info);
96                 extractor.advance();
97             }
98         }
99 
100         for (MediaExtractor extractor: mExtractors) {
101             extractor.release();
102         }
103         muxer.stop();
104         muxer.release();
105     }
106 }
107