H. 265 is not supported by many players. Even bilibili’s open source ijkPlayer cannot play the video directly. You need to recompile it to support it. Here through this demo to demonstrate how to hard decode video, play H. 265 video, in fact, the encoded video is the same truth.
The video is played mainly in the surfaceView, and the decoding process is executed in the audio decoder thread and the video decoder thread respectively.
Video decoding
The main use of a MediaCodec class for decoding.
Setting the data source
MediaExtractor mediaExtractor = new MediaExtractor(); try { mediaExtractor.setDataSource(path); } catch (IOException e1) {e1.printStackTrace(); }Copy the code
Initialize MediaCodec according to the encoding information of the video:
The mineType of video is the video type.
String mimeType = null; for (int i = 0; i < mediaExtractor.getTrackCount(); Total i++) {/ / channel MediaFormat format = mediaExtractor. GetTrackFormat (I); MimeType = format.getString(mediaformat.key_mime); // Audio file information mimeType = format.getString(mediaformat.key_mime); If (mimeType. StartsWith (" video/")) {/ / video channel mediaExtractor. SelectTrack (I); / / try to switch to video channel {mediaCodec = mediaCodec. CreateDecoderByType (mimeType); } catch (IOException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace(); } mediaCodec.configure(format, surface, null, 0); break; } } mediaCodec.start(); // Start MediaCodec and wait for incoming dataCopy the code
Get cache
/ / input ByteBuffer [] inputBuffers = mediaCodec. GetInputBuffers (); / / / / used to store the target file data output ByteBuffer [] outputBuffers = mediaCodec. GetOutputBuffers (); // Decoded data mediacodec.bufferInfo info = new Mediacodec.bufferInfo (); // Used to describe the information about the decoded byte[] dataCopy the code
To decode
while (! Thread.interrupted()) { if (! bIsEos) { int inIndex = mediaCodec.dequeueInputBuffer(0); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int nSampleSize = mediaExtractor.readSampleData(buffer, 0); If (nSampleSize < 0) {log.d (TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM"); mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); bIsEos = true; } else {/ / fill data mediaCodec queueInputBuffer (inIndex, 0, nSampleSize, mediaExtractor getSampleTime (), 0). // Notify MediaDecode to decode the data just passed in. Mediaextractor.advance (); / / continue to the next sampling}}} int outIndex = mediaCodec. DequeueOutputBuffer (info, 0); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED"); outputBuffers = mediaCodec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: Log.d(TAG, "New format " + mediaCodec.getOutputFormat()); break; case MediaCodec.INFO_TRY_AGAIN_LATER: Log.d(TAG, "dequeueOutputBuffer timed out!" ); break; default: ByteBuffer buffer = outputBuffers[outIndex]; Log.v(TAG, "We can't use this buffer but render it due to the API limit, " + buffer); mediaCodec.releaseOutputBuffer(outIndex, true); break; } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) ! = 0) { Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; }}Copy the code
Release resources after decoding is complete
mediaCodec.stop();
mediaCodec.release();
mediaExtractor.release();
Copy the code
Now that the decoding of the video is complete, the surfaceView is ready to play the video, and the audio is decoded.
Audio decoding
The audio decoding process is much the same as above, the main difference is that the video is played by surfaceView, while the audio is played by AudioTrack.
Create an AudioPlayer class to play the audio
public class AudioPlayer { private int mFrequency; // Private int mChannel; // Private int mSampBit; Private AudioTrack mAudioTrack; public AudioPlayer(int frequency, int channel, int sampbit) { this.mFrequency = frequency; this.mChannel = channel; this.mSampBit = sampbit; Public void init() {if (mAudioTrack! = null) { release(); } / / for constructing the minimum buffer size object int minBufSize = AudioTrack. GetMinBufferSize (mFrequency mChannel, mSampBit); mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mFrequency, mChannel, mSampBit, minBufSize, AudioTrack.MODE_STREAM); mAudioTrack.play(); } private void release() {if (mAudioTrack! = null) { mAudioTrack.stop(); mAudioTrack.release(); }} /** * Write PCM data after decoding audioTrack Playback ** @param data data * @param offset offset * @param Length Length to be played */ public void play(byte[] data, int offset, int length) { if (data == null || data.length == 0) { return; } try { mAudioTrack.write(data, offset, length); } catch (Exception e) { e.printStackTrace(); }}}Copy the code
Initialize the audio decoder:
So the mineType for audio is audio, so that’s what we’re going to do.
String mimeType; for (int i = 0; i < mediaExtractor.getTrackCount(); Total i++) {/ / channel MediaFormat format = mediaExtractor. GetTrackFormat (I); MimeType = format.getString(mediaformat.key_mime); // Audio file information mimeType = format.getString(mediaformat.key_mime); If (mimeType. StartsWith (" audio/")) {/ / audio channel mediaExtractor. SelectTrack (I); / / try to switch to the audio channel {mediaCodec = mediaCodec. CreateDecoderByType (mimeType); } catch (IOException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace(); } mediaCodec.configure(format, null, null, 0); mPlayer = new AudioPlayer(format.getInteger(MediaFormat.KEY_SAMPLE_RATE), AudioFormat .CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); mPlayer.init(); break; } } if (mediaCodec == null) { Log.e(TAG, "Can't find video info!" ); return; } mediaCodec.start(); // Start MediaCodec and wait for incoming dataCopy the code
Audio decoding:
The audio decoding process is pretty much the same as the video decoding process, just a little extra call to the AudioPlayer we created to play the audio.
while (! Thread.interrupted()) { if (! bIsEos) { int inIndex = mediaCodec.dequeueInputBuffer(0); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int nSampleSize = mediaExtractor.readSampleData(buffer, 0); If (nSampleSize < 0) {log.d (TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM"); mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); bIsEos = true; } else {/ / fill data mediaCodec queueInputBuffer (inIndex, 0, nSampleSize, mediaExtractor getSampleTime (), 0). // Notify MediaDecode to decode the data just passed in. Mediaextractor.advance (); / / continue to the next sampling}}} int outIndex = mediaCodec. DequeueOutputBuffer (info, 0); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED"); outputBuffers = mediaCodec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: Log.d(TAG, "New format " + mediaCodec.getOutputFormat()); break; case MediaCodec.INFO_TRY_AGAIN_LATER: Log.d(TAG, "dequeueOutputBuffer timed out!" ); break; default: ByteBuffer buffer = outputBuffers[outIndex]; Log.v(TAG, "We can't use this buffer but render it due to the API limit, " + buffer); while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) { try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); break; Byte [] outData = new byte[info.size]; buffer.get(outData); // Clear the buffer. Clear (); Mplayer.play (outData, 0, info.size); mediaCodec.releaseOutputBuffer(outIndex, true); break; } // All decoded frames have been rendered, we can stop playing // now if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) ! = 0) { Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; }}Copy the code
Results show
Video encoding information, as H.265:
Playback Effect (with sound) :
Get the encoding format that MediaCodec supports decoding:
HashMap<String, MediaCodecInfo.CodecCapabilities> mEncoderInfos = new HashMap<>(); for(int i = MediaCodecList.getCodecCount() - 1; i >= 0; i--){ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); if(codecInfo.isEncoder()){ for(String t : codecInfo.getSupportedTypes()){ try{ mEncoderInfos.put(t, codecInfo.getCapabilitiesForType(t)); } catch(IllegalArgumentException e){ e.printStackTrace(); }}}}Copy the code
Full Demo address
Copy h265.mp4 under Assets to the SD card when using it
Making address: github.com/JavaNoober/…