1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 package org.mozilla.gecko.media;
6 
7 import android.media.MediaCodec;
8 import android.media.MediaCodec.BufferInfo;
9 import android.media.MediaCodec.CryptoInfo;
10 import android.os.Parcel;
11 import android.os.Parcelable;
12 
13 import org.mozilla.gecko.annotation.WrapForJNI;
14 
15 import java.nio.ByteBuffer;
16 
17 // Parcelable carrying input/output sample data and info cross process.
18 public final class Sample implements Parcelable {
19     public static final Sample EOS;
20     static {
21         final BufferInfo eosInfo = new BufferInfo();
22         EOS = new Sample();
23         EOS.info.set(0, 0, Long.MIN_VALUE, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
24     }
25     @WrapForJNI
26     public long session;
27 
28     public static final int NO_BUFFER = -1;
29 
30     public int bufferId = NO_BUFFER;
31     @WrapForJNI
32     public BufferInfo info = new BufferInfo();
33     public CryptoInfo cryptoInfo;
34 
35     // Simple Linked list for recycling objects.
36     // Used to nodify Sample objects. Do not marshal/unmarshal.
37     private Sample mNext;
38     private static Sample sPool = new Sample();
39     private static int sPoolSize = 1;
40 
Sample()41     private Sample() { }
42 
readInfo(final Parcel in)43     private void readInfo(final Parcel in) {
44         final int offset = in.readInt();
45         final int size = in.readInt();
46         final long pts = in.readLong();
47         final int flags = in.readInt();
48 
49         info.set(offset, size, pts, flags);
50     }
51 
readCrypto(final Parcel in)52     private void readCrypto(final Parcel in) {
53         final int hasCryptoInfo = in.readInt();
54         if (hasCryptoInfo == 0) {
55             cryptoInfo = null;
56             return;
57         }
58 
59         final byte[] iv = in.createByteArray();
60         final byte[] key = in.createByteArray();
61         final int mode = in.readInt();
62         final int[] numBytesOfClearData = in.createIntArray();
63         final int[] numBytesOfEncryptedData = in.createIntArray();
64         final int numSubSamples = in.readInt();
65 
66         if (cryptoInfo == null) {
67             cryptoInfo = new CryptoInfo();
68         }
69         cryptoInfo.set(numSubSamples,
70                       numBytesOfClearData,
71                       numBytesOfEncryptedData,
72                       key,
73                       iv,
74                       mode);
75     }
76 
set(final BufferInfo info, final CryptoInfo cryptoInfo)77     public Sample set(final BufferInfo info, final CryptoInfo cryptoInfo) {
78         setBufferInfo(info);
79         setCryptoInfo(cryptoInfo);
80         return this;
81     }
82 
setBufferInfo(final BufferInfo info)83     public void setBufferInfo(final BufferInfo info) {
84         this.info.set(0, info.size, info.presentationTimeUs, info.flags);
85     }
86 
setCryptoInfo(final CryptoInfo crypto)87     public void setCryptoInfo(final CryptoInfo crypto) {
88         if (crypto == null) {
89             cryptoInfo = null;
90             return;
91         }
92 
93         if (cryptoInfo == null) {
94             cryptoInfo = new CryptoInfo();
95         }
96         cryptoInfo.set(crypto.numSubSamples,
97                        crypto.numBytesOfClearData,
98                        crypto.numBytesOfEncryptedData,
99                        crypto.key,
100                        crypto.iv,
101                        crypto.mode);
102     }
103 
104     @WrapForJNI
dispose()105     public void dispose() {
106         if (isEOS()) {
107             return;
108         }
109 
110         bufferId = NO_BUFFER;
111         info.set(0, 0, 0, 0);
112         if (cryptoInfo != null) {
113             cryptoInfo.set(0, null, null, null, null, 0);
114         }
115 
116         // Recycle it.
117         synchronized (CREATOR) {
118             this.mNext = sPool;
119             sPool = this;
120             sPoolSize++;
121         }
122     }
123 
isEOS()124     public boolean isEOS() {
125         return (this == EOS) ||
126                 ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
127     }
128 
obtain()129     public static Sample obtain() {
130         synchronized (CREATOR) {
131             Sample s = null;
132             if (sPoolSize > 0) {
133                 s = sPool;
134                 sPool = s.mNext;
135                 s.mNext = null;
136                 sPoolSize--;
137             } else {
138                 s = new Sample();
139             }
140             return s;
141         }
142     }
143 
144     public static final Creator<Sample> CREATOR = new Creator<Sample>() {
145         @Override
146         public Sample createFromParcel(final Parcel in) {
147             return obtainSample(in);
148         }
149 
150         @Override
151         public Sample[] newArray(final int size) {
152             return new Sample[size];
153         }
154 
155         private Sample obtainSample(final Parcel in) {
156             final Sample s = obtain();
157             s.session = in.readLong();
158             s.bufferId = in.readInt();
159             s.readInfo(in);
160             s.readCrypto(in);
161             return s;
162         }
163     };
164 
165     @Override
describeContents()166     public int describeContents() {
167         return 0;
168     }
169 
170     @Override
writeToParcel(final Parcel dest, final int parcelableFlags)171     public void writeToParcel(final Parcel dest, final int parcelableFlags) {
172         dest.writeLong(session);
173         dest.writeInt(bufferId);
174         writeInfo(dest);
175         writeCrypto(dest);
176     }
177 
writeInfo(final Parcel dest)178     private void writeInfo(final Parcel dest) {
179         dest.writeInt(info.offset);
180         dest.writeInt(info.size);
181         dest.writeLong(info.presentationTimeUs);
182         dest.writeInt(info.flags);
183     }
184 
writeCrypto(final Parcel dest)185     private void writeCrypto(final Parcel dest) {
186         if (cryptoInfo != null) {
187             dest.writeInt(1);
188             dest.writeByteArray(cryptoInfo.iv);
189             dest.writeByteArray(cryptoInfo.key);
190             dest.writeInt(cryptoInfo.mode);
191             dest.writeIntArray(cryptoInfo.numBytesOfClearData);
192             dest.writeIntArray(cryptoInfo.numBytesOfEncryptedData);
193             dest.writeInt(cryptoInfo.numSubSamples);
194         } else {
195             dest.writeInt(0);
196         }
197     }
198 
byteArrayFromBuffer(final ByteBuffer buffer, final int offset, final int size)199     public static byte[] byteArrayFromBuffer(final ByteBuffer buffer, final int offset,
200                                              final int size) {
201         if (buffer == null || buffer.capacity() == 0 || size == 0) {
202             return null;
203         }
204         if (buffer.hasArray() && offset == 0 && buffer.array().length == size) {
205             return buffer.array();
206         }
207         final int length = Math.min(offset + size, buffer.capacity()) - offset;
208         final byte[] bytes = new byte[length];
209         buffer.position(offset);
210         buffer.get(bytes);
211         return bytes;
212     }
213 
214     @Override
toString()215     public String toString() {
216         if (isEOS()) {
217             return "EOS sample";
218         }
219 
220         final StringBuilder str = new StringBuilder();
221         str.append("{ session#:").append(session).
222                 append(", buffer#").append(bufferId).
223                 append(", info=").
224                 append("{ offset=").append(info.offset).
225                 append(", size=").append(info.size).
226                 append(", pts=").append(info.presentationTimeUs).
227                 append(", flags=").append(Integer.toHexString(info.flags)).append(" }").
228                 append(" }");
229         return str.toString();
230     }
231 }
232